LonWorksPumpGroupHandler.cs 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212
  1. using Edge.Core.Database;
  2. using Edge.Core.Processor;
  3. using Edge.Core.IndustryStandardInterface.Pump;
  4. using HengShan_Pump_TQC_IFSF.MessageEntity;
  5. using HengShan_Pump_TQC_IFSF.MessageEntity.Incoming;
  6. using HengShan_Pump_TQC_IFSF.MessageEntity.Outgoing;
  7. using Edge.Core.Parser.BinaryParser.Util;
  8. using System;
  9. using System.Collections;
  10. using System.Collections.Generic;
  11. using System.Globalization;
  12. using System.Linq;
  13. using System.Text;
  14. using System.Threading;
  15. using System.Threading.Tasks;
  16. using System.Xml;
  17. using Wayne.FDCPOSLibrary;
  18. using Timer = System.Timers.Timer;
  19. using Edge.Core.Processor.Dispatcher.Attributes;
  20. using Edge.Core.Processor.Communicator;
  21. using Edge.Core.Configuration;
  22. using Microsoft.Extensions.Logging.Abstractions;
  23. using Microsoft.Extensions.Logging;
  24. using Microsoft.Extensions.DependencyInjection;
  25. namespace HengShan_Pump_TQC_IFSF
  26. {
  27. /// <summary>
  28. /// one TQC connection controls a full physical pump, typically 2 sides.
  29. /// </summary>
  30. [MetaPartsRequired(typeof(GenericDeviceProcessor<,>))]
  31. [MetaPartsRequired(typeof(TcpClientCommunicator<>))]
  32. [MetaPartsRequired(typeof(ComPortCommunicator<>))]
  33. [MetaPartsRequired(typeof(IfsfMessageBase))]
  34. [MetaPartsDescriptor(
  35. "lang-zh-cn:LonWorks HS TQC加油机lang-en-us:LonWorks HS TQC pump",
  36. "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",
  37. new[] { "lang-zh-cn:加油机lang-en-us:Pump" })]
  38. public class LonWorksPumpGroupHandler : IEnumerable<IFdcPumpController>, IDeviceHandler<byte[], IfsfMessageBase>, IDisposable
  39. {
  40. #region Ctor parameters
  41. public class PumpIfsfNodeGroupConfig
  42. {
  43. //public byte AmountDecimalDigits { get; set; }
  44. //public byte VolumeDecimalDigits { get; set; }
  45. //public byte PriceDecimalDigits { get; set; }
  46. //public byte VolumeTotalizerDecimalDigits { get; set; }
  47. public byte FccIfsfSubnetValue { get; set; }
  48. public byte FccIfsfNodeValue { get; set; }
  49. public List<IfsfNodeConfig> IfsfNodeConfigs { get; set; }
  50. }
  51. public class IfsfNodeConfig
  52. {
  53. /// <summary>
  54. /// </summary>
  55. public byte IfsfSubnetValue { get; set; }
  56. public byte IfsfNodeValue { get; set; }
  57. public List<IfsfFpConfig> IfsfFpConfigs { get; set; }
  58. }
  59. public class IfsfFpConfig
  60. {
  61. public byte PumpId { get; set; }
  62. /// <summary>
  63. /// 0x21 - 0x24
  64. /// </summary>
  65. public byte PhysicalId { get; set; }
  66. public List<IfsfNozzleConfig> IfsfNozzleConfigs { get; set; }
  67. }
  68. public class IfsfNozzleConfig
  69. {
  70. public byte LogicalId { get; set; }
  71. /// <summary>
  72. /// 0x11 - 0x18
  73. /// </summary>
  74. public byte PhysicalId { get; set; }
  75. }
  76. #endregion
  77. private IServiceProvider services;
  78. private static ILogger logger = NullLogger.Instance;
  79. private Timer fccHeartbeatSendingTimer;
  80. private Timer pumpIfsfNodeOfflineCheckTimer;
  81. /// <summary>
  82. /// treat pump ifsf node to offline if this time period have not recieve any of its message.
  83. /// </summary>
  84. private int pumpIfsfNodeOfflineTimeThresholdByMs = 25000;
  85. public PumpIfsfNodeGroupConfig deviceConfig;
  86. private object syncObject = new object();
  87. protected IEnumerable<NozzleExtraInfo> nozzleExtraInfos;
  88. private Guid uniqueId = Guid.NewGuid();
  89. internal List<TqcPumpGroupInitializer> tqcPumpGroupInitializers;
  90. //protected static ILog logger = log4net.LogManager.GetLogger("PumpHandler");
  91. protected IContext<byte[], IfsfMessageBase> context;
  92. protected List<LonWorksPumpHandler> pumpHandlers = new List<LonWorksPumpHandler>();
  93. //// for test, the tqc configured as subnet 1, node 1.
  94. //protected byte recipientSubnet = 1;
  95. //protected byte recipientNode = 1;
  96. //// originator, it's the FC self.
  97. public static byte originatorSubnet = 2;
  98. public static byte originatorNode = 1;
  99. public LonWorksPumpGroupHandler(PumpIfsfNodeGroupConfig pumpIfsfNodeGroupConfig, IServiceProvider services)
  100. {
  101. this.services = services;
  102. var loggerFactory = services.GetRequiredService<ILoggerFactory>();
  103. logger = loggerFactory.CreateLogger("PumpHandler");
  104. this.deviceConfig = pumpIfsfNodeGroupConfig;
  105. originatorSubnet = pumpIfsfNodeGroupConfig.FccIfsfSubnetValue;
  106. originatorNode = pumpIfsfNodeGroupConfig.FccIfsfNodeValue;
  107. //this.recipientSubnet = pumpIfsfNodeGroupConfig.IfsfSubnetValue;
  108. //this.recipientNodes.AddRange(pumpIfsfNodeGroupConfig.IfsfNodeConfigs.Select(pc => pc.PumpIfsfNodeValue));
  109. //logger.LogInformation("node " + this.recipientNodes.Select(n => n.ToString()).Aggregate((acc, n) => acc + ", " + n) + ", Will create " + pumpIfsfNodeGroupConfig.IfsfNodeConfigs.Count
  110. // + " pump handlers for this TQC connection according from pumpGroupConfiguration");
  111. if (pumpIfsfNodeGroupConfig.IfsfNodeConfigs.SelectMany(nc => nc.IfsfFpConfigs).Any(fpConfig => fpConfig.PhysicalId < 0x21 || fpConfig.PhysicalId > 0x24))
  112. throw new ArgumentException("Ifsf fuel point id must be range from 0x21 to 0x24");
  113. foreach (var nodeConfig in pumpIfsfNodeGroupConfig.IfsfNodeConfigs)
  114. {
  115. var initor = new TqcPumpGroupInitializer(originatorSubnet, originatorNode,
  116. nodeConfig.IfsfSubnetValue, nodeConfig.IfsfNodeValue, this.pumpHandlers, this.GetNewMessageToken);
  117. initor.OnInitTimeout += (cc, dd) =>
  118. {
  119. logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + ", init timed out, will start it over...");
  120. initor.Reset();
  121. initor.FeedIn(this.context);
  122. };
  123. initor.OnInitDone += (ee, ff) =>
  124. {
  125. logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + ", init done, will query all FP status and routed following msg to PumpHandlers");
  126. // query all FP status, answer will be routed to each PumpHandler to handle.
  127. this.context.Outgoing.Write(
  128. new FuellingPointDbRequest_Read_FuelPointState(nodeConfig.IfsfSubnetValue,
  129. nodeConfig.IfsfNodeValue,
  130. originatorSubnet,
  131. originatorNode,
  132. this.GetNewMessageToken(),
  133. 0x20));
  134. initor.pumpHandlers.SelectMany(p => p.Nozzles).ToList().ForEach(n =>
  135. {
  136. var boundProductNo = this.nozzleExtraInfos.First(c => c.PumpId == n.PumpId && c.NozzleLogicalId == n.LogicalId).ProductBarcode;
  137. logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode
  138. + ", will write back price to nozzle on pump " + n.PumpId + ", logicalNozzleId: " + n.LogicalId + ", boundProductNo: " + boundProductNo
  139. + " to new Init Price(without decimal points): " + initor.PriceBook[boundProductNo]);
  140. n.RealPriceOnPhysicalPump = initor.PriceBook[boundProductNo];
  141. });
  142. };
  143. this.tqcPumpGroupInitializers.Add(initor);
  144. foreach (var fpConfig in nodeConfig.IfsfFpConfigs)
  145. {
  146. var pumpHandler = new LonWorksPumpHandler(initor,
  147. fpConfig.PumpId, fpConfig.PhysicalId,
  148. nodeConfig.IfsfSubnetValue, nodeConfig.IfsfNodeValue,
  149. fpConfig.IfsfNozzleConfigs, GetNewMessageToken);
  150. this.pumpHandlers.Add(pumpHandler);
  151. }
  152. }
  153. }
  154. /// <summary>
  155. /// will be called at the Init stage of FdcServerApp, that means before the calling the Start() for all the Processors.
  156. /// </summary>
  157. /// <param name="parameters"></param>
  158. public void OnFdcServerInit(Dictionary<string, object> parameters)
  159. {
  160. if (parameters != null && parameters.TryGetValue("NozzleProductMapping", out object param))
  161. {
  162. this.nozzleExtraInfos = param as IEnumerable<NozzleExtraInfo>;
  163. }
  164. this.pumpHandlers.ForEach(ph => ph.OnFdcServerInit(parameters));
  165. }
  166. public virtual void Init(IContext<byte[], IfsfMessageBase> context)
  167. {
  168. this.context = context;
  169. bool isCommConnected = false;
  170. this.context.Communicator.OnConnected += async (e, f) =>
  171. {
  172. logger.LogInformation("TqcPumpGroupInitializer, Communicator Connected, will start init each Initializer...");
  173. isCommConnected = true;
  174. foreach (var initor in this.tqcPumpGroupInitializers)
  175. {
  176. while (isCommConnected)
  177. {
  178. initor.Reset();
  179. if (initor.CurrentStatus == TqcPumpGroupInitializer.Status.UnInit)
  180. initor.FeedIn(this.context);
  181. else
  182. logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + " is already in a initing process, just wait for finish...");
  183. // may refine in future for wait the init Done event.
  184. var initResult = await initor.InitDoneTaskCompletionSource.Task;
  185. if (initResult)
  186. {
  187. logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + ", init done, will query all FP status and routed following msg to PumpHandlers");
  188. // query all FP status, answer will be routed to each PumpHandler to handle.
  189. this.context.Outgoing.Write(
  190. new FuellingPointDbRequest_Read_FuelPointState(
  191. initor.ifsfRecipientSubnet,
  192. initor.ifsfRecipientNode,
  193. originatorSubnet,
  194. originatorNode,
  195. this.GetNewMessageToken(),
  196. 0x20));
  197. //var nozzleProductConfig
  198. // = Configurator.Default.NozzleExtraInfoConfiguration.Mapping;
  199. initor.pumpHandlers.SelectMany(p => p.Nozzles).ToList().ForEach(n =>
  200. {
  201. var boundProductNo = this.nozzleExtraInfos.First(c => c.PumpId == n.PumpId && c.NozzleLogicalId == n.LogicalId).ProductBarcode;
  202. logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode
  203. + ", will write back price to nozzle on pump " + n.PumpId + ", logicalNozzleId: " + n.LogicalId + ", boundProductNo: " + boundProductNo
  204. + " to new Init Price(without decimal points): " + initor.PriceBook[boundProductNo]);
  205. n.RealPriceOnPhysicalPump = initor.PriceBook[boundProductNo];
  206. });
  207. break;
  208. }
  209. else
  210. {
  211. logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + ", init timed out, will start it over...");
  212. //initor.Reset();
  213. //initor.FeedIn(this.context);
  214. }
  215. }
  216. }
  217. };
  218. this.pumpHandlers.ForEach(p => p.Init(this.context));
  219. //accuracy is 1000ms
  220. this.pumpIfsfNodeOfflineCheckTimer = new Timer(1000);
  221. this.pumpIfsfNodeOfflineCheckTimer.Elapsed += async (a, b) =>
  222. {
  223. var offlinePumps = this.pumpHandlers.Where(b => DateTime.Now.Subtract(b.LastIncomingMsgReceivedTime ?? DateTime.MinValue).TotalMilliseconds >= pumpIfsfNodeOfflineTimeThresholdByMs);
  224. if (offlinePumps.Any())
  225. {
  226. 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.");
  227. }
  228. foreach (var p in offlinePumps)
  229. {
  230. p.HandleFpStatusChange(FuellingPointStatus.Inoperative, null);
  231. }
  232. };
  233. this.pumpIfsfNodeOfflineCheckTimer.Start();
  234. this.fccHeartbeatSendingTimer = new Timer(10000);
  235. this.fccHeartbeatSendingTimer.Elapsed += async (a, b) =>
  236. {
  237. context.Outgoing.Write(new LonWorksHeartbeat(1, originatorSubnet, originatorNode));
  238. };
  239. this.fccHeartbeatSendingTimer.Start();
  240. }
  241. private byte rotateMsgToken = 0;
  242. public byte GetNewMessageToken()
  243. {
  244. if (rotateMsgToken == (0xFF & 0x01F))
  245. rotateMsgToken = 0;
  246. else
  247. rotateMsgToken++;
  248. return rotateMsgToken;
  249. }
  250. public virtual Task Process(IContext<byte[], IfsfMessageBase> context)
  251. {
  252. this.context = context;
  253. var initor = this.tqcPumpGroupInitializers?.FirstOrDefault(i => i.ifsfRecipientNode == context.Incoming.Message.OriginatorNode);
  254. if (initor == null)
  255. {
  256. // must has initor created first
  257. return Task.CompletedTask;
  258. }
  259. if (!initor.IsInitDone)
  260. {
  261. initor.FeedIn(context);
  262. return Task.CompletedTask;
  263. }
  264. this.RouteMessageToHandlers(context);
  265. return Task.CompletedTask;
  266. }
  267. protected virtual void RouteMessageToHandlers(IContext<byte[], IfsfMessageBase> context)
  268. {
  269. switch (context.Incoming.Message)
  270. {
  271. case FuellingPointDb_FpStatus_Event fpStatusEvent:
  272. this.pumpHandlers.Where(h => h.PumpPhysicalId == fpStatusEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
  273. break;
  274. case FuellingPointDb_FpStatus_Answer fpStatusAnswer:
  275. this.pumpHandlers.Where(h => h.PumpPhysicalId == fpStatusAnswer.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
  276. break;
  277. case FuellingPointDb_FpRunningTransaction_Event fpRunningTrxEvent:
  278. this.pumpHandlers.Where(h => h.PumpPhysicalId == fpRunningTrxEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
  279. break;
  280. case FuellingPointDb_FpRunningTransaction_Event_ACK fpRunningTrxEvent:
  281. this.pumpHandlers.Where(h => h.PumpPhysicalId == fpRunningTrxEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
  282. break;
  283. case FuellingTrxDb_TransactionBufferStatus_Event trxBufferStatusEvent:
  284. this.pumpHandlers.Where(h => h.PumpPhysicalId == trxBufferStatusEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
  285. break;
  286. case FuellingTrxDb_TransactionBufferStatus_Event_ACK trxBufferStatusEvent:
  287. this.pumpHandlers.Where(h => h.PumpPhysicalId == trxBufferStatusEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
  288. break;
  289. case FuellingTrxDb_TransactionBufferStatus_Answer trxBufferStatusAnswer:
  290. this.pumpHandlers.Where(h => h.PumpPhysicalId == trxBufferStatusAnswer.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
  291. break;
  292. //default: this.pumpHandlers.ForEach(p => p.Process(context)); break;
  293. }
  294. }
  295. public IEnumerator<IFdcPumpController> GetEnumerator()
  296. {
  297. return this.pumpHandlers.GetEnumerator();
  298. }
  299. IEnumerator IEnumerable.GetEnumerator()
  300. {
  301. return this.pumpHandlers.GetEnumerator();
  302. }
  303. public Guid Id => this.uniqueId;
  304. /// <summary>
  305. /// Initalizer for init default values into TQC pump.
  306. /// especially for default product price: it will read pre-config values for each product in pump first,
  307. /// </summary>
  308. public class TqcPumpGroupInitializer
  309. {
  310. System.Timers.Timer initTimeoutTimer = new Timer(initTimeout);
  311. const int initTimeout = 10000;
  312. /// <summary>
  313. /// will fire when reached timed out time, and state is still not Done.
  314. /// </summary>
  315. public event EventHandler OnInitTimeout;
  316. /// <summary>
  317. /// false indicates init timed out, true indicates init done successfully.
  318. /// </summary>
  319. public TaskCompletionSource<bool> InitDoneTaskCompletionSource = new TaskCompletionSource<bool>();
  320. public event EventHandler OnInitDone;
  321. public event EventHandler OnInitError;
  322. /// <summary>
  323. /// init process will firstly read all necessary config values in TQC, and then execute the write operations.
  324. /// this event will be fired once those config values were read, and write operation is still not perform.
  325. /// </summary>
  326. public event EventHandler OnTqcExistedConfigRead;
  327. private List<FuellingPointDb_FpStatus_Answer> fpStatus_Answers
  328. = new List<FuellingPointDb_FpStatus_Answer>();
  329. private List<LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer> logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers
  330. = new List<LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer>();
  331. /// <summary>
  332. /// Gets pre-config value in TQC pump.
  333. /// </summary>
  334. public IEnumerable<LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer> LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers => this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers;
  335. private List<ProductDb_ProductNo_Answer> productDb_ProductNo_Answers
  336. = new List<ProductDb_ProductNo_Answer>();
  337. /// <summary>
  338. /// Gets pre-config value in TQC pump.
  339. /// </summary>
  340. public IEnumerable<ProductDb_ProductNo_Answer> ProductDb_ProductNo_Answers => this.productDb_ProductNo_Answers;
  341. private List<ProductPerFuellingModeDb_ProductPrice_Answer> productPerFuellingModeDb_ProductPrice_Answers
  342. = new List<ProductPerFuellingModeDb_ProductPrice_Answer>();
  343. /// <summary>
  344. /// Gets pre-config value in TQC pump.
  345. /// </summary>
  346. public IEnumerable<ProductPerFuellingModeDb_ProductPrice_Answer> ProductPerFuellingModeDb_ProductPrice_Answers => this.productPerFuellingModeDb_ProductPrice_Answers;
  347. private Dictionary<int, int> priceBook = new Dictionary<int, int>();
  348. /// <summary>
  349. /// Gets or set the init price for each product.
  350. /// default price init policy is read from pricebook, if found then apply, otherwise use pre-config value read from pump.
  351. /// should follow-> barcode:rawFormatPriceWithoutDecimalPoints
  352. /// </summary>
  353. public Dictionary<int, int> PriceBook => this.priceBook;
  354. private IEnumerable<NozzleExtraInfo> nozzleProductConfig;
  355. public IEnumerable<NozzleExtraInfo> NozzleProductConfig
  356. {
  357. get
  358. {
  359. return this.nozzleProductConfig;
  360. }
  361. set
  362. {
  363. this.nozzleProductConfig = value;
  364. this.thisTqcProductBarcodes = this.nozzleProductConfig.Where(n => this.pumpHandlers.Select(p => p.PumpId).Contains(n.PumpId)).Select(s => s.ProductBarcode).Distinct().OrderBy(o => o).ToList();
  365. //this.thisTqcProductBarcodes = nozzleProductConfig.Select(s => s.ProductBarcode).Distinct().OrderBy(o => o).ToList();
  366. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " current TQC local configuration indicates total have "
  367. + this.thisTqcProductBarcodes.Count + " product barcodes: " + this.thisTqcProductBarcodes.Select(s => s.ToString()).Aggregate((acc, n) => acc + ", " + n));
  368. }
  369. }
  370. //= Configurator.Default.NozzleProductConfiguration.Mapping;
  371. private byte ifsfSelfSubnet;
  372. private byte ifsfSelfNode;
  373. public byte ifsfRecipientSubnet;
  374. public byte ifsfRecipientNode;
  375. public IEnumerable<LonWorksPumpHandler> pumpHandlers;
  376. Func<byte> msgTokenGenerator;
  377. private List<int> thisTqcProductBarcodes;
  378. private Status currentStatus;
  379. public Status CurrentStatus
  380. {
  381. get { return this.currentStatus; }
  382. set
  383. {
  384. //logger.Debug("PumpInitializer, node " + ifsfRecipientNode + ", Status switched from " + this.currentStatus + " to " + value);
  385. this.currentStatus = value;
  386. }
  387. }
  388. //private DateTime previousRequestingTime;
  389. private byte? pendingForAckMsgToken;
  390. public enum Status
  391. {
  392. UnInit = 0,
  393. Wait_Answer_Query_All_FuelPointState,
  394. Wait_Answer_Close_All_FuelPoint,
  395. Wait_Answer_Query_Caculator_Overall_Info,
  396. Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId,
  397. Wait_Answer_Read_All_ProductNumber,
  398. Wait_Answer_Read_All_ProductPrice,
  399. //Sending_Add_Recipient_Addr,
  400. Wait_ACK_Add_Recipient_Addr,
  401. //Sending_Clear_ProductNumber_In_Caculator,
  402. Wait_ACK_Clear_ProductNumber_In_ProductDb,
  403. //Sending_Set_ProductNumber_In_Caculator,
  404. Wait_ACK_Set_ProductNumber_In_ProductDb,
  405. //Sending_Link_Meter_To_CaculatorSlot,
  406. Wait_ACK_Link_Meter_To_ProductDb_ProductNoSlot,
  407. //Sending_Set_AUTH_MODE,
  408. Wait_ACK_Set_AUTH_MODE,
  409. //Sending_Set_Price,
  410. Wait_ACK_Set_Price,
  411. //Sending_Set_Nozzle_To_CacalatorSlot,
  412. Wait_ACK_Set_Nozzle_To_ProductDb_ProductNoSlot,
  413. //Sending_Set_FP_Default_FuellingMode,
  414. Wait_ACK_Set_FP_Default_FuellingMode,
  415. //Send_Open_FP,
  416. //Wait_ACK_Open_FP,
  417. Done
  418. }
  419. /// <summary>
  420. ///
  421. /// </summary>
  422. /// <param name="selfSubnet">fc ifsf subnet value</param>
  423. /// <param name="selfNode">fc ifsf node value</param>
  424. /// <param name="ifsfRecipientSubnet">remote ifsf recipient subnet value</param>
  425. /// <param name="ifsfRecipientNode">remote ifsf recipient node value</param>
  426. /// <param name="pumpHandlers">pump handlers created from the pump Group handler</param>
  427. /// <param name="msgTokenGenerator"></param>
  428. public TqcPumpGroupInitializer(byte selfSubnet, byte selfNode, byte ifsfRecipientSubnet, byte ifsfRecipientNode,
  429. IEnumerable<LonWorksPumpHandler> pumpHandlers, Func<byte> msgTokenGenerator)
  430. {
  431. this.ifsfSelfSubnet = selfSubnet;
  432. this.ifsfSelfNode = selfNode;
  433. this.ifsfRecipientSubnet = ifsfRecipientSubnet;
  434. this.ifsfRecipientNode = ifsfRecipientNode;
  435. this.pumpHandlers = pumpHandlers;
  436. this.msgTokenGenerator = msgTokenGenerator;
  437. this.initTimeoutTimer.Elapsed += (a, b) =>
  438. {
  439. this.initTimeoutTimer.Stop();
  440. if (!this.IsInitDone)
  441. {
  442. this.InitDoneTaskCompletionSource.SetResult(false);
  443. var safe = this.OnInitTimeout;
  444. safe(this, null);
  445. }
  446. };
  447. }
  448. private byte next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
  449. private byte next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
  450. //private byte next_Link_Meter_to_ProductNumber_Index = 1;
  451. private byte next_site_nozzle_Index = 1;
  452. private byte next_Fp_Index = 1;
  453. /// <summary>
  454. /// if it was Done, return false. otherwise, the init is kicking off, return true.
  455. /// </summary>
  456. /// <param name="context"></param>
  457. /// <returns></returns>
  458. public bool FeedIn(IContext<byte[], IfsfMessageBase> context)
  459. {
  460. if (!this.initTimeoutTimer.Enabled) this.initTimeoutTimer.Start();
  461. if (IsInitDone) return false;
  462. switch (this.CurrentStatus)
  463. {
  464. case Status.UnInit:
  465. {
  466. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " start Init, firstly Query&Close all FuelPoints...");
  467. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Send QueryAllFp state(first time)");
  468. this.pendingForAckMsgToken = this.msgTokenGenerator();
  469. context.Outgoing.Write(
  470. new FuellingPointDbRequest_Read_FuelPointState(this.ifsfRecipientSubnet,
  471. this.ifsfRecipientNode,
  472. this.ifsfSelfSubnet,
  473. this.ifsfSelfNode,
  474. this.pendingForAckMsgToken.Value, 0x20
  475. ));
  476. this.CurrentStatus = Status.Wait_Answer_Query_All_FuelPointState;
  477. break;
  478. }
  479. case Status.Wait_Answer_Query_All_FuelPointState:
  480. {
  481. if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  482. && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
  483. {
  484. var historyIncoming = context.Incoming as HistoryKeepIncoming<IfsfMessageBase>;
  485. this.fpStatus_Answers = historyIncoming.History.Where(h => (h.Item1 is FuellingPointDb_FpStatus_Answer im)
  486. && im.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
  487. && im.MessageToken == this.pendingForAckMsgToken)
  488. .Select(s => (FuellingPointDb_FpStatus_Answer)s.Item1).ToList();
  489. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + ", read total " + fpStatus_Answers.Count
  490. + " Fp status, they're " + this.fpStatus_Answers.Select(s => "Fp 0x" + s.TargetFuelPointId.ToHexLogString() + " is " + s.FuelPointState)
  491. .Aggregate((n, acc) => n + ", " + acc));
  492. this.pendingForAckMsgToken = this.msgTokenGenerator();
  493. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Send CloseFuelPoint(first time) for FP: 0x"
  494. + this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString());
  495. this.pendingForAckMsgToken = this.msgTokenGenerator();
  496. context.Outgoing.Write(
  497. new FuellingPointDbRequest_Write_CloseFuelPoint(this.ifsfRecipientSubnet,
  498. this.ifsfRecipientNode,
  499. this.ifsfSelfSubnet,
  500. this.ifsfSelfNode,
  501. this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId)
  502. ));
  503. this.CurrentStatus = Status.Wait_Answer_Close_All_FuelPoint;
  504. }
  505. break;
  506. }
  507. case Status.Wait_Answer_Close_All_FuelPoint:
  508. {
  509. if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  510. && ifsfMsg.MessageToken == this.pendingForAckMsgToken)
  511. {
  512. if (++next_Fp_Index <= this.pumpHandlers.Count())
  513. {
  514. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " CloseFuelPoint Acked");
  515. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Send CloseFuelPoint for FP: 0x"
  516. + this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString());
  517. this.pendingForAckMsgToken = this.msgTokenGenerator();
  518. context.Outgoing.Write(
  519. new FuellingPointDbRequest_Write_CloseFuelPoint(this.ifsfRecipientSubnet,
  520. this.ifsfRecipientNode,
  521. this.ifsfSelfSubnet,
  522. this.ifsfSelfNode,
  523. this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId)
  524. ));
  525. this.CurrentStatus = Status.Wait_Answer_Close_All_FuelPoint;
  526. }
  527. else
  528. {
  529. this.next_Fp_Index = 1;
  530. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending CaculatorDbRequest_Read_FuelPoint_Product_FuelMode_Meter_Info...");
  531. this.pendingForAckMsgToken = this.msgTokenGenerator();
  532. context.Outgoing.Write(
  533. new CaculatorDbRequest_Read_FuelPoint_Product_FuelMode_Meter_Info(this.ifsfRecipientSubnet,
  534. this.ifsfRecipientNode,
  535. this.ifsfSelfSubnet,
  536. this.ifsfSelfNode,
  537. this.pendingForAckMsgToken.Value
  538. ));
  539. this.CurrentStatus = Status.Wait_Answer_Query_Caculator_Overall_Info;
  540. }
  541. }
  542. break;
  543. }
  544. case Status.Wait_Answer_Query_Caculator_Overall_Info:
  545. {
  546. if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
  547. && ifsfMsg.MessageToken == this.pendingForAckMsgToken)
  548. {
  549. var dataParser = DatabaseDataParser.New().Convert(ifsfMsg.RawDatabaseData.ToArray());
  550. var numberOfProducts = dataParser.DataIds.First(i => i.Item1 == 0x02).Item2.ToInt32();
  551. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " CaculatorDbRequest_Read_FuelPoint_Product_FuelMode_Meter_Info Acked,\r\n" +
  552. "Read caculatorDbOverallInfo, No_Products: " + numberOfProducts
  553. + ", No_Fuelling_Modes: " + dataParser.DataIds.First(i => i.Item1 == 0x03).Item2.ToInt32()
  554. + ", No_Meters: " + dataParser.DataIds.First(i => i.Item1 == 0x04).Item2.ToInt32()
  555. + ", No_FP: " + dataParser.DataIds.First(i => i.Item1 == 0x05).Item2.ToInt32()
  556. + ", Auth_State_Mode: " + dataParser.DataIds.First(i => i.Item1 == 0x0B).Item2.ToInt32());
  557. if (numberOfProducts != this.thisTqcProductBarcodes.Count)
  558. {
  559. logger.LogInformation("!!!!!!PumpInitializer, node " + ifsfRecipientNode + " This TQC MUST config " + numberOfProducts + " products, but now trying to load local config with " + this.thisTqcProductBarcodes.Count + " products");
  560. }
  561. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending CommunicationServiceDbRequest_Write_RecipientAddressTable...");
  562. this.pendingForAckMsgToken = this.msgTokenGenerator();
  563. context.Outgoing.Write(
  564. new CommunicationServiceDbRequest_Write_RecipientAddressTable(this.ifsfRecipientSubnet,
  565. this.ifsfRecipientNode,
  566. this.ifsfSelfSubnet,
  567. this.ifsfSelfNode,
  568. this.pendingForAckMsgToken.Value,
  569. this.ifsfSelfSubnet,
  570. this.ifsfSelfNode));
  571. this.CurrentStatus = Status.Wait_ACK_Add_Recipient_Addr;
  572. }
  573. break;
  574. }
  575. case Status.Wait_ACK_Add_Recipient_Addr:
  576. {
  577. if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  578. && ifsfMsg.MessageToken == this.pendingForAckMsgToken)
  579. {
  580. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " CommunicationServiceDbRequest_Write_RecipientAddressTable Acked.");
  581. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Send LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId for FP: 0x"
  582. + this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString());
  583. this.pendingForAckMsgToken = this.msgTokenGenerator();
  584. context.Outgoing.Write(
  585. new LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId(this.ifsfRecipientSubnet,
  586. this.ifsfRecipientNode,
  587. this.ifsfSelfSubnet,
  588. this.ifsfSelfNode,
  589. this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 0x10));
  590. this.CurrentStatus = Status.Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId;
  591. }
  592. break;
  593. }
  594. case Status.Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId:
  595. {
  596. if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  597. && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
  598. {
  599. var historyIncoming = context.Incoming as HistoryKeepIncoming<IfsfMessageBase>;
  600. this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers.AddRange(
  601. historyIncoming.History.Where(h => (h.Item1 is LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer im)
  602. && im.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
  603. && im.MessageToken == this.pendingForAckMsgToken)
  604. .Select(s => s.Item1 as LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer).OrderBy(a => a.LogicalNozzleId));
  605. if (++next_Fp_Index <= this.pumpHandlers.Count())
  606. {
  607. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId Acked");
  608. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Send LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId for FP: 0x"
  609. + this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString());
  610. this.pendingForAckMsgToken = this.msgTokenGenerator();
  611. context.Outgoing.Write(
  612. new LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId(this.ifsfRecipientSubnet,
  613. this.ifsfRecipientNode,
  614. this.ifsfSelfSubnet,
  615. this.ifsfSelfNode,
  616. this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 0x10));
  617. this.CurrentStatus = Status.Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId;
  618. }
  619. else
  620. {
  621. foreach (var answer in this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers)
  622. logger.LogInformation(" node " + ifsfRecipientNode + " Read nozzles info for FP: 0x" + answer.TargetFuelPointId.ToHexLogString()
  623. + ", with logicalId: 0x" + answer.LogicalNozzleId.ToHexLogString()
  624. + ", with physicalId: 0x" + answer.PhyscialNozzleId.ToHexLogString()
  625. + ", with product slotId: 0x" + answer.LinkedProductSlotId_InProductDbProdcutNoSlot.ToHexLogString());
  626. next_Fp_Index = 1;
  627. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending ProductDbRequest_Read_ProductNo");
  628. this.pendingForAckMsgToken = this.msgTokenGenerator();
  629. // will receive several answers and an ACK messages since this is a query all request.
  630. context.Outgoing.Write(new ProductDbRequest_Read_ProductNo(this.ifsfRecipientSubnet,
  631. this.ifsfRecipientNode,
  632. this.ifsfSelfSubnet,
  633. this.ifsfSelfNode,
  634. this.pendingForAckMsgToken.Value, 0));
  635. this.CurrentStatus = Status.Wait_Answer_Read_All_ProductNumber;
  636. }
  637. }
  638. break;
  639. }
  640. case Status.Wait_Answer_Read_All_ProductNumber:
  641. {
  642. if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  643. && ifsfMsg.MessageToken == this.pendingForAckMsgToken)
  644. {
  645. var historyIncoming = context.Incoming as HistoryKeepIncoming<IfsfMessageBase>;
  646. //logger.Debug("************" + historyIncoming.History.Select(a => a.Item1.GetType().Name).Aggregate((acc, n) => acc + ", " + n));
  647. //logger.Debug("************historyIncoming.History.Count: " + historyIncoming.History.Count);
  648. this.productDb_ProductNo_Answers.AddRange(
  649. historyIncoming.History.Where(h => (h.Item1 is ProductDb_ProductNo_Answer im)
  650. && im.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
  651. && im.MessageToken == this.pendingForAckMsgToken).Select(s => s.Item1 as ProductDb_ProductNo_Answer).OrderBy(a => a.ProductSlotId));
  652. if (this.productDb_ProductNo_Answers.Any())
  653. {
  654. 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));
  655. 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();
  656. this.pendingForAckMsgToken = this.msgTokenGenerator();
  657. // each Product will receive a serial of answers for its differnt FuelMode since we input 0x10 as query all FM.
  658. context.Outgoing.Write(new ProductPerFuellingModeDbRequest_Read_ProductPrice(this.ifsfRecipientSubnet,
  659. this.ifsfRecipientNode,
  660. this.ifsfSelfSubnet,
  661. this.ifsfSelfNode,
  662. this.pendingForAckMsgToken.Value, actualProductNumber, 0x10));
  663. this.CurrentStatus = Status.Wait_Answer_Read_All_ProductPrice;
  664. }
  665. else
  666. {
  667. /* TQC without any product configed, happened for fresh new TQC pump (get hardware reset or just shipped from factory??), handle here */
  668. 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");
  669. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending Clear ProductNumber in ProductDb slot index: " + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index);
  670. this.pendingForAckMsgToken = this.msgTokenGenerator();
  671. context.Outgoing.Write(
  672. new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet,
  673. this.ifsfRecipientNode,
  674. this.ifsfSelfSubnet,
  675. this.ifsfSelfNode,
  676. this.pendingForAckMsgToken.Value,
  677. next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index, null));
  678. this.CurrentStatus = Status.Wait_ACK_Set_ProductNumber_In_ProductDb;
  679. }
  680. }
  681. break;
  682. }
  683. case Status.Wait_Answer_Read_All_ProductPrice:
  684. {
  685. if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  686. && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
  687. {
  688. var historyIncoming = context.Incoming as HistoryKeepIncoming<IfsfMessageBase>;
  689. var resultSetsForOneProduct = historyIncoming.History.Where(h => (h.Item1 is ProductPerFuellingModeDb_ProductPrice_Answer im)
  690. && im.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
  691. && im.MessageToken == this.pendingForAckMsgToken).Select(s => s.Item1 as ProductPerFuellingModeDb_ProductPrice_Answer);
  692. logger.LogInformation(" node " + ifsfRecipientNode + ", Read one existed product price with product number: "
  693. + resultSetsForOneProduct.Last().ProductNumber
  694. + ", price are: " + resultSetsForOneProduct.OrderBy(a => a.FuelModeId).Select(s => s.Price + " in FM:0x" + s.FuelModeId.ToString("X")).Aggregate((acc, n) => acc + ", " + n));
  695. this.productPerFuellingModeDb_ProductPrice_Answers.AddRange(resultSetsForOneProduct);
  696. if (this.productDb_ProductNo_Answers.Count() >= ++next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index)
  697. {
  698. 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();
  699. this.pendingForAckMsgToken = this.msgTokenGenerator();
  700. context.Outgoing.Write(new ProductPerFuellingModeDbRequest_Read_ProductPrice(this.ifsfRecipientSubnet,
  701. this.ifsfRecipientNode,
  702. this.ifsfSelfSubnet,
  703. this.ifsfSelfNode,
  704. this.pendingForAckMsgToken.Value, actualProductNumber, 0x10));
  705. this.CurrentStatus = Status.Wait_Answer_Read_All_ProductPrice;
  706. }
  707. else
  708. {
  709. next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
  710. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Read_All_ProductPrice done!");
  711. var safe = this.OnTqcExistedConfigRead;
  712. safe?.Invoke(this, null);
  713. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending Clear productNo(1st time) in ProductDb with slot index: " + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index);
  714. this.pendingForAckMsgToken = this.msgTokenGenerator();
  715. context.Outgoing.Write(
  716. new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet,
  717. this.ifsfRecipientNode,
  718. this.ifsfSelfSubnet,
  719. this.ifsfSelfNode,
  720. this.pendingForAckMsgToken.Value,
  721. next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index, null));
  722. this.CurrentStatus = Status.Wait_ACK_Clear_ProductNumber_In_ProductDb;
  723. }
  724. }
  725. break;
  726. }
  727. case Status.Wait_ACK_Clear_ProductNumber_In_ProductDb:
  728. {
  729. if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  730. && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
  731. {
  732. if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived)
  733. logger.LogError(" node " + ifsfRecipientNode + ", clear product from ProductDb failed!");
  734. logger.LogInformation(" PumpInitializer, node " + ifsfRecipientNode + ", clear product no. from ProductDb with msgToken:" + this.pendingForAckMsgToken.Value + " Acked.");
  735. if (++next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index <= 8)
  736. {
  737. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending Clear productNo in ProductDb with slot index: " + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index);
  738. this.pendingForAckMsgToken = this.msgTokenGenerator();
  739. context.Outgoing.Write(
  740. new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet,
  741. this.ifsfRecipientNode,
  742. this.ifsfSelfSubnet,
  743. this.ifsfSelfNode,
  744. this.pendingForAckMsgToken.Value,
  745. next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index, null));
  746. this.CurrentStatus = Status.Wait_ACK_Clear_ProductNumber_In_ProductDb;
  747. }
  748. else
  749. {
  750. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " clear product number on all productDb productNo slots(total:" + (next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1) + ") done!");
  751. next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
  752. var next_site_nozzle = this.pumpHandlers.SelectMany(p => p.Nozzles)
  753. .OrderBy(n => n.PumpId).OrderBy(n => n.LogicalId).ElementAt(next_site_nozzle_Index - 1);
  754. var nozzleProductConfig = this.nozzleProductConfig
  755. .First(c => c.PumpId == next_site_nozzle.PumpId && c.NozzleLogicalId == next_site_nozzle.LogicalId);
  756. var ifsfNozzle
  757. = this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers
  758. .First(ifsfNzl => ifsfNzl.TargetFuelPointId == this.pumpHandlers.First(p => p.PumpId == next_site_nozzle.PumpId).PumpPhysicalId
  759. && ifsfNzl.LogicalNozzleId == next_site_nozzle.PhysicalId);
  760. var nozzleBoundToProductDbProductSlot = ifsfNozzle.LinkedProductSlotId_InProductDbProdcutNoSlot;
  761. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode
  762. + " pump " + next_site_nozzle.PumpId + ", nozzle logicalId " + next_site_nozzle.LogicalId
  763. + "(ifsf FpId 0x" + ifsfNozzle.TargetFuelPointId.ToHexLogString() + ", ifsf nozzle logicalId 0x" + ifsfNozzle.LogicalNozzleId.ToHexLogString() + ")"
  764. + " is hardware-bound to productDb product No. slot 0x" + nozzleBoundToProductDbProductSlot.ToHexLogString()
  765. + ", will(1st time) set 0x" + nozzleBoundToProductDbProductSlot.ToHexLogString() + " to product No.: " + nozzleProductConfig.ProductBarcode);
  766. this.pendingForAckMsgToken = this.msgTokenGenerator();
  767. context.Outgoing.Write(
  768. new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet,
  769. this.ifsfRecipientNode,
  770. this.ifsfSelfSubnet,
  771. this.ifsfSelfNode,
  772. this.pendingForAckMsgToken.Value,
  773. (byte)(nozzleBoundToProductDbProductSlot - 0x40), nozzleProductConfig.ProductBarcode.ToString()));
  774. this.CurrentStatus = Status.Wait_ACK_Set_ProductNumber_In_ProductDb;
  775. }
  776. }
  777. break;
  778. }
  779. case Status.Wait_ACK_Set_ProductNumber_In_ProductDb:
  780. {
  781. if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  782. && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
  783. {
  784. if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived)
  785. logger.LogError(" node " + ifsfRecipientNode + ", set product to ProductDb failed!");
  786. logger.LogInformation(" PumpInitializer, node " + ifsfRecipientNode + " ProductDbRequest_Write_SetProductNumber with msgToken:" + this.pendingForAckMsgToken.Value + " Acked.");
  787. if (++next_site_nozzle_Index <= this.pumpHandlers.SelectMany(p => p.Nozzles).Count())
  788. {
  789. var next_site_nozzle = this.pumpHandlers.SelectMany(p => p.Nozzles)
  790. .OrderBy(n => n.PumpId).OrderBy(n => n.LogicalId).ElementAt(next_site_nozzle_Index - 1);
  791. var nozzleProductConfig = this.nozzleProductConfig
  792. .First(c => c.PumpId == next_site_nozzle.PumpId && c.NozzleLogicalId == next_site_nozzle.LogicalId);
  793. var ifsfNozzle
  794. = this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers
  795. .First(ifsfNzl => ifsfNzl.TargetFuelPointId == this.pumpHandlers.First(p => p.PumpId == next_site_nozzle.PumpId).PumpPhysicalId
  796. && ifsfNzl.LogicalNozzleId == next_site_nozzle.PhysicalId);
  797. var nozzleBoundToProductDbProductSlot = ifsfNozzle.LinkedProductSlotId_InProductDbProdcutNoSlot;
  798. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode
  799. + " pump " + next_site_nozzle.PumpId + ", nozzle logicalId " + next_site_nozzle.LogicalId
  800. + "(ifsf FpId 0x" + ifsfNozzle.TargetFuelPointId.ToHexLogString() + ", ifsf nozzle logicalId 0x" + ifsfNozzle.LogicalNozzleId.ToHexLogString() + ")"
  801. + " is hardware-bound to productDb product No. slot 0x" + nozzleBoundToProductDbProductSlot.ToHexLogString()
  802. + ", will set 0x" + nozzleBoundToProductDbProductSlot.ToHexLogString() + " to product No.: " + nozzleProductConfig.ProductBarcode);
  803. this.pendingForAckMsgToken = this.msgTokenGenerator();
  804. context.Outgoing.Write(
  805. new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet,
  806. this.ifsfRecipientNode,
  807. this.ifsfSelfSubnet,
  808. this.ifsfSelfNode,
  809. this.pendingForAckMsgToken.Value,
  810. (byte)(nozzleBoundToProductDbProductSlot - 0x40), nozzleProductConfig.ProductBarcode.ToString()));
  811. this.CurrentStatus = Status.Wait_ACK_Set_ProductNumber_In_ProductDb;
  812. }
  813. else
  814. {
  815. next_site_nozzle_Index = 1;
  816. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol");
  817. this.pendingForAckMsgToken = this.msgTokenGenerator();
  818. context.Outgoing.Write(
  819. new CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol(this.ifsfRecipientSubnet,
  820. this.ifsfRecipientNode,
  821. this.ifsfSelfSubnet,
  822. this.ifsfSelfNode,
  823. this.pendingForAckMsgToken.Value,
  824. CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol.Caculator_Auth_State_Mode.AUTH_State_Allowed));
  825. this.CurrentStatus = Status.Wait_ACK_Set_AUTH_MODE;
  826. }
  827. //if (++next_Clear_ProductNumber_In_Caculator_SlotIndex <= 8)
  828. //this.CurrentStatus = Status.Sending_Link_Meter_To_CaculatorSlot;
  829. //logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending MeterDbRequest_Write_SetMesureToProduct(first), meterId: "
  830. // + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index + ", 2nd arg: " + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index);
  831. //this.pendingForAckMsgToken = this.msgTokenGenerator();
  832. //context.Outgoing.Write(
  833. // new MeterDbRequest_Write_SetMesureToProduct(this.ifsfRecipientSubnet,
  834. // this.ifsfRecipientNode,
  835. // this.ifsfSelfSubnet,
  836. // this.ifsfSelfNode,
  837. // this.pendingForAckMsgToken.Value,
  838. // next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index,
  839. // next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index));
  840. //this.CurrentStatus = Status.Wait_ACK_Link_Meter_To_ProductDb_ProductNoSlot;
  841. }
  842. break;
  843. }
  844. //case Status.Wait_ACK_Link_Meter_To_ProductDb_ProductNoSlot:
  845. // {
  846. // if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  847. // && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
  848. // {
  849. // if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived)
  850. // logger.Error(" node " + ifsfRecipientNode + ", link meter to ProductDb failed!");
  851. // logger.Info(" PumpInitializer, MeterDbRequest_Write_SetMesureToProduct with msgToken:" + this.pendingForAckMsgToken.Value + " Acked.");
  852. // if (this.thisTqcProductBarcodes.Count >= ++next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index)
  853. // {
  854. // //this.CurrentStatus = Status.Sending_Set_ProductNumber_In_Caculator;
  855. // this.pendingForAckMsgToken = this.msgTokenGenerator();
  856. // // put 91 to 98 to caculator 8 slots.
  857. // string operatingProductNumber = this.thisTqcProductBarcodes[next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1].ToString();
  858. // logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending ProductDbRequest_Write_SetProductNumber, ProductDb ProductNoSlot: "
  859. // + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index + ", productNo: " + operatingProductNumber);
  860. // context.Outgoing.Write(
  861. // new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet,
  862. // this.ifsfRecipientNode,
  863. // this.ifsfSelfSubnet,
  864. // this.ifsfSelfNode,
  865. // this.pendingForAckMsgToken.Value,
  866. // next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index, operatingProductNumber));
  867. // this.CurrentStatus = Status.Wait_ACK_Set_ProductNumber_In_ProductDb;
  868. // }
  869. // else
  870. // {
  871. // next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
  872. // logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Link_Meter_To_ProductDb_ProductNoSlot done!");
  873. // //this.CurrentStatus = Status.Sending_Set_AUTH_MODE;
  874. // }
  875. // }
  876. // break;
  877. // }
  878. case Status.Wait_ACK_Set_AUTH_MODE:
  879. {
  880. if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  881. && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
  882. {
  883. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol Acked.");
  884. var targetBarcode = this.thisTqcProductBarcodes[next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1];
  885. int newInitPriceWithoutDecimalPoints;
  886. if (this.PriceBook.ContainsKey(targetBarcode))
  887. newInitPriceWithoutDecimalPoints = this.PriceBook[targetBarcode];
  888. else
  889. {
  890. // try load the existed pre-configed price value which just read from TQC pump.
  891. var preConfigedPriceOnPump = this.productPerFuellingModeDb_ProductPrice_Answers.FirstOrDefault(p => p.FuelModeId == 0x11 && p.ProductNumber == targetBarcode.ToString().PadLeft(8, '0'))?.Price;
  892. if (string.IsNullOrEmpty(preConfigedPriceOnPump))
  893. newInitPriceWithoutDecimalPoints = 1883 + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index;
  894. else
  895. newInitPriceWithoutDecimalPoints = int.Parse(preConfigedPriceOnPump);
  896. this.PriceBook.Add(targetBarcode, newInitPriceWithoutDecimalPoints);
  897. }
  898. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending ProductPerFuellingModeDbRequest_Write_SetPrice, productNo: "
  899. + targetBarcode.ToString()
  900. + ", new price(without decimalPoints): " + newInitPriceWithoutDecimalPoints.ToString());
  901. this.pendingForAckMsgToken = this.msgTokenGenerator();
  902. context.Outgoing.Write(
  903. new ProductPerFuellingModeDbRequest_Write_SetPrice(this.ifsfRecipientSubnet,
  904. this.ifsfRecipientNode,
  905. this.ifsfSelfSubnet,
  906. this.ifsfSelfNode,
  907. this.pendingForAckMsgToken.Value,
  908. targetBarcode.ToString(), newInitPriceWithoutDecimalPoints.ToString()
  909. , 0x11)
  910. );
  911. this.CurrentStatus = Status.Wait_ACK_Set_Price;
  912. }
  913. break;
  914. }
  915. case Status.Wait_ACK_Set_Price:
  916. {
  917. if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  918. && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
  919. {
  920. if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived)
  921. logger.LogError(" node " + ifsfRecipientNode + ", set price for product failed!");
  922. if (this.thisTqcProductBarcodes.Count >= ++next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index)
  923. {
  924. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " ProductPerFuellingModeDbRequest_Write_SetPrice Acked.");
  925. var targetBarcode = this.thisTqcProductBarcodes[next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1];
  926. int newInitPriceWithoutDecimalPoints;
  927. if (this.PriceBook != null && this.PriceBook.ContainsKey(targetBarcode))
  928. newInitPriceWithoutDecimalPoints = this.PriceBook[targetBarcode];
  929. else
  930. {
  931. // try load the existed pre-configed price value which just read from TQC pump.
  932. var preConfigedPriceOnPump = this.productPerFuellingModeDb_ProductPrice_Answers.FirstOrDefault(p => p.FuelModeId == 0x11 && p.ProductNumber == targetBarcode.ToString().PadLeft(8, '0'))?.Price;
  933. if (string.IsNullOrEmpty(preConfigedPriceOnPump))
  934. newInitPriceWithoutDecimalPoints = 1883 + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index;
  935. else
  936. newInitPriceWithoutDecimalPoints = int.Parse(preConfigedPriceOnPump);
  937. this.PriceBook.Add(targetBarcode, newInitPriceWithoutDecimalPoints);
  938. }
  939. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending ProductPerFuellingModeDbRequest_Write_SetPrice, productNo: "
  940. + targetBarcode.ToString()
  941. + ", new price(without decimalPoints): " + newInitPriceWithoutDecimalPoints.ToString());
  942. this.pendingForAckMsgToken = this.msgTokenGenerator();
  943. //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());
  944. context.Outgoing.Write(
  945. new ProductPerFuellingModeDbRequest_Write_SetPrice(this.ifsfRecipientSubnet,
  946. this.ifsfRecipientNode,
  947. this.ifsfSelfSubnet,
  948. this.ifsfSelfNode,
  949. this.pendingForAckMsgToken.Value, targetBarcode.ToString(), newInitPriceWithoutDecimalPoints.ToString(), 0x11)
  950. );
  951. this.CurrentStatus = Status.Wait_ACK_Set_Price;
  952. }
  953. else
  954. {
  955. next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
  956. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " set product Price(8 products) in fuel mode 0x11 done!");
  957. //this.CurrentStatus = Status.Sending_Set_Nozzle_To_CacalatorSlot;
  958. // now we only have fuelling mode 0x11.
  959. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Send FuellingPointDbRequest_Write_SetDefaultFuellingMode,\r\n" +
  960. "FP: 0x" + this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString() + " set to fuelling mode 0x11");
  961. this.pendingForAckMsgToken = this.msgTokenGenerator();
  962. context.Outgoing.Write(
  963. new FuellingPointDbRequest_Write_SetDefaultFuellingMode(this.ifsfRecipientSubnet,
  964. this.ifsfRecipientNode,
  965. this.ifsfSelfSubnet,
  966. this.ifsfSelfNode,
  967. this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 1));
  968. this.CurrentStatus = Status.Wait_ACK_Set_FP_Default_FuellingMode;
  969. }
  970. }
  971. break;
  972. }
  973. //case Status.Wait_ACK_Set_Nozzle_To_ProductDb_ProductNoSlot:
  974. // {
  975. // if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  976. // && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
  977. // {
  978. // if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived)
  979. // logger.Error(" node " + ifsfRecipientNode + ", set nozzle to ProductDb failed:\r\n " + ifsfMsg.ToLogString());
  980. // if (++next_site_nozzle_Index <= this.pumpHandlers.SelectMany(p => p.Nozzles).Count())
  981. // {
  982. // logger.Info("PumpInitializer, node " + ifsfRecipientNode + " LogicalNozzleDb_Write_SetNozzleProductAndMeter Acked.");
  983. // var next_site_nozzle = this.pumpHandlers.SelectMany(p => p.Nozzles).ElementAt(next_site_nozzle_Index - 1);
  984. // var productOnThisNozzleSitsInWhichCaculatorSlot =
  985. // thisTqcProductBarcodes.IndexOf(nozzleProductConfig.First(p => p.NozzleLogicalId == next_site_nozzle.LogicalId && p.PumpId == next_site_nozzle.PumpId).ProductBarcode) + 1;
  986. // this.pendingForAckMsgToken = this.msgTokenGenerator();
  987. // byte targetIfsfFpId = this.pumpHandlers.First(p => p.PumpId == next_site_nozzle.PumpId).ifsfFuelPointId;
  988. // byte targetIfsfNozzleId = ((byte)(0x11 + next_site_nozzle.LogicalId - 1));
  989. // logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending LogicalNozzleDb_Write_SetNozzleProductAndMeter,\r\n" +
  990. // "set product to nozzle, FP: 0x" + targetIfsfFpId.ToHexLogString()
  991. // + ", nozzle: 0x" + targetIfsfNozzleId.ToHexLogString()
  992. // + ", PumpId: " + next_site_nozzle.PumpId + " map to ProductDb ProductNo Slot: " + productOnThisNozzleSitsInWhichCaculatorSlot);
  993. // context.Outgoing.Write(
  994. // new LogicalNozzleDb_Write_SetNozzleProductAndMeter(this.ifsfRecipientSubnet,
  995. // this.ifsfRecipientNode,
  996. // this.ifsfSelfSubnet,
  997. // this.ifsfSelfNode,
  998. // this.pendingForAckMsgToken.Value,
  999. // targetIfsfFpId
  1000. // //assume logical id is 1 based.
  1001. // , targetIfsfNozzleId,
  1002. // (byte)productOnThisNozzleSitsInWhichCaculatorSlot, 1)
  1003. // );
  1004. // this.CurrentStatus = Status.Wait_ACK_Set_Nozzle_To_ProductDb_ProductNoSlot;
  1005. // }
  1006. // else
  1007. // {
  1008. // next_site_nozzle_Index = 1;
  1009. // logger.Info("PumpInitializer, node " + ifsfRecipientNode + " set fp nozzles to ProductDb ProductNo Slot done!");
  1010. // }
  1011. // }
  1012. // break;
  1013. // }
  1014. case Status.Wait_ACK_Set_FP_Default_FuellingMode:
  1015. {
  1016. if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  1017. && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
  1018. {
  1019. if (++next_Fp_Index <= this.pumpHandlers.Count())
  1020. {
  1021. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " FuellingPointDbRequest_Write_SetDefaultFuellingMode Acked");
  1022. // now we only have fuelling mode 0x11.
  1023. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Send FuellingPointDbRequest_Write_SetDefaultFuellingMode,\r\n" +
  1024. "FP Default fuelling mode, FP: 0x" + this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString());
  1025. this.pendingForAckMsgToken = this.msgTokenGenerator();
  1026. context.Outgoing.Write(
  1027. new FuellingPointDbRequest_Write_SetDefaultFuellingMode(this.ifsfRecipientSubnet,
  1028. this.ifsfRecipientNode,
  1029. this.ifsfSelfSubnet,
  1030. this.ifsfSelfNode,
  1031. this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 1));
  1032. this.CurrentStatus = Status.Wait_ACK_Set_FP_Default_FuellingMode;
  1033. }
  1034. else
  1035. {
  1036. next_Fp_Index = 1;
  1037. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " set fp default fuelling mode all done!");
  1038. logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " DONE!!!");
  1039. this.InitDoneTaskCompletionSource.SetResult(true);
  1040. this.CurrentStatus = Status.Done;
  1041. var safe = this.OnInitDone;
  1042. safe?.Invoke(this, null);
  1043. }
  1044. }
  1045. break;
  1046. }
  1047. }
  1048. return true;
  1049. }
  1050. public bool IsInitDone => this.CurrentStatus == Status.Done;
  1051. public void Reset()
  1052. {
  1053. this.CurrentStatus = Status.UnInit;
  1054. this.initTimeoutTimer.Stop();
  1055. this.InitDoneTaskCompletionSource = new TaskCompletionSource<bool>();
  1056. this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers.Clear();
  1057. this.productDb_ProductNo_Answers.Clear();
  1058. this.productPerFuellingModeDb_ProductPrice_Answers.Clear();
  1059. }
  1060. }
  1061. #region IDisposable Support
  1062. private bool disposedValue = false; // To detect redundant calls
  1063. protected virtual void Dispose(bool disposing)
  1064. {
  1065. if (!disposedValue)
  1066. {
  1067. if (disposing)
  1068. {
  1069. // TODO: dispose managed state (managed objects).
  1070. this.pumpIfsfNodeOfflineCheckTimer?.Stop();
  1071. this.fccHeartbeatSendingTimer?.Stop();
  1072. }
  1073. // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
  1074. // TODO: set large fields to null.
  1075. disposedValue = true;
  1076. }
  1077. }
  1078. // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
  1079. // ~PumpHandler() {
  1080. // // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
  1081. // Dispose(false);
  1082. // }
  1083. // This code added to correctly implement the disposable pattern.
  1084. public void Dispose()
  1085. {
  1086. // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
  1087. Dispose(true);
  1088. // TODO: uncomment the following line if the finalizer is overridden above.
  1089. // GC.SuppressFinalize(this);
  1090. }
  1091. IEnumerator<IFdcPumpController> IEnumerable<IFdcPumpController>.GetEnumerator()
  1092. {
  1093. return this.pumpHandlers.GetEnumerator();
  1094. }
  1095. #endregion
  1096. }
  1097. }