123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212 |
- 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;
- using Microsoft.Extensions.Logging.Abstractions;
- using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.DependencyInjection;
- namespace HengShan_Pump_TQC_IFSF
- {
- /// <summary>
- /// one TQC connection controls a full physical pump, typically 2 sides.
- /// </summary>
- [MetaPartsRequired(typeof(GenericDeviceProcessor<,>))]
- [MetaPartsRequired(typeof(TcpClientCommunicator<>))]
- [MetaPartsRequired(typeof(ComPortCommunicator<>))]
- [MetaPartsRequired(typeof(IfsfMessageBase))]
- [MetaPartsDescriptor(
- "lang-zh-cn:LonWorks HS TQC加油机lang-en-us:LonWorks HS TQC pump",
- "lang-zh-cn:用于驱动基于 LonWorks Card 连接的IFSF协议的 稳牌-WT30 或 恒山-TH22 加油机lang-en-us:Used for driven Wayne-WT30 or HengShan-TH22 dispensers which using IFSF protocol on LonWorks Card",
- new[] { "lang-zh-cn:加油机lang-en-us:Pump" })]
- public class LonWorksPumpGroupHandler : IEnumerable<IFdcPumpController>, IDeviceHandler<byte[], IfsfMessageBase>, IDisposable
- {
- #region Ctor parameters
- public class PumpIfsfNodeGroupConfig
- {
- //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 List<IfsfNodeConfig> IfsfNodeConfigs { get; set; }
- }
- public class IfsfNodeConfig
- {
- /// <summary>
- /// </summary>
- public byte IfsfSubnetValue { get; set; }
- public byte IfsfNodeValue { get; set; }
- public List<IfsfFpConfig> IfsfFpConfigs { get; set; }
- }
- public class IfsfFpConfig
- {
- public byte PumpId { get; set; }
- /// <summary>
- /// 0x21 - 0x24
- /// </summary>
- public byte PhysicalId { get; set; }
- public List<IfsfNozzleConfig> IfsfNozzleConfigs { get; set; }
- }
- public class IfsfNozzleConfig
- {
- public byte LogicalId { get; set; }
- /// <summary>
- /// 0x11 - 0x18
- /// </summary>
- public byte PhysicalId { get; set; }
- }
- #endregion
- private IServiceProvider services;
- private static ILogger logger = NullLogger.Instance;
- private Timer fccHeartbeatSendingTimer;
- private Timer pumpIfsfNodeOfflineCheckTimer;
- /// <summary>
- /// treat pump ifsf node to offline if this time period have not recieve any of its message.
- /// </summary>
- private int pumpIfsfNodeOfflineTimeThresholdByMs = 25000;
- public PumpIfsfNodeGroupConfig deviceConfig;
- private object syncObject = new object();
- protected IEnumerable<NozzleExtraInfo> nozzleExtraInfos;
- private Guid uniqueId = Guid.NewGuid();
- internal List<TqcPumpGroupInitializer> tqcPumpGroupInitializers;
- //protected static ILog logger = log4net.LogManager.GetLogger("PumpHandler");
- protected IContext<byte[], IfsfMessageBase> context;
- protected List<LonWorksPumpHandler> pumpHandlers = new List<LonWorksPumpHandler>();
- //// 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;
- public LonWorksPumpGroupHandler(PumpIfsfNodeGroupConfig pumpIfsfNodeGroupConfig, IServiceProvider services)
- {
- this.services = services;
- var loggerFactory = services.GetRequiredService<ILoggerFactory>();
- logger = loggerFactory.CreateLogger("PumpHandler");
- this.deviceConfig = pumpIfsfNodeGroupConfig;
- originatorSubnet = pumpIfsfNodeGroupConfig.FccIfsfSubnetValue;
- originatorNode = pumpIfsfNodeGroupConfig.FccIfsfNodeValue;
- //this.recipientSubnet = pumpIfsfNodeGroupConfig.IfsfSubnetValue;
- //this.recipientNodes.AddRange(pumpIfsfNodeGroupConfig.IfsfNodeConfigs.Select(pc => pc.PumpIfsfNodeValue));
- //logger.LogInformation("node " + this.recipientNodes.Select(n => n.ToString()).Aggregate((acc, n) => acc + ", " + n) + ", Will create " + pumpIfsfNodeGroupConfig.IfsfNodeConfigs.Count
- // + " pump handlers for this TQC connection according from pumpGroupConfiguration");
- if (pumpIfsfNodeGroupConfig.IfsfNodeConfigs.SelectMany(nc => nc.IfsfFpConfigs).Any(fpConfig => fpConfig.PhysicalId < 0x21 || fpConfig.PhysicalId > 0x24))
- throw new ArgumentException("Ifsf fuel point id must be range from 0x21 to 0x24");
- foreach (var nodeConfig in pumpIfsfNodeGroupConfig.IfsfNodeConfigs)
- {
- var initor = new TqcPumpGroupInitializer(originatorSubnet, originatorNode,
- nodeConfig.IfsfSubnetValue, nodeConfig.IfsfNodeValue, this.pumpHandlers, this.GetNewMessageToken);
- initor.OnInitTimeout += (cc, dd) =>
- {
- logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + ", init timed out, will start it over...");
- initor.Reset();
- initor.FeedIn(this.context);
- };
- initor.OnInitDone += (ee, ff) =>
- {
- logger.LogInformation("TqcPumpGroupInitializer, node " + initor.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(nodeConfig.IfsfSubnetValue,
- nodeConfig.IfsfNodeValue,
- originatorSubnet,
- originatorNode,
- this.GetNewMessageToken(),
- 0x20));
- initor.pumpHandlers.SelectMany(p => p.Nozzles).ToList().ForEach(n =>
- {
- var boundProductNo = this.nozzleExtraInfos.First(c => c.PumpId == n.PumpId && c.NozzleLogicalId == n.LogicalId).ProductBarcode;
- logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode
- + ", will write back price to nozzle on pump " + n.PumpId + ", logicalNozzleId: " + n.LogicalId + ", boundProductNo: " + boundProductNo
- + " to new Init Price(without decimal points): " + initor.PriceBook[boundProductNo]);
- n.RealPriceOnPhysicalPump = initor.PriceBook[boundProductNo];
- });
- };
- this.tqcPumpGroupInitializers.Add(initor);
- foreach (var fpConfig in nodeConfig.IfsfFpConfigs)
- {
- var pumpHandler = new LonWorksPumpHandler(initor,
- fpConfig.PumpId, fpConfig.PhysicalId,
- nodeConfig.IfsfSubnetValue, nodeConfig.IfsfNodeValue,
- fpConfig.IfsfNozzleConfigs, GetNewMessageToken);
- this.pumpHandlers.Add(pumpHandler);
- }
- }
- }
- /// <summary>
- /// will be called at the Init stage of FdcServerApp, that means before the calling the Start() for all the Processors.
- /// </summary>
- /// <param name="parameters"></param>
- public void OnFdcServerInit(Dictionary<string, object> parameters)
- {
- if (parameters != null && parameters.TryGetValue("NozzleProductMapping", out object param))
- {
- this.nozzleExtraInfos = param as IEnumerable<NozzleExtraInfo>;
- }
- this.pumpHandlers.ForEach(ph => ph.OnFdcServerInit(parameters));
- }
- public virtual void Init(IContext<byte[], IfsfMessageBase> context)
- {
- this.context = context;
- bool isCommConnected = false;
- this.context.Communicator.OnConnected += async (e, f) =>
- {
- logger.LogInformation("TqcPumpGroupInitializer, Communicator Connected, will start init each Initializer...");
- isCommConnected = true;
- foreach (var initor in this.tqcPumpGroupInitializers)
- {
- while (isCommConnected)
- {
- initor.Reset();
- if (initor.CurrentStatus == TqcPumpGroupInitializer.Status.UnInit)
- initor.FeedIn(this.context);
- else
- logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + " is already in a initing process, just wait for finish...");
- // may refine in future for wait the init Done event.
- var initResult = await initor.InitDoneTaskCompletionSource.Task;
- if (initResult)
- {
- logger.LogInformation("TqcPumpGroupInitializer, node " + initor.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(
- initor.ifsfRecipientSubnet,
- initor.ifsfRecipientNode,
- originatorSubnet,
- originatorNode,
- this.GetNewMessageToken(),
- 0x20));
- //var nozzleProductConfig
- // = Configurator.Default.NozzleExtraInfoConfiguration.Mapping;
- initor.pumpHandlers.SelectMany(p => p.Nozzles).ToList().ForEach(n =>
- {
- var boundProductNo = this.nozzleExtraInfos.First(c => c.PumpId == n.PumpId && c.NozzleLogicalId == n.LogicalId).ProductBarcode;
- logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode
- + ", will write back price to nozzle on pump " + n.PumpId + ", logicalNozzleId: " + n.LogicalId + ", boundProductNo: " + boundProductNo
- + " to new Init Price(without decimal points): " + initor.PriceBook[boundProductNo]);
- n.RealPriceOnPhysicalPump = initor.PriceBook[boundProductNo];
- });
- break;
- }
- else
- {
- logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + ", init timed out, will start it over...");
- //initor.Reset();
- //initor.FeedIn(this.context);
- }
- }
- }
- };
- this.pumpHandlers.ForEach(p => p.Init(this.context));
- //accuracy is 1000ms
- this.pumpIfsfNodeOfflineCheckTimer = new Timer(1000);
- this.pumpIfsfNodeOfflineCheckTimer.Elapsed += async (a, b) =>
- {
- var offlinePumps = this.pumpHandlers.Where(b => DateTime.Now.Subtract(b.LastIncomingMsgReceivedTime ?? DateTime.MinValue).TotalMilliseconds >= pumpIfsfNodeOfflineTimeThresholdByMs);
- if (offlinePumps.Any())
- {
- logger.LogError($"Long time no see any msg incoming for {offlinePumps.Select(p => $"Pump {p.PumpId}").Aggregate((acc, n) => acc + ", " + n)}, will trigger them to Fdc_Offline state.");
- }
- foreach (var p in offlinePumps)
- {
- p.HandleFpStatusChange(FuellingPointStatus.Inoperative, null);
- }
- };
- this.pumpIfsfNodeOfflineCheckTimer.Start();
- this.fccHeartbeatSendingTimer = new Timer(10000);
- this.fccHeartbeatSendingTimer.Elapsed += async (a, b) =>
- {
- context.Outgoing.Write(new LonWorksHeartbeat(1, originatorSubnet, originatorNode));
- };
- this.fccHeartbeatSendingTimer.Start();
- }
- private byte rotateMsgToken = 0;
- public byte GetNewMessageToken()
- {
- if (rotateMsgToken == (0xFF & 0x01F))
- rotateMsgToken = 0;
- else
- rotateMsgToken++;
- return rotateMsgToken;
- }
- public virtual Task Process(IContext<byte[], IfsfMessageBase> context)
- {
- this.context = context;
- var initor = this.tqcPumpGroupInitializers?.FirstOrDefault(i => i.ifsfRecipientNode == context.Incoming.Message.OriginatorNode);
- if (initor == null)
- {
- // must has initor created first
- return Task.CompletedTask;
- }
- if (!initor.IsInitDone)
- {
- initor.FeedIn(context);
- return Task.CompletedTask;
- }
- this.RouteMessageToHandlers(context);
- return Task.CompletedTask;
- }
- protected virtual void RouteMessageToHandlers(IContext<byte[], IfsfMessageBase> 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<IFdcPumpController> GetEnumerator()
- {
- return this.pumpHandlers.GetEnumerator();
- }
- IEnumerator IEnumerable.GetEnumerator()
- {
- return this.pumpHandlers.GetEnumerator();
- }
- public Guid Id => this.uniqueId;
- /// <summary>
- /// 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,
- /// </summary>
- public class TqcPumpGroupInitializer
- {
- System.Timers.Timer initTimeoutTimer = new Timer(initTimeout);
- const int initTimeout = 10000;
- /// <summary>
- /// will fire when reached timed out time, and state is still not Done.
- /// </summary>
- public event EventHandler OnInitTimeout;
- /// <summary>
- /// false indicates init timed out, true indicates init done successfully.
- /// </summary>
- public TaskCompletionSource<bool> InitDoneTaskCompletionSource = new TaskCompletionSource<bool>();
- public event EventHandler OnInitDone;
- public event EventHandler OnInitError;
- /// <summary>
- /// 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.
- /// </summary>
- public event EventHandler OnTqcExistedConfigRead;
- private List<FuellingPointDb_FpStatus_Answer> fpStatus_Answers
- = new List<FuellingPointDb_FpStatus_Answer>();
- private List<LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer> logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers
- = new List<LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer>();
- /// <summary>
- /// Gets pre-config value in TQC pump.
- /// </summary>
- public IEnumerable<LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer> LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers => this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers;
- private List<ProductDb_ProductNo_Answer> productDb_ProductNo_Answers
- = new List<ProductDb_ProductNo_Answer>();
- /// <summary>
- /// Gets pre-config value in TQC pump.
- /// </summary>
- public IEnumerable<ProductDb_ProductNo_Answer> ProductDb_ProductNo_Answers => this.productDb_ProductNo_Answers;
- private List<ProductPerFuellingModeDb_ProductPrice_Answer> productPerFuellingModeDb_ProductPrice_Answers
- = new List<ProductPerFuellingModeDb_ProductPrice_Answer>();
- /// <summary>
- /// Gets pre-config value in TQC pump.
- /// </summary>
- public IEnumerable<ProductPerFuellingModeDb_ProductPrice_Answer> ProductPerFuellingModeDb_ProductPrice_Answers => this.productPerFuellingModeDb_ProductPrice_Answers;
- private Dictionary<int, int> priceBook = new Dictionary<int, int>();
- /// <summary>
- /// 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
- /// </summary>
- public Dictionary<int, int> PriceBook => this.priceBook;
- private IEnumerable<NozzleExtraInfo> nozzleProductConfig;
- public IEnumerable<NozzleExtraInfo> 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.LogInformation("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;
- public byte ifsfRecipientSubnet;
- public byte ifsfRecipientNode;
- public IEnumerable<LonWorksPumpHandler> pumpHandlers;
- Func<byte> msgTokenGenerator;
- private List<int> thisTqcProductBarcodes;
- private Status currentStatus;
- public 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;
- public 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
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="selfSubnet">fc ifsf subnet value</param>
- /// <param name="selfNode">fc ifsf node value</param>
- /// <param name="ifsfRecipientSubnet">remote ifsf recipient subnet value</param>
- /// <param name="ifsfRecipientNode">remote ifsf recipient node value</param>
- /// <param name="pumpHandlers">pump handlers created from the pump Group handler</param>
- /// <param name="msgTokenGenerator"></param>
- public TqcPumpGroupInitializer(byte selfSubnet, byte selfNode, byte ifsfRecipientSubnet, byte ifsfRecipientNode,
- IEnumerable<LonWorksPumpHandler> pumpHandlers, Func<byte> 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)
- {
- this.InitDoneTaskCompletionSource.SetResult(false);
- 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;
- /// <summary>
- /// if it was Done, return false. otherwise, the init is kicking off, return true.
- /// </summary>
- /// <param name="context"></param>
- /// <returns></returns>
- public bool FeedIn(IContext<byte[], IfsfMessageBase> context)
- {
- if (!this.initTimeoutTimer.Enabled) this.initTimeoutTimer.Start();
- if (IsInitDone) return false;
- switch (this.CurrentStatus)
- {
- case Status.UnInit:
- {
- logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " start Init, firstly Query&Close all FuelPoints...");
- logger.LogInformation("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<IfsfMessageBase>;
- 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.LogInformation("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.LogInformation("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.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " CloseFuelPoint Acked");
- logger.LogInformation("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.LogInformation("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.LogInformation("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.LogInformation("!!!!!!PumpInitializer, node " + ifsfRecipientNode + " This TQC MUST config " + numberOfProducts + " products, but now trying to load local config with " + this.thisTqcProductBarcodes.Count + " products");
- }
- logger.LogInformation("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.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " CommunicationServiceDbRequest_Write_RecipientAddressTable Acked.");
- logger.LogInformation("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<IfsfMessageBase>;
- 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.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId Acked");
- logger.LogInformation("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.LogInformation(" 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.LogInformation("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<IfsfMessageBase>;
- //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.LogInformation(" 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.LogInformation(" 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.LogInformation("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<IfsfMessageBase>;
- 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.LogInformation(" 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.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Read_All_ProductPrice done!");
- var safe = this.OnTqcExistedConfigRead;
- safe?.Invoke(this, null);
- logger.LogInformation("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.LogError(" node " + ifsfRecipientNode + ", clear product from ProductDb failed!");
- logger.LogInformation(" PumpInitializer, node " + ifsfRecipientNode + ", clear product no. from ProductDb with msgToken:" + this.pendingForAckMsgToken.Value + " Acked.");
- if (++next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index <= 8)
- {
- logger.LogInformation("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.LogInformation("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.LogInformation("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.LogError(" node " + ifsfRecipientNode + ", set product to ProductDb failed!");
- logger.LogInformation(" 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.LogInformation("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.LogInformation("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.LogInformation("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.LogInformation("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.LogError(" node " + ifsfRecipientNode + ", set price for product failed!");
- if (this.thisTqcProductBarcodes.Count >= ++next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index)
- {
- logger.LogInformation("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.LogInformation("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.LogInformation("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.LogInformation("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.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " FuellingPointDbRequest_Write_SetDefaultFuellingMode Acked");
- // now we only have fuelling mode 0x11.
- logger.LogInformation("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.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " set fp default fuelling mode all done!");
- logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " DONE!!!");
- this.InitDoneTaskCompletionSource.SetResult(true);
- 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.InitDoneTaskCompletionSource = new TaskCompletionSource<bool>();
- 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).
- this.pumpIfsfNodeOfflineCheckTimer?.Stop();
- this.fccHeartbeatSendingTimer?.Stop();
- }
- // 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<IFdcPumpController> IEnumerable<IFdcPumpController>.GetEnumerator()
- {
- return this.pumpHandlers.GetEnumerator();
- }
- #endregion
- }
- }
|