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 Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump; using Wayne.FDCPOSLibrary; using System.Xml; using Edge.Core.Database.Models; using System.Threading.Tasks; namespace FuRen_Sinopec_IcCardReader { /// <summary> /// This pump handler only listen the ic card reader, will never send message. /// </summary> public class SilentPumpHandler : IFdcPumpController, IDeviceHandler<byte[], KaJiLianDongV11MessageTemplateBase> { static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("PumpHandler"); protected IContext<byte[], KaJiLianDongV11MessageTemplateBase> context; public event EventHandler<FdcPumpControllerOnStateChangeEventArg> OnStateChange; /// <summary> /// fired on fueling process is on going, the fuel amount should keep changing. /// </summary> public event EventHandler<FdcTransactionDoneEventArg> OnCurrentFuellingStatusChange; protected LogicalDeviceState lastLogicalDeviceState = LogicalDeviceState.FDC_CLOSED; private DateTime lastLogicalDeviceStateReceivedTime; // by seconds private const int lastLogicalDeviceStateExpiredTime = 6; private Guid uniqueId = Guid.NewGuid(); private int pumpId = -1; protected List<LogicalNozzle> nozzles = new List<LogicalNozzle>(); public IEnumerable<LogicalNozzle> Nozzles => this.nozzles; /// <summary> /// </summary> /// <param name="pumpId"></param> /// <param name="nozzlesXmlConfiguration"></param> public SilentPumpHandler(int pumpId) { this.pumpId = pumpId; //this.nozzles.Add(new LogicalNozzle(pumpId, 1, 1, null)); } public void Init(IContext<byte[], KaJiLianDongV11MessageTemplateBase> context) { this.context = context; } public virtual async Task Process(IContext<byte[], KaJiLianDongV11MessageTemplateBase> context) { this.context = context; this.lastLogicalDeviceStateReceivedTime = DateTime.Now; //PumpStateChangeCommand -- it's also the PC polling message response from pump. //PumpNotifyTransactionDoneCommand if (this.lastLogicalDeviceState == LogicalDeviceState.FDC_OFFLINE) { logger.Info("Pump: " + this.pumpId + ", " + "Recevied an IcCard Msg in FDC_OFFLINE state, " + "indicates the underlying connection is established, switch to FDC_READY"); this.lastLogicalDeviceState = LogicalDeviceState.FDC_READY; var safe = this.OnStateChange; safe?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_READY)); } if (context.Incoming.Message is PumpStateChangeCommand PumpStateChangeCommand) { if (PumpStateChangeCommand.StateNozzleOperatingSubMessages == null && PumpStateChangeCommand.StateNozzleOperatingSubMessages == null) { if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_READY) { this.lastLogicalDeviceState = LogicalDeviceState.FDC_READY; var safe2 = this.OnStateChange; safe2?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg( LogicalDeviceState.FDC_READY, this.nozzles.FirstOrDefault())); } } if (PumpStateChangeCommand.StateNozzleOperatingSubMessages != null && PumpStateChangeCommand.StateNozzleOperatingSubMessages.Any(n => n.St状态字 == PumpStateChangeNozzleOperatingSubState.PumpStateChangeCode.抬枪或加油中)) { if (!this.nozzles.Any()) this.nozzles.Add(new LogicalNozzle(this.pumpId, 0, PumpStateChangeCommand.StateNozzleOperatingSubMessages.First().MZN枪号, PumpStateChangeCommand.StateNozzleOperatingSubMessages.First().PRC价格)); if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_FUELLING) { this.lastLogicalDeviceState = LogicalDeviceState.FDC_FUELLING; var safe = this.OnCurrentFuellingStatusChange; safe?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction() { Nozzle = this.nozzles.First(), Amount = PumpStateChangeCommand.StateNozzleOperatingSubMessages.First().AMN数额, Volumn = PumpStateChangeCommand.StateNozzleOperatingSubMessages.First().VOL升数, Price = PumpStateChangeCommand.StateNozzleOperatingSubMessages.First().PRC价格, Finished = false, })); } } } else if (context.Incoming.Message is PumpNotifyTransactionDoneCommand PumpNotifyTransactionDoneCommand) { if (!this.nozzles.Any()) this.nozzles.Add(new LogicalNozzle(this.pumpId, 0, PumpNotifyTransactionDoneCommand.NZN_枪号, PumpNotifyTransactionDoneCommand.PRC_成交价格)); var safe1 = this.OnCurrentFuellingStatusChange; safe1?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction() { // 恒山油机 一个加油点只有一把枪 Nozzle = this.nozzles.First(), Amount = PumpNotifyTransactionDoneCommand.AMN数额, Volumn = PumpNotifyTransactionDoneCommand.VOL_升数, Price = PumpNotifyTransactionDoneCommand.PRC_成交价格, SequenceNumberGeneratedOnPhysicalPump = PumpNotifyTransactionDoneCommand.POS_TTC, //AmountTotalizer = , VolumeTotalizer = PumpNotifyTransactionDoneCommand.V_TOT_升累计, Finished = true, })); } else { if (logger.IsDebugEnabled) logger.Debug("Incoming an unknown message, will do nothing and ignore."); } } public string Name => this.GetType().FullName; public Guid Id => this.uniqueId; /// <summary> /// Gets the Identification of the pump for the system. Is the logical number of the pump /// </summary> public int PumpId => this.pumpId; /// <summary> /// this pump have no way to share same comport since this HengShan protocol content does not contains /// any id info, so always static 0 here. /// 地址面地址 /// </summary> public int PumpPhysicalId => 0; public int AmountDecimalDigits => 2; public int VolumeDecimalDigits => 2; public int PriceDecimalDigits => 2; public int VolumeTotalizerDecimalDigits => 2; public virtual async Task<LogicalDeviceState> QueryStatusAsync() { // if last state is expired, we return a OFFLINE here to FdcClient. if (DateTime.Now.Subtract(this.lastLogicalDeviceStateReceivedTime).TotalSeconds > lastLogicalDeviceStateExpiredTime) { if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_OFFLINE) { this.lastLogicalDeviceState = LogicalDeviceState.FDC_OFFLINE; logger.Info("Pump: " + this.pumpId + ", " + " State switched to FDC_OFFLINE due to cached state expired"); var safe0 = this.OnStateChange; safe0?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_OFFLINE, null)); } return LogicalDeviceState.FDC_OFFLINE; } return this.lastLogicalDeviceState; } /// <summary> /// /// </summary> /// <returns>MoneyTotalizer:VolumnTotalizer</returns> public async Task<Tuple<int, int>> QueryTotalizerAsync(byte logicalNozzleId) { throw new NotImplementedException(); } public virtual async Task<bool> ChangeFuelPriceAsync(int newPriceWithoutDecimalPoint, byte logicalNozzleId) { throw new NotImplementedException(); } /// <summary> /// /// </summary> /// <param name="logicalNozzleId">useless for this type of pump, it always one pump one nozzle</param> /// <returns></returns> public virtual async Task<bool> AuthorizeAsync(byte logicalNozzleId) { throw new NotImplementedException(); } /// <summary> /// /// </summary> /// <param name="moneyAmount"></param> /// <param name="logicalNozzleId">useless for this type of pump, it always one pump one nozzle</param> /// <returns></returns> public virtual async Task<bool> AuthorizeWithAmountAsync(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId) { throw new NotImplementedException(); } /// <summary> /// /// </summary> /// <param name="volumn"></param> /// <param name="logicalNozzleId">useless for this type of pump, it always one pump one nozzle</param> /// <returns></returns> public virtual async Task<bool> AuthorizeWithVolumeAsync(int volumnWithoutDecimalPoint, byte logicalNozzleId) { throw new NotImplementedException(); } public virtual async Task<bool> FuelingRoundUpByAmountAsync(int amount) { throw new NotImplementedException(); } #region not implemented public async Task<bool> UnAuthorizeAsync(byte logicalNozzleId) { throw new NotImplementedException(); } public async Task<bool> SuspendFuellingAsync() { throw new NotImplementedException(); } public async Task<bool> ResumeFuellingAsync() { throw new NotImplementedException(); } public async Task<bool> FuelingRoundUpByVolumeAsync(int volume) { throw new NotImplementedException(); } #endregion /// <summary> /// </summary> protected Dictionary<byte, FuelSaleTransaction> logicalNozzleIdToLastFuelSaleTrxMapping = new Dictionary<byte, FuelSaleTransaction>(); public void OnFdcServerInit(Dictionary<string, object> parameters) { if (parameters.ContainsKey("LastPriceChange")) { } /* Load Last sale(from db) for void the case of FC accidently disconnect from Pump in fueling, and may cause a fueling trx gone from FC control */ if (parameters.ContainsKey("LastFuelSaleTrx")) { // nozzle logical id:lastSale //var lastFuelSaleTrxes = parameters["LastFuelSaleTrx"] as Dictionary<byte, FuelSaleTransaction>; //foreach (var lastFuelSaleTrx in lastFuelSaleTrxes) //{ // logger.Info("Pump: " + this.pumpId + ", OnFdcServerInit, load last fuel sale " + // "on logical nozzle: " + lastFuelSaleTrx.Key + " with value: " + lastFuelSaleTrx.Value); // this.logicalNozzleIdToLastFuelSaleTrxMapping.Remove(lastFuelSaleTrx.Key); // this.logicalNozzleIdToLastFuelSaleTrxMapping.Add(lastFuelSaleTrx.Key, lastFuelSaleTrx.Value); //} } } public async Task<bool> LockNozzleAsync(byte logicalNozzleId) { return false; } public async Task<bool> UnlockNozzleAsync(byte logicalNozzleId) { return false; } } }