using Edge.Core.Database; using Edge.Core.Processor; using Edge.Core.IndustryStandardInterface.Pump; using HengShan_Pump_TQC_IFSF.MessageEntity; using HengShan_Pump_TQC_IFSF.MessageEntity.Incoming; using HengShan_Pump_TQC_IFSF.MessageEntity.Outgoing; using Edge.Core.Parser.BinaryParser.Util; using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; using Wayne.FDCPOSLibrary; using Timer = System.Timers.Timer; using Edge.Core.Processor.Dispatcher.Attributes; using Edge.Core.Processor.Communicator; using Edge.Core.Configuration; namespace HengShan_Pump_TQC_IFSF { #region Ctor parameters public class PumpGroupConfiguration { //public byte AmountDecimalDigits { get; set; } //public byte VolumeDecimalDigits { get; set; } //public byte PriceDecimalDigits { get; set; } //public byte VolumeTotalizerDecimalDigits { get; set; } public byte FccIfsfSubnetValue { get; set; } public byte FccIfsfNodeValue { get; set; } public byte PumpIfsfSubnetValue { get; set; } public byte PumpIfsfNodeValue { get; set; } public List PumpConfigurations { get; set; } } public class PumpConfiguration { public byte PumpId { get; set; } /// /// 0x21 - 0x24 /// public byte PhysicalId { get; set; } public List NozzleConfigurations { get; set; } } public class NozzleConfiguration { public byte LogicalId { get; set; } /// /// 0x11 - 0x18 /// public byte PhysicalId { get; set; } } #endregion /// /// one TQC connection controls a full physical pump, typically 2 sides. /// [MetaPartsRequired(typeof(GenericDeviceProcessor<,>))] [MetaPartsRequired(typeof(HengShan_TQC_IFsfMessageTcpIpCommunicator<>))] [MetaPartsRequired(typeof(IfsfMessageBase))] [MetaPartsDescriptor( "lang-zh-cn:HS TQC加油机lang-en-us:HS TQC Pump", "lang-zh-cn:用于驱动基于TCP连接的IFSF协议的稳牌-WT30或恒山-TH22加油机,或者其它IFSF兼容的加油机lang-en-us:Used for driven Wayne-WT30 or HengShan-TH22, or other IFSF(on TCP/IP) compatible Pumps", new[] { "lang-zh-cn:加油机lang-en-us:Pump" })] public class PumpGroupHandler : IEnumerable, IDeviceHandler { private object syncObject = new object(); protected IEnumerable nozzleProductMappings; //private System.Timers.Timer heartBeatUdpPolling; //protected int heartBeatUdpPollingInterval = 6000; //private System.Timers.Timer queryTqcAllPumpStatePolling; //private long queryTqcAllPumpStatePollingInterval = 60 * 1000; private Guid uniqueId = Guid.NewGuid(); internal TqcPumpGroupInitializer tqcPumpGroupInitializer; //protected static ILog logger = log4net.LogManager.GetLogger("PumpHandler"); protected static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("PumpHandler"); protected IContext context; protected List pumpHandlers = new List(); // for test, the tqc configured as subnet 1, node 1. protected byte recipientSubnet = 1; protected byte recipientNode = 1; // originator, it's the FC self. public static byte originatorSubnet = 2; public static byte originatorNode = 1; [ParamsJsonSchemas("PumpGroupHandlerCtorParamsJsonSchemas")] public PumpGroupHandler(PumpGroupConfiguration pumpGroupConfiguration, IServiceProvider services) { originatorSubnet = pumpGroupConfiguration.FccIfsfSubnetValue; originatorNode = pumpGroupConfiguration.FccIfsfNodeValue; this.recipientSubnet = pumpGroupConfiguration.PumpIfsfSubnetValue; this.recipientNode = pumpGroupConfiguration.PumpIfsfNodeValue; logger.Info("node " + this.recipientNode + ", Will create " + pumpGroupConfiguration.PumpConfigurations.Count + " pump handlers for this TQC connection according from config"); this.CreatePumpHandlers(pumpGroupConfiguration.PumpConfigurations.Select(pc => { XmlDocument doc = new XmlDocument(); var xml = "" + "" + pc.NozzleConfigurations.Select(nc => "") .Aggregate((acc, n) => acc + n) + "" + ""; doc.LoadXml(xml); return doc.FirstChild; })); this.SetupTqcPumpGroupInitializer(); } /// /// /// /// fcc as a node in a ifsf subnet, has its subnet value /// fcc as a node in a ifsf subnet, has its node value /// public PumpGroupHandler(int fccIfsfSubnetValue, int fccIfsfNodeValue, string pumpsXmlConfiguration) { originatorSubnet = (byte)fccIfsfSubnetValue; originatorNode = (byte)fccIfsfNodeValue; // sample // // // // // // // // // // // // // // // // // // // // // // // // // // XmlDocument xmlDocument = new XmlDocument(); xmlDocument.LoadXml(pumpsXmlConfiguration); var rootElement = xmlDocument.GetElementsByTagName("PumpGroup").Cast().First(); this.recipientSubnet = byte.Parse(rootElement.Attributes["ifsfSubNet"].Value); this.recipientNode = byte.Parse(rootElement.Attributes["ifsfNode"].Value); var pumpElements = xmlDocument.GetElementsByTagName("Pump").Cast(); logger.Info("node " + recipientNode + ", Will create " + pumpElements.Count() + " pump handlers for this TQC connection according from local config"); this.CreatePumpHandlers(pumpElements); this.SetupTqcPumpGroupInitializer(); } protected virtual void SetupTqcPumpGroupInitializer() { this.tqcPumpGroupInitializer = new TqcPumpGroupInitializer(originatorSubnet, originatorNode, this.recipientSubnet, this.recipientNode, this.pumpHandlers, this.GetNewMessageToken); this.tqcPumpGroupInitializer.OnInitTimeout += (cc, dd) => { logger.Info("TqcPumpGroupInitializer, node " + this.tqcPumpGroupInitializer.ifsfRecipientNode + ", init timed out, will start it over..."); this.tqcPumpGroupInitializer.Reset(); this.tqcPumpGroupInitializer.FeedIn(this.context); }; this.tqcPumpGroupInitializer.OnInitDone += (ee, ff) => { logger.Info("TqcPumpGroupInitializer, node " + this.tqcPumpGroupInitializer.ifsfRecipientNode + ", init done, will query all FP status and routed following msg to PumpHandlers"); // query all FP status, answer will be routed to each PumpHandler to handle. this.context.Outgoing.Write( new FuellingPointDbRequest_Read_FuelPointState(this.recipientSubnet, this.recipientNode, originatorSubnet, originatorNode, this.GetNewMessageToken(), 0x20)); this.pumpHandlers.SelectMany(p => p.Nozzles).ToList().ForEach(n => { var boundProductNo = this.nozzleProductMappings.First(c => c.PumpId == n.PumpId && c.NozzleLogicalId == n.LogicalId).ProductBarcode; logger.Info("TqcPumpGroupInitializer, node " + this.tqcPumpGroupInitializer.ifsfRecipientNode + ", will write back price to nozzle on pump " + n.PumpId + ", logicalNozzleId: " + n.LogicalId + ", boundProductNo: " + boundProductNo + " to new Init Price(without decimal points): " + this.tqcPumpGroupInitializer.PriceBook[boundProductNo]); n.RealPriceOnPhysicalPump = this.tqcPumpGroupInitializer.PriceBook[boundProductNo]; }); }; } protected virtual void CreatePumpHandlers(IEnumerable pumpElements) { foreach (XmlNode pumpElement in pumpElements) { byte pumpPhysicalIdValue; var rawPumpPhysicalId = pumpElement.Attributes["physicalId"].Value;//.Substring(2); if (rawPumpPhysicalId.StartsWith("0x") || rawPumpPhysicalId.StartsWith("0X")) pumpPhysicalIdValue = byte.Parse(pumpElement.Attributes["physicalId"].Value.Substring(2), NumberStyles.HexNumber); else pumpPhysicalIdValue = byte.Parse(pumpElement.Attributes["physicalId"].Value); if (pumpPhysicalIdValue < 0x21 || pumpPhysicalIdValue > 0x24) throw new ArgumentException("ifsf fuel point id must be range from 0x21 to 0x24, while the configuration passed in " + rawPumpPhysicalId); PumpHandler pumpHandler = new PumpHandler(this, int.Parse(pumpElement.Attributes["pumpId"].Value), pumpPhysicalIdValue, this.recipientSubnet, this.recipientNode, pumpElement.InnerXml, GetNewMessageToken); this.pumpHandlers.Add(pumpHandler); } } /// /// will be called at the Init stage of FdcServerApp, that means before the calling the Start() for all the Processors. /// /// public void OnFdcServerInit(Dictionary parameters) { if (parameters != null && parameters.TryGetValue("NozzleProductMapping", out object param)) { this.nozzleProductMappings = param as IEnumerable; this.tqcPumpGroupInitializer.NozzleProductConfig = this.nozzleProductMappings; } this.pumpHandlers.ForEach(ph => ph.OnFdcServerInit(parameters)); } public virtual void Init(IContext context) { this.context = context; this.context.Communicator.OnConnected += (e, f) => { // each time communicator get disconnect, will trigger a re-init. logger.Info("TqcPumpGroupInitializer, node " + this.tqcPumpGroupInitializer.ifsfRecipientNode + ", OnCommunicator Connected, will start init..."); this.tqcPumpGroupInitializer.Reset(); this.tqcPumpGroupInitializer.FeedIn(this.context); }; this.context.Communicator.OnDisconnected += (e, f) => { logger.Info("TqcPumpGroupInitializer, node " + this.tqcPumpGroupInitializer.ifsfRecipientNode + ", OnCommunicator Disconnected..."); this.tqcPumpGroupInitializer.Reset(); this.pumpHandlers.ForEach(p => p.HandleFpStatusChange(FuellingPointStatus.Inoperative, null)); }; this.pumpHandlers.ForEach(p => p.Init(this.context)); } private byte rotateMsgToken = 0; public byte GetNewMessageToken() { if (rotateMsgToken == (0xFF & 0x01F)) rotateMsgToken = 0; else rotateMsgToken++; return rotateMsgToken; } public virtual Task Process(IContext context) { this.context = context; if (!this.tqcPumpGroupInitializer.IsInitDone) { this.tqcPumpGroupInitializer.FeedIn(context); return Task.CompletedTask; } this.RouteMessageToHandlers(context); return Task.CompletedTask; } protected virtual void RouteMessageToHandlers(IContext context) { switch (context.Incoming.Message) { case FuellingPointDb_FpStatus_Event fpStatusEvent: this.pumpHandlers.Where(h => h.PumpPhysicalId == fpStatusEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context)); break; case FuellingPointDb_FpStatus_Answer fpStatusAnswer: this.pumpHandlers.Where(h => h.PumpPhysicalId == fpStatusAnswer.TargetFuelPointId).ToList().ForEach(p => p.Process(context)); break; case FuellingPointDb_FpRunningTransaction_Event fpRunningTrxEvent: this.pumpHandlers.Where(h => h.PumpPhysicalId == fpRunningTrxEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context)); break; case FuellingPointDb_FpRunningTransaction_Event_ACK fpRunningTrxEvent: this.pumpHandlers.Where(h => h.PumpPhysicalId == fpRunningTrxEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context)); break; case FuellingTrxDb_TransactionBufferStatus_Event trxBufferStatusEvent: this.pumpHandlers.Where(h => h.PumpPhysicalId == trxBufferStatusEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context)); break; case FuellingTrxDb_TransactionBufferStatus_Event_ACK trxBufferStatusEvent: this.pumpHandlers.Where(h => h.PumpPhysicalId == trxBufferStatusEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context)); break; case FuellingTrxDb_TransactionBufferStatus_Answer trxBufferStatusAnswer: this.pumpHandlers.Where(h => h.PumpPhysicalId == trxBufferStatusAnswer.TargetFuelPointId).ToList().ForEach(p => p.Process(context)); break; //default: this.pumpHandlers.ForEach(p => p.Process(context)); break; } } public IEnumerator GetEnumerator() { return this.pumpHandlers.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return this.pumpHandlers.GetEnumerator(); } public Guid Id => this.uniqueId; /// /// Initalizer for init default values into TQC pump. /// especially for default product price: it will read pre-config values for each product in pump first, /// internal class TqcPumpGroupInitializer { System.Timers.Timer initTimeoutTimer = new Timer(initTimeout); const int initTimeout = 10000; /// /// will fire when reached timed out time, and state is still not Done. /// public event EventHandler OnInitTimeout; public event EventHandler OnInitDone; public event EventHandler OnInitError; /// /// init process will firstly read all necessary config values in TQC, and then execute the write operations. /// this event will be fired once those config values were read, and write operation is still not perform. /// public event EventHandler OnTqcExistedConfigRead; private List fpStatus_Answers = new List(); private List logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers = new List(); /// /// Gets pre-config value in TQC pump. /// public IEnumerable LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers => this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers; private List productDb_ProductNo_Answers = new List(); /// /// Gets pre-config value in TQC pump. /// public IEnumerable ProductDb_ProductNo_Answers => this.productDb_ProductNo_Answers; private List productPerFuellingModeDb_ProductPrice_Answers = new List(); /// /// Gets pre-config value in TQC pump. /// public IEnumerable ProductPerFuellingModeDb_ProductPrice_Answers => this.productPerFuellingModeDb_ProductPrice_Answers; private Dictionary priceBook = new Dictionary(); /// /// Gets or set the init price for each product. /// default price init policy is read from pricebook, if found then apply, otherwise use pre-config value read from pump. /// should follow-> barcode:rawFormatPriceWithoutDecimalPoints /// public Dictionary PriceBook => this.priceBook; private IEnumerable nozzleProductConfig; public IEnumerable NozzleProductConfig { get { return this.nozzleProductConfig; } set { this.nozzleProductConfig = value; this.thisTqcProductBarcodes = this.nozzleProductConfig.Where(n => this.pumpHandlers.Select(p => p.PumpId).Contains(n.PumpId)).Select(s => s.ProductBarcode).Distinct().OrderBy(o => o).ToList(); //this.thisTqcProductBarcodes = nozzleProductConfig.Select(s => s.ProductBarcode).Distinct().OrderBy(o => o).ToList(); logger.Info("PumpInitializer, node " + ifsfRecipientNode + " current TQC local configuration indicates total have " + this.thisTqcProductBarcodes.Count + " product barcodes: " + this.thisTqcProductBarcodes.Select(s => s.ToString()).Aggregate((acc, n) => acc + ", " + n)); } } //= Configurator.Default.NozzleProductConfiguration.Mapping; private byte ifsfSelfSubnet; private byte ifsfSelfNode; private byte ifsfRecipientSubnet; public byte ifsfRecipientNode; IEnumerable pumpHandlers; Func msgTokenGenerator; private List thisTqcProductBarcodes; private Status currentStatus; private Status CurrentStatus { get { return this.currentStatus; } set { //logger.Debug("PumpInitializer, node " + ifsfRecipientNode + ", Status switched from " + this.currentStatus + " to " + value); this.currentStatus = value; } } //private DateTime previousRequestingTime; private byte? pendingForAckMsgToken; private enum Status { UnInit = 0, Wait_Answer_Query_All_FuelPointState, Wait_Answer_Close_All_FuelPoint, Wait_Answer_Query_Caculator_Overall_Info, Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId, Wait_Answer_Read_All_ProductNumber, Wait_Answer_Read_All_ProductPrice, //Sending_Add_Recipient_Addr, Wait_ACK_Add_Recipient_Addr, //Sending_Clear_ProductNumber_In_Caculator, Wait_ACK_Clear_ProductNumber_In_ProductDb, //Sending_Set_ProductNumber_In_Caculator, Wait_ACK_Set_ProductNumber_In_ProductDb, //Sending_Link_Meter_To_CaculatorSlot, Wait_ACK_Link_Meter_To_ProductDb_ProductNoSlot, //Sending_Set_AUTH_MODE, Wait_ACK_Set_AUTH_MODE, //Sending_Set_Price, Wait_ACK_Set_Price, //Sending_Set_Nozzle_To_CacalatorSlot, Wait_ACK_Set_Nozzle_To_ProductDb_ProductNoSlot, //Sending_Set_FP_Default_FuellingMode, Wait_ACK_Set_FP_Default_FuellingMode, //Send_Open_FP, //Wait_ACK_Open_FP, Done } /// /// /// /// fc ifsf subnet value /// fc ifsf node value /// remote ifsf recipient subnet value /// remote ifsf recipient node value /// pump handlers created from the pump Group handler /// public TqcPumpGroupInitializer(byte selfSubnet, byte selfNode, byte ifsfRecipientSubnet, byte ifsfRecipientNode, IEnumerable pumpHandlers, Func msgTokenGenerator) { this.ifsfSelfSubnet = selfSubnet; this.ifsfSelfNode = selfNode; this.ifsfRecipientSubnet = ifsfRecipientSubnet; this.ifsfRecipientNode = ifsfRecipientNode; this.pumpHandlers = pumpHandlers; this.msgTokenGenerator = msgTokenGenerator; this.initTimeoutTimer.Elapsed += (a, b) => { this.initTimeoutTimer.Stop(); if (!this.IsInitDone) { var safe = this.OnInitTimeout; safe(this, null); } }; } private byte next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1; private byte next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1; //private byte next_Link_Meter_to_ProductNumber_Index = 1; private byte next_site_nozzle_Index = 1; private byte next_Fp_Index = 1; /// /// if it was Done, return false. otherwise, the init is kicking off, return true. /// /// /// public bool FeedIn(IContext context) { if (!this.initTimeoutTimer.Enabled) this.initTimeoutTimer.Start(); if (IsInitDone) return false; switch (this.CurrentStatus) { case Status.UnInit: { logger.Info("PumpInitializer, node " + ifsfRecipientNode + " start Init, firstly Query&Close all FuelPoints..."); logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Send QueryAllFp state(first time)"); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new FuellingPointDbRequest_Read_FuelPointState(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, 0x20 )); this.CurrentStatus = Status.Wait_Answer_Query_All_FuelPointState; break; } case Status.Wait_Answer_Query_All_FuelPointState: { if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value) { var historyIncoming = context.Incoming as HistoryKeepIncoming; this.fpStatus_Answers = historyIncoming.History.Where(h => (h.Item1 is FuellingPointDb_FpStatus_Answer im) && im.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER && im.MessageToken == this.pendingForAckMsgToken) .Select(s => (FuellingPointDb_FpStatus_Answer)s.Item1).ToList(); logger.Info("PumpInitializer, node " + ifsfRecipientNode + ", read total " + fpStatus_Answers.Count + " Fp status, they're " + this.fpStatus_Answers.Select(s => "Fp 0x" + s.TargetFuelPointId.ToHexLogString() + " is " + s.FuelPointState) .Aggregate((n, acc) => n + ", " + acc)); this.pendingForAckMsgToken = this.msgTokenGenerator(); logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Send CloseFuelPoint(first time) for FP: 0x" + this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString()); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new FuellingPointDbRequest_Write_CloseFuelPoint(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId) )); this.CurrentStatus = Status.Wait_Answer_Close_All_FuelPoint; } break; } case Status.Wait_Answer_Close_All_FuelPoint: { if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK && ifsfMsg.MessageToken == this.pendingForAckMsgToken) { if (++next_Fp_Index <= this.pumpHandlers.Count()) { logger.Info("PumpInitializer, node " + ifsfRecipientNode + " CloseFuelPoint Acked"); logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Send CloseFuelPoint for FP: 0x" + this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString()); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new FuellingPointDbRequest_Write_CloseFuelPoint(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId) )); this.CurrentStatus = Status.Wait_Answer_Close_All_FuelPoint; } else { this.next_Fp_Index = 1; logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending CaculatorDbRequest_Read_FuelPoint_Product_FuelMode_Meter_Info..."); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new CaculatorDbRequest_Read_FuelPoint_Product_FuelMode_Meter_Info(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value )); this.CurrentStatus = Status.Wait_Answer_Query_Caculator_Overall_Info; } } break; } case Status.Wait_Answer_Query_Caculator_Overall_Info: { if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER && ifsfMsg.MessageToken == this.pendingForAckMsgToken) { var dataParser = DatabaseDataParser.New().Convert(ifsfMsg.RawDatabaseData.ToArray()); var numberOfProducts = dataParser.DataIds.First(i => i.Item1 == 0x02).Item2.ToInt32(); logger.Info("PumpInitializer, node " + ifsfRecipientNode + " CaculatorDbRequest_Read_FuelPoint_Product_FuelMode_Meter_Info Acked,\r\n" + "Read caculatorDbOverallInfo, No_Products: " + numberOfProducts + ", No_Fuelling_Modes: " + dataParser.DataIds.First(i => i.Item1 == 0x03).Item2.ToInt32() + ", No_Meters: " + dataParser.DataIds.First(i => i.Item1 == 0x04).Item2.ToInt32() + ", No_FP: " + dataParser.DataIds.First(i => i.Item1 == 0x05).Item2.ToInt32() + ", Auth_State_Mode: " + dataParser.DataIds.First(i => i.Item1 == 0x0B).Item2.ToInt32()); if (numberOfProducts != this.thisTqcProductBarcodes.Count) { logger.Info("!!!!!!PumpInitializer, node " + ifsfRecipientNode + " This TQC MUST config " + numberOfProducts + " products, but now trying to load local config with " + this.thisTqcProductBarcodes.Count + " products"); } logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending CommunicationServiceDbRequest_Write_RecipientAddressTable..."); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new CommunicationServiceDbRequest_Write_RecipientAddressTable(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, this.ifsfSelfSubnet, this.ifsfSelfNode)); this.CurrentStatus = Status.Wait_ACK_Add_Recipient_Addr; } break; } case Status.Wait_ACK_Add_Recipient_Addr: { if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK && ifsfMsg.MessageToken == this.pendingForAckMsgToken) { logger.Info("PumpInitializer, node " + ifsfRecipientNode + " CommunicationServiceDbRequest_Write_RecipientAddressTable Acked."); logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Send LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId for FP: 0x" + this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString()); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 0x10)); this.CurrentStatus = Status.Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId; } break; } case Status.Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId: { if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value) { var historyIncoming = context.Incoming as HistoryKeepIncoming; this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers.AddRange( historyIncoming.History.Where(h => (h.Item1 is LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer im) && im.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER && im.MessageToken == this.pendingForAckMsgToken) .Select(s => s.Item1 as LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer).OrderBy(a => a.LogicalNozzleId)); if (++next_Fp_Index <= this.pumpHandlers.Count()) { logger.Info("PumpInitializer, node " + ifsfRecipientNode + " LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId Acked"); logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Send LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId for FP: 0x" + this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString()); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 0x10)); this.CurrentStatus = Status.Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId; } else { foreach (var answer in this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers) logger.Info(" node " + ifsfRecipientNode + " Read nozzles info for FP: 0x" + answer.TargetFuelPointId.ToHexLogString() + ", with logicalId: 0x" + answer.LogicalNozzleId.ToHexLogString() + ", with physicalId: 0x" + answer.PhyscialNozzleId.ToHexLogString() + ", with product slotId: 0x" + answer.LinkedProductSlotId_InProductDbProdcutNoSlot.ToHexLogString()); next_Fp_Index = 1; logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending ProductDbRequest_Read_ProductNo"); this.pendingForAckMsgToken = this.msgTokenGenerator(); // will receive several answers and an ACK messages since this is a query all request. context.Outgoing.Write(new ProductDbRequest_Read_ProductNo(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, 0)); this.CurrentStatus = Status.Wait_Answer_Read_All_ProductNumber; } } break; } case Status.Wait_Answer_Read_All_ProductNumber: { if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK && ifsfMsg.MessageToken == this.pendingForAckMsgToken) { var historyIncoming = context.Incoming as HistoryKeepIncoming; //logger.Debug("************" + historyIncoming.History.Select(a => a.Item1.GetType().Name).Aggregate((acc, n) => acc + ", " + n)); //logger.Debug("************historyIncoming.History.Count: " + historyIncoming.History.Count); this.productDb_ProductNo_Answers.AddRange( historyIncoming.History.Where(h => (h.Item1 is ProductDb_ProductNo_Answer im) && im.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER && im.MessageToken == this.pendingForAckMsgToken).Select(s => s.Item1 as ProductDb_ProductNo_Answer).OrderBy(a => a.ProductSlotId)); if (this.productDb_ProductNo_Answers.Any()) { logger.Info(" node " + ifsfRecipientNode + " Read all existed product number in a TQC which are: " + this.productDb_ProductNo_Answers.Select(s => s.ProductNumber + "(in 0x" + s.ProductSlotId.ToHexLogString() + ")").Aggregate((acc, n) => acc + ", " + n)); string actualProductNumber = this.productDb_ProductNo_Answers.ElementAt(this.next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1).ProductNumber;//this.siteProductBarcodes[next_Clear_ProductNumber_In_Caculator_SlotIndex - 1].ToString(); this.pendingForAckMsgToken = this.msgTokenGenerator(); // each Product will receive a serial of answers for its differnt FuelMode since we input 0x10 as query all FM. context.Outgoing.Write(new ProductPerFuellingModeDbRequest_Read_ProductPrice(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, actualProductNumber, 0x10)); this.CurrentStatus = Status.Wait_Answer_Read_All_ProductPrice; } else { /* TQC without any product configed, happened for fresh new TQC pump (get hardware reset or just shipped from factory??), handle here */ logger.Info(" node " + ifsfRecipientNode + " has no ProductDb_ProductNo_Answer received, seems there're no product configurated in this TQC yet, will directly Clear Set_ProductNumber_In_ProductDb"); logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending Clear ProductNumber in ProductDb slot index: " + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index, null)); this.CurrentStatus = Status.Wait_ACK_Set_ProductNumber_In_ProductDb; } } break; } case Status.Wait_Answer_Read_All_ProductPrice: { if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value) { var historyIncoming = context.Incoming as HistoryKeepIncoming; var resultSetsForOneProduct = historyIncoming.History.Where(h => (h.Item1 is ProductPerFuellingModeDb_ProductPrice_Answer im) && im.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER && im.MessageToken == this.pendingForAckMsgToken).Select(s => s.Item1 as ProductPerFuellingModeDb_ProductPrice_Answer); logger.Info(" node " + ifsfRecipientNode + ", Read one existed product price with product number: " + resultSetsForOneProduct.Last().ProductNumber + ", price are: " + resultSetsForOneProduct.OrderBy(a => a.FuelModeId).Select(s => s.Price + " in FM:0x" + s.FuelModeId.ToString("X")).Aggregate((acc, n) => acc + ", " + n)); this.productPerFuellingModeDb_ProductPrice_Answers.AddRange(resultSetsForOneProduct); if (this.productDb_ProductNo_Answers.Count() >= ++next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index) { string actualProductNumber = this.productDb_ProductNo_Answers.ElementAt(this.next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1).ProductNumber;//this.siteProductBarcodes[next_Clear_ProductNumber_In_Caculator_SlotIndex - 1].ToString(); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write(new ProductPerFuellingModeDbRequest_Read_ProductPrice(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, actualProductNumber, 0x10)); this.CurrentStatus = Status.Wait_Answer_Read_All_ProductPrice; } else { next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1; logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Read_All_ProductPrice done!"); var safe = this.OnTqcExistedConfigRead; safe?.Invoke(this, null); logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending Clear productNo(1st time) in ProductDb with slot index: " + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index, null)); this.CurrentStatus = Status.Wait_ACK_Clear_ProductNumber_In_ProductDb; } } break; } case Status.Wait_ACK_Clear_ProductNumber_In_ProductDb: { if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value) { if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived) logger.Error(" node " + ifsfRecipientNode + ", clear product from ProductDb failed!"); logger.Info(" PumpInitializer, node " + ifsfRecipientNode + ", clear product no. from ProductDb with msgToken:" + this.pendingForAckMsgToken.Value + " Acked."); if (++next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index <= 8) { logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending Clear productNo in ProductDb with slot index: " + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index, null)); this.CurrentStatus = Status.Wait_ACK_Clear_ProductNumber_In_ProductDb; } else { logger.Info("PumpInitializer, node " + ifsfRecipientNode + " clear product number on all productDb productNo slots(total:" + (next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1) + ") done!"); next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1; var next_site_nozzle = this.pumpHandlers.SelectMany(p => p.Nozzles) .OrderBy(n => n.PumpId).OrderBy(n => n.LogicalId).ElementAt(next_site_nozzle_Index - 1); var nozzleProductConfig = this.nozzleProductConfig .First(c => c.PumpId == next_site_nozzle.PumpId && c.NozzleLogicalId == next_site_nozzle.LogicalId); var ifsfNozzle = this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers .First(ifsfNzl => ifsfNzl.TargetFuelPointId == this.pumpHandlers.First(p => p.PumpId == next_site_nozzle.PumpId).PumpPhysicalId && ifsfNzl.LogicalNozzleId == next_site_nozzle.PhysicalId); var nozzleBoundToProductDbProductSlot = ifsfNozzle.LinkedProductSlotId_InProductDbProdcutNoSlot; logger.Info("PumpInitializer, node " + ifsfRecipientNode + " pump " + next_site_nozzle.PumpId + ", nozzle logicalId " + next_site_nozzle.LogicalId + "(ifsf FpId 0x" + ifsfNozzle.TargetFuelPointId.ToHexLogString() + ", ifsf nozzle logicalId 0x" + ifsfNozzle.LogicalNozzleId.ToHexLogString() + ")" + " is hardware-bound to productDb product No. slot 0x" + nozzleBoundToProductDbProductSlot.ToHexLogString() + ", will(1st time) set 0x" + nozzleBoundToProductDbProductSlot.ToHexLogString() + " to product No.: " + nozzleProductConfig.ProductBarcode); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, (byte)(nozzleBoundToProductDbProductSlot - 0x40), nozzleProductConfig.ProductBarcode.ToString())); this.CurrentStatus = Status.Wait_ACK_Set_ProductNumber_In_ProductDb; } } break; } case Status.Wait_ACK_Set_ProductNumber_In_ProductDb: { if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value) { if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived) logger.Error(" node " + ifsfRecipientNode + ", set product to ProductDb failed!"); logger.Info(" PumpInitializer, node " + ifsfRecipientNode + " ProductDbRequest_Write_SetProductNumber with msgToken:" + this.pendingForAckMsgToken.Value + " Acked."); if (++next_site_nozzle_Index <= this.pumpHandlers.SelectMany(p => p.Nozzles).Count()) { var next_site_nozzle = this.pumpHandlers.SelectMany(p => p.Nozzles) .OrderBy(n => n.PumpId).OrderBy(n => n.LogicalId).ElementAt(next_site_nozzle_Index - 1); var nozzleProductConfig = this.nozzleProductConfig .First(c => c.PumpId == next_site_nozzle.PumpId && c.NozzleLogicalId == next_site_nozzle.LogicalId); var ifsfNozzle = this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers .First(ifsfNzl => ifsfNzl.TargetFuelPointId == this.pumpHandlers.First(p => p.PumpId == next_site_nozzle.PumpId).PumpPhysicalId && ifsfNzl.LogicalNozzleId == next_site_nozzle.PhysicalId); var nozzleBoundToProductDbProductSlot = ifsfNozzle.LinkedProductSlotId_InProductDbProdcutNoSlot; logger.Info("PumpInitializer, node " + ifsfRecipientNode + " pump " + next_site_nozzle.PumpId + ", nozzle logicalId " + next_site_nozzle.LogicalId + "(ifsf FpId 0x" + ifsfNozzle.TargetFuelPointId.ToHexLogString() + ", ifsf nozzle logicalId 0x" + ifsfNozzle.LogicalNozzleId.ToHexLogString() + ")" + " is hardware-bound to productDb product No. slot 0x" + nozzleBoundToProductDbProductSlot.ToHexLogString() + ", will set 0x" + nozzleBoundToProductDbProductSlot.ToHexLogString() + " to product No.: " + nozzleProductConfig.ProductBarcode); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, (byte)(nozzleBoundToProductDbProductSlot - 0x40), nozzleProductConfig.ProductBarcode.ToString())); this.CurrentStatus = Status.Wait_ACK_Set_ProductNumber_In_ProductDb; } else { next_site_nozzle_Index = 1; logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol"); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol.Caculator_Auth_State_Mode.AUTH_State_Allowed)); this.CurrentStatus = Status.Wait_ACK_Set_AUTH_MODE; } //if (++next_Clear_ProductNumber_In_Caculator_SlotIndex <= 8) //this.CurrentStatus = Status.Sending_Link_Meter_To_CaculatorSlot; //logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending MeterDbRequest_Write_SetMesureToProduct(first), meterId: " // + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index + ", 2nd arg: " + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index); //this.pendingForAckMsgToken = this.msgTokenGenerator(); //context.Outgoing.Write( // new MeterDbRequest_Write_SetMesureToProduct(this.ifsfRecipientSubnet, // this.ifsfRecipientNode, // this.ifsfSelfSubnet, // this.ifsfSelfNode, // this.pendingForAckMsgToken.Value, // next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index, // next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index)); //this.CurrentStatus = Status.Wait_ACK_Link_Meter_To_ProductDb_ProductNoSlot; } break; } //case Status.Wait_ACK_Link_Meter_To_ProductDb_ProductNoSlot: // { // if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK // && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value) // { // if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived) // logger.Error(" node " + ifsfRecipientNode + ", link meter to ProductDb failed!"); // logger.Info(" PumpInitializer, MeterDbRequest_Write_SetMesureToProduct with msgToken:" + this.pendingForAckMsgToken.Value + " Acked."); // if (this.thisTqcProductBarcodes.Count >= ++next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index) // { // //this.CurrentStatus = Status.Sending_Set_ProductNumber_In_Caculator; // this.pendingForAckMsgToken = this.msgTokenGenerator(); // // put 91 to 98 to caculator 8 slots. // string operatingProductNumber = this.thisTqcProductBarcodes[next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1].ToString(); // logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending ProductDbRequest_Write_SetProductNumber, ProductDb ProductNoSlot: " // + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index + ", productNo: " + operatingProductNumber); // context.Outgoing.Write( // new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet, // this.ifsfRecipientNode, // this.ifsfSelfSubnet, // this.ifsfSelfNode, // this.pendingForAckMsgToken.Value, // next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index, operatingProductNumber)); // this.CurrentStatus = Status.Wait_ACK_Set_ProductNumber_In_ProductDb; // } // else // { // next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1; // logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Link_Meter_To_ProductDb_ProductNoSlot done!"); // //this.CurrentStatus = Status.Sending_Set_AUTH_MODE; // } // } // break; // } case Status.Wait_ACK_Set_AUTH_MODE: { if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value) { logger.Info("PumpInitializer, node " + ifsfRecipientNode + " CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol Acked."); var targetBarcode = this.thisTqcProductBarcodes[next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1]; int newInitPriceWithoutDecimalPoints; if (this.PriceBook.ContainsKey(targetBarcode)) newInitPriceWithoutDecimalPoints = this.PriceBook[targetBarcode]; else { // try load the existed pre-configed price value which just read from TQC pump. var preConfigedPriceOnPump = this.productPerFuellingModeDb_ProductPrice_Answers.FirstOrDefault(p => p.FuelModeId == 0x11 && p.ProductNumber == targetBarcode.ToString().PadLeft(8, '0'))?.Price; if (string.IsNullOrEmpty(preConfigedPriceOnPump)) newInitPriceWithoutDecimalPoints = 1883 + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index; else newInitPriceWithoutDecimalPoints = int.Parse(preConfigedPriceOnPump); this.PriceBook.Add(targetBarcode, newInitPriceWithoutDecimalPoints); } logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending ProductPerFuellingModeDbRequest_Write_SetPrice, productNo: " + targetBarcode.ToString() + ", new price(without decimalPoints): " + newInitPriceWithoutDecimalPoints.ToString()); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new ProductPerFuellingModeDbRequest_Write_SetPrice(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, targetBarcode.ToString(), newInitPriceWithoutDecimalPoints.ToString() , 0x11) ); this.CurrentStatus = Status.Wait_ACK_Set_Price; } break; } case Status.Wait_ACK_Set_Price: { if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value) { if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived) logger.Error(" node " + ifsfRecipientNode + ", set price for product failed!"); if (this.thisTqcProductBarcodes.Count >= ++next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index) { logger.Info("PumpInitializer, node " + ifsfRecipientNode + " ProductPerFuellingModeDbRequest_Write_SetPrice Acked."); var targetBarcode = this.thisTqcProductBarcodes[next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1]; int newInitPriceWithoutDecimalPoints; if (this.PriceBook != null && this.PriceBook.ContainsKey(targetBarcode)) newInitPriceWithoutDecimalPoints = this.PriceBook[targetBarcode]; else { // try load the existed pre-configed price value which just read from TQC pump. var preConfigedPriceOnPump = this.productPerFuellingModeDb_ProductPrice_Answers.FirstOrDefault(p => p.FuelModeId == 0x11 && p.ProductNumber == targetBarcode.ToString().PadLeft(8, '0'))?.Price; if (string.IsNullOrEmpty(preConfigedPriceOnPump)) newInitPriceWithoutDecimalPoints = 1883 + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index; else newInitPriceWithoutDecimalPoints = int.Parse(preConfigedPriceOnPump); this.PriceBook.Add(targetBarcode, newInitPriceWithoutDecimalPoints); } logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending ProductPerFuellingModeDbRequest_Write_SetPrice, productNo: " + targetBarcode.ToString() + ", new price(without decimalPoints): " + newInitPriceWithoutDecimalPoints.ToString()); this.pendingForAckMsgToken = this.msgTokenGenerator(); //logger.Info("PumpInitializer, node "+ifsfRecipientNode+" will set product: " + this.siteProductBarcodes[next_Clear_ProductNumber_In_Caculator_SlotIndex - 1].ToString() + " with price " + (1883 + next_Clear_ProductNumber_In_Caculator_SlotIndex).ToString()); context.Outgoing.Write( new ProductPerFuellingModeDbRequest_Write_SetPrice(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, targetBarcode.ToString(), newInitPriceWithoutDecimalPoints.ToString(), 0x11) ); this.CurrentStatus = Status.Wait_ACK_Set_Price; } else { next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1; logger.Info("PumpInitializer, node " + ifsfRecipientNode + " set product Price(8 products) in fuel mode 0x11 done!"); //this.CurrentStatus = Status.Sending_Set_Nozzle_To_CacalatorSlot; // now we only have fuelling mode 0x11. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Send FuellingPointDbRequest_Write_SetDefaultFuellingMode,\r\n" + "FP: 0x" + this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString() + " set to fuelling mode 0x11"); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new FuellingPointDbRequest_Write_SetDefaultFuellingMode(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 1)); this.CurrentStatus = Status.Wait_ACK_Set_FP_Default_FuellingMode; } } break; } //case Status.Wait_ACK_Set_Nozzle_To_ProductDb_ProductNoSlot: // { // if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK // && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value) // { // if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived) // logger.Error(" node " + ifsfRecipientNode + ", set nozzle to ProductDb failed:\r\n " + ifsfMsg.ToLogString()); // if (++next_site_nozzle_Index <= this.pumpHandlers.SelectMany(p => p.Nozzles).Count()) // { // logger.Info("PumpInitializer, node " + ifsfRecipientNode + " LogicalNozzleDb_Write_SetNozzleProductAndMeter Acked."); // var next_site_nozzle = this.pumpHandlers.SelectMany(p => p.Nozzles).ElementAt(next_site_nozzle_Index - 1); // var productOnThisNozzleSitsInWhichCaculatorSlot = // thisTqcProductBarcodes.IndexOf(nozzleProductConfig.First(p => p.NozzleLogicalId == next_site_nozzle.LogicalId && p.PumpId == next_site_nozzle.PumpId).ProductBarcode) + 1; // this.pendingForAckMsgToken = this.msgTokenGenerator(); // byte targetIfsfFpId = this.pumpHandlers.First(p => p.PumpId == next_site_nozzle.PumpId).ifsfFuelPointId; // byte targetIfsfNozzleId = ((byte)(0x11 + next_site_nozzle.LogicalId - 1)); // logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending LogicalNozzleDb_Write_SetNozzleProductAndMeter,\r\n" + // "set product to nozzle, FP: 0x" + targetIfsfFpId.ToHexLogString() // + ", nozzle: 0x" + targetIfsfNozzleId.ToHexLogString() // + ", PumpId: " + next_site_nozzle.PumpId + " map to ProductDb ProductNo Slot: " + productOnThisNozzleSitsInWhichCaculatorSlot); // context.Outgoing.Write( // new LogicalNozzleDb_Write_SetNozzleProductAndMeter(this.ifsfRecipientSubnet, // this.ifsfRecipientNode, // this.ifsfSelfSubnet, // this.ifsfSelfNode, // this.pendingForAckMsgToken.Value, // targetIfsfFpId // //assume logical id is 1 based. // , targetIfsfNozzleId, // (byte)productOnThisNozzleSitsInWhichCaculatorSlot, 1) // ); // this.CurrentStatus = Status.Wait_ACK_Set_Nozzle_To_ProductDb_ProductNoSlot; // } // else // { // next_site_nozzle_Index = 1; // logger.Info("PumpInitializer, node " + ifsfRecipientNode + " set fp nozzles to ProductDb ProductNo Slot done!"); // } // } // break; // } case Status.Wait_ACK_Set_FP_Default_FuellingMode: { if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value) { if (++next_Fp_Index <= this.pumpHandlers.Count()) { logger.Info("PumpInitializer, node " + ifsfRecipientNode + " FuellingPointDbRequest_Write_SetDefaultFuellingMode Acked"); // now we only have fuelling mode 0x11. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Send FuellingPointDbRequest_Write_SetDefaultFuellingMode,\r\n" + "FP Default fuelling mode, FP: 0x" + this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString()); this.pendingForAckMsgToken = this.msgTokenGenerator(); context.Outgoing.Write( new FuellingPointDbRequest_Write_SetDefaultFuellingMode(this.ifsfRecipientSubnet, this.ifsfRecipientNode, this.ifsfSelfSubnet, this.ifsfSelfNode, this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 1)); this.CurrentStatus = Status.Wait_ACK_Set_FP_Default_FuellingMode; } else { next_Fp_Index = 1; logger.Info("PumpInitializer, node " + ifsfRecipientNode + " set fp default fuelling mode all done!"); logger.Info("PumpInitializer, node " + ifsfRecipientNode + " DONE!!!"); this.CurrentStatus = Status.Done; var safe = this.OnInitDone; safe?.Invoke(this, null); } } break; } } return true; } public bool IsInitDone => this.CurrentStatus == Status.Done; public void Reset() { this.CurrentStatus = Status.UnInit; this.initTimeoutTimer.Stop(); this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers.Clear(); this.productDb_ProductNo_Answers.Clear(); this.productPerFuellingModeDb_ProductPrice_Answers.Clear(); } } #region IDisposable Support private bool disposedValue = false; // To detect redundant calls protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { // TODO: dispose managed state (managed objects). } // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. // TODO: set large fields to null. disposedValue = true; } } // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. // ~PumpHandler() { // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. // Dispose(false); // } // This code added to correctly implement the disposable pattern. public void Dispose() { // Do not change this code. Put cleanup code in Dispose(bool disposing) above. Dispose(true); // TODO: uncomment the following line if the finalizer is overridden above. // GC.SuppressFinalize(this); } IEnumerator IEnumerable.GetEnumerator() { return this.pumpHandlers.GetEnumerator(); } #endregion } }