using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using Timer = System.Timers.Timer;
using System.Collections;
using HengShan_Pump_NonIC.MessageEntity;
using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;
using Wayne.FDCPOSLibrary;
using System.Xml;
using Edge.Core.Database.Models;
using System.Threading.Tasks;
namespace HengShan_Pump_NonIC
{
///
/// this is kind of HS non IC pump that have a build-in auto auth self function by hardware, so outside(fcc) can't auth it, it's
/// a 'lift nozzle, and fueling pump'.
/// basically it only have 2 states:
/// idle: 不允许加油--加油结束--油枪关--电机关--D30--D20--金额加油
/// fueling: 允许加油--加油过程--油枪打开--电机打开--D30--D20--金额加油
///
public class PumpHandlerForSelfAuthPump : PumpHandler
{
private object syncObject = new object();
// by seconds, change this value need change the correlated deviceOfflineCountdownTimer's interval as well
private const int lastLogicalDeviceStateExpiredTime = 3;
private DateTime lastLogicalDeviceStateReceivedTime;
private System.Timers.Timer deviceOfflineCountdownTimer;
//static ILog fdcLogger = log4net.LogManager.GetLogger("FdcServer");
//static ILog logger = log4net.LogManager.GetLogger("PumpHandler");
static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("PumpHandler");
///
/// 恒山非IC油机
///
///
///
public PumpHandlerForSelfAuthPump(int pumpId, string nozzlesXmlConfiguration) : base(pumpId, nozzlesXmlConfiguration)
{
logger.Info("Pump: " + this.PumpId + ", PumpHandlerForSelfAuthPump");
this.deviceOfflineCountdownTimer = new System.Timers.Timer(1000);
this.deviceOfflineCountdownTimer.Elapsed += (_, __) =>
{
if (DateTime.Now.Subtract(this.lastLogicalDeviceStateReceivedTime).TotalSeconds
>= lastLogicalDeviceStateExpiredTime)
{
//if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_OFFLINE)
//{
// this.lastLogicalDeviceState = LogicalDeviceState.FDC_OFFLINE;
logger.Info("Pump: " + this.PumpId + ", " + " seems offline due to long time no see pump data incoming");
// var safe0 = this.OnStateChange;
// safe0?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_OFFLINE, null));
// logger.Trace("Pump: " + this.pumpId + ", " + " OnStateChange event fired and back");
//}
}
};
this.deviceOfflineCountdownTimer.Start();
}
public override async Task Process(IContext context)
{
if (!isOnFdcServerInitCalled) return;
this.lastLogicalDeviceStateReceivedTime = DateTime.Now;
this.context = context;
if (context.Incoming.Message is GetNozzleStatusResponse getNozzleStatusResponse)
{
//this.isSafeForSend = true;
this.lastLogicalDeviceStateReceivedTime = DateTime.Now;
//var getNozzleStatusResponse = context.Incoming.Message as GetNozzleStatusResponse;
// put the price by reading the real price.
this.nozzles.First().RealPriceOnPhysicalPump = getNozzleStatusResponse.单价;
var latestStatus = getNozzleStatusResponse.GetPumpStatus();
if (latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.允许加油)
&& latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.油枪打开)
&& latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.加油过程)
&& latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.电机打开))
{
/* 正处于加油状态下 */
if (this.previousUnfinishedFuelingNozzleStatus != null
&& this.previousUnfinishedFuelingNozzleStatus.流水号 != getNozzleStatusResponse.流水号)
{
logger.Info("Pump: " + this.PumpId + ", " + "Detected a fast put back and pull out nozzle case(action time < polling time cause nozzle place back not detected)," +
"\r\n will fire the trx done event for earlier trx(may have volume/money data loss!!!) with most recoverable data(most likely a bit less than real)->\r\n "
+ "seqNo: " + this.previousUnfinishedFuelingNozzleStatus.流水号
+ ", amount: " + this.previousUnfinishedFuelingNozzleStatus.加油金额
+ ", volume: " + this.previousUnfinishedFuelingNozzleStatus.加油量
+ ", price: " + this.previousUnfinishedFuelingNozzleStatus.单价);
logger.Info("Pump: " + this.PumpId + ", " + " State switched to FDC_READY(simulate)");
this.lastLogicalDeviceState = LogicalDeviceState.FDC_READY;
this.FireOnStateChangeEvent(LogicalDeviceState.FDC_READY);
var previousFuelingAmount = this.previousUnfinishedFuelingNozzleStatus.加油金额;
var previousFuelingVol = this.previousUnfinishedFuelingNozzleStatus.加油量;
var previousPrice = this.previousUnfinishedFuelingNozzleStatus.单价;
var previousSeqNo = this.previousUnfinishedFuelingNozzleStatus.流水号;
if (this.logicalNozzleIdToLastFuelSaleTrxMapping.ContainsKey(1))
this.logicalNozzleIdToLastFuelSaleTrxMapping[1] = new FuelSaleTransaction()
{
TransactionSeqNumberFromPhysicalPump = this.previousUnfinishedFuelingNozzleStatus.流水号.ToString()
};
else
this.logicalNozzleIdToLastFuelSaleTrxMapping.Add(1, new FuelSaleTransaction()
{
TransactionSeqNumberFromPhysicalPump = this.previousUnfinishedFuelingNozzleStatus.流水号.ToString()
});
//ThreadPool.QueueUserWorkItem(o =>
//{
var totalizer = await base.QueryTotalizerAsync(1);
this.FireOnCurrentFuellingStatusChangeEvent(new FdcTransaction()
{
// use previousUnfinishedFuelingNozzleStatus could miss reading the last trx correct numbers(less than actual number)
//, obviously this is caused by pump hardware design, impossible to recovery it back by our application.
Nozzle = this.nozzles.First(),
Amount = previousFuelingAmount,
Volumn = previousFuelingVol,
Price = previousPrice,
SequenceNumberGeneratedOnPhysicalPump = previousSeqNo,
AmountTotalizer = totalizer.Item1,
VolumeTotalizer = totalizer.Item2,
Finished = true,
});
//});
}
this.previousUnfinishedFuelingNozzleStatus = getNozzleStatusResponse;
if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_FUELLING)
{
//status code: B1
logger.Debug("Pump: " + this.PumpId + ", " + "收到新状态: 处于加油状态下");
logger.Debug("Pump: " + this.PumpId + ", " + " State switched to FDC_CALLING(by simulate)");
this.lastLogicalDeviceState = LogicalDeviceState.FDC_CALLING;
base.FireOnStateChangeEvent(LogicalDeviceState.FDC_CALLING);
logger.Debug("Pump: " + this.PumpId + ", " + " State switched to FDC_FUELLING(流水号: " + getNozzleStatusResponse.流水号 + ")");
this.lastLogicalDeviceState = LogicalDeviceState.FDC_FUELLING;
base.FireOnStateChangeEvent(LogicalDeviceState.FDC_FUELLING);
}
logger.Debug("Pump: " + this.PumpId + ", " + " fueling in progress with amt: " + getNozzleStatusResponse.加油金额
+ ", vol: " + getNozzleStatusResponse.加油量 + ", seq: " + getNozzleStatusResponse.流水号);
//fire fuelling progress.
base.FireOnCurrentFuellingStatusChangeEvent(new FdcTransaction()
{
// 恒山油机只有一把枪
Nozzle = this.nozzles.First(),
Amount = getNozzleStatusResponse.加油金额,
Volumn = getNozzleStatusResponse.加油量,
Price = getNozzleStatusResponse.单价,
SequenceNumberGeneratedOnPhysicalPump = getNozzleStatusResponse.流水号,
Finished = false,
});
}
else if (latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.不允许加油)
&& latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.加油结束))
{
/* 油机首次上电也会进入此处,并不断发送上一次的加油记录,这种情况下的交易记录并无法判断其之前是否已经发送至上层系统(比如是否已经存入数据库),
所以此处不判断而是往上层系统里送入,由系统判断重复情况。 而其它正常加油过程中的交易记录仅在油机状态变化时才送入系统。*/
// status code: 40
this.previousUnfinishedFuelingNozzleStatus = null;
if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_READY)
{
logger.Info("Pump: " + this.PumpId + ", " + "收到新状态: 加油结束(seq No.: " + getNozzleStatusResponse.流水号.ToString() + ")");
logger.Info("Pump: " + this.PumpId + ", " + " State switched to FDC_READY");
lastLogicalDeviceState = LogicalDeviceState.FDC_READY;
base.FireOnStateChangeEvent(LogicalDeviceState.FDC_READY);
// zero trx, do nothing.
if (getNozzleStatusResponse.加油量 == 0 || getNozzleStatusResponse.加油金额 == 0)
{
logger.Debug("Pump: " + this.PumpId + ", " + "0 amount trx, will ignore");
return;
}
// repeat received last fuel sale message, do nothing.
if (base.logicalNozzleIdToLastFuelSaleTrxMapping.ContainsKey(1)
&& base.logicalNozzleIdToLastFuelSaleTrxMapping[1].TransactionSeqNumberFromPhysicalPump
== getNozzleStatusResponse.流水号.ToString())
{
logger.Info("Pump: " + this.PumpId + ", fuel trx seq No. have NOT changed(previous seq No.: "
+ base.logicalNozzleIdToLastFuelSaleTrxMapping[1].TransactionSeqNumberFromPhysicalPump + "), will ignore");
return;
}
// lastSale exists and 流水号 diff
if (base.logicalNozzleIdToLastFuelSaleTrxMapping.ContainsKey(1)
&& base.logicalNozzleIdToLastFuelSaleTrxMapping[1].TransactionSeqNumberFromPhysicalPump
!= getNozzleStatusResponse.流水号.ToString())
{
logger.Info("Pump: " + base.PumpId
+ ", Detect a fule trx with 流水号: " + getNozzleStatusResponse.流水号 + " which is diff from previous fule trx(previous 流水号:" +
this.logicalNozzleIdToLastFuelSaleTrxMapping[1].TransactionSeqNumberFromPhysicalPump + "), will generate a new fule sale trx with vol: " + getNozzleStatusResponse.加油量);
this.logicalNozzleIdToLastFuelSaleTrxMapping[1] = new FuelSaleTransaction() { TransactionSeqNumberFromPhysicalPump = getNozzleStatusResponse.流水号.ToString() };
}
// lastSale not exists
if (!this.logicalNozzleIdToLastFuelSaleTrxMapping.ContainsKey(1))
{
logger.Info("Pump: " + base.PumpId + ", very first initial trx detected on this pump, will generate a new fule sale trx(seq: " + getNozzleStatusResponse.流水号 + ", vol: " + getNozzleStatusResponse.加油量 + ", amt: " + getNozzleStatusResponse.加油金额 + ")");
this.logicalNozzleIdToLastFuelSaleTrxMapping.Add(1, new FuelSaleTransaction() { TransactionSeqNumberFromPhysicalPump = getNozzleStatusResponse.流水号.ToString() });
}
var amount = getNozzleStatusResponse.加油金额;
var fuelingVol = getNozzleStatusResponse.加油量;
var price = getNozzleStatusResponse.单价;
var seqNo = getNozzleStatusResponse.流水号;
logger.Info("Pump: " + base.PumpId + ", fire trx done with amt: " + amount + ", vol: " + fuelingVol
+ ", price: " + price + ", seq No.: " + seqNo);
//ThreadPool.QueueUserWorkItem(o =>
//{
var totalizer = await this.QueryTotalizerAsync(1);
base.FireOnCurrentFuellingStatusChangeEvent(new FdcTransaction()
{
// 恒山油机 一个加油点只有一把枪
Nozzle = this.nozzles.First(),
Amount = amount,
Volumn = fuelingVol,
Price = price,
SequenceNumberGeneratedOnPhysicalPump = seqNo,
AmountTotalizer = totalizer.Item1,
VolumeTotalizer = totalizer.Item2,
Finished = true,
});
//});
}
}
else
{
if (logger.IsTraceEnabled)
logger.Trace("Unhandled GetNozzleStatusResponse is incoming: " + getNozzleStatusResponse.ToLogString());
}
}
else
{
await base.Process(context);
}
}
///
///
///
/// useless for this type of pump, it always one pump one nozzle
///
public override async Task AuthorizeAsync(byte logicalNozzleId)
{
return false;
}
///
///
///
///
/// useless for this type of pump, it always one pump one nozzle
///
public override async Task AuthorizeWithAmountAsync(int moneyAmount, byte logicalNozzleId)
{
return false;
}
///
///
///
///
/// useless for this type of pump, it always one pump one nozzle
///
public override async Task AuthorizeWithVolumeAsync(int volumn, byte logicalNozzleId)
{
return false;
}
}
}