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; } } }