DitPumpGroupHandler.cs 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960
  1. using Edge.Core.Configuration;
  2. using Edge.Core.IndustryStandardInterface.Pump;
  3. using Edge.Core.Parser.BinaryParser.Util;
  4. using Edge.Core.Processor;
  5. using Edge.Core.Processor.Communicator;
  6. using Edge.Core.Processor.Dispatcher.Attributes;
  7. using HengShan_Pump_TQC_IFSF.MessageEntity;
  8. using HengShan_Pump_TQC_IFSF.MessageEntity.Incoming;
  9. using HengShan_Pump_TQC_IFSF.MessageEntity.Outgoing;
  10. using System;
  11. using System.Collections;
  12. using System.Collections.Generic;
  13. using System.Globalization;
  14. using System.Linq;
  15. using System.Text;
  16. using System.Threading;
  17. using System.Threading.Tasks;
  18. using System.Xml;
  19. using Wayne.FDCPOSLibrary;
  20. using Timer = System.Timers.Timer;
  21. namespace HengShan_Pump_TQC_IFSF
  22. {
  23. /// <summary>
  24. /// DIT ifsf pump has some different with TQC ifsf.
  25. /// 1. DIT support multiple ifsf node(one fp is a node) in single communicator, thus in a pumpGroup, multiple pumps could have different ifsf node value.
  26. /// 2. DIT pump's fuel product code is specified in a pump side.
  27. /// further more, there're 2 types of DIT, one can change code by web page, another one, looks pretty much the same as
  28. /// TCP IFSF TQC, could not support web page config, then have to do it at the dispenser keyboard, the FCC could not change it,
  29. /// sometimes, though the request and response can go through, actually no change in pump side.
  30. /// thus, need align FCC's product config before run FCC.
  31. /// </summary>
  32. [MetaPartsRequired(typeof(GenericDeviceProcessor<,>))]
  33. [MetaPartsRequired(typeof(HengShan_TQC_IFsfMessageTcpIpCommunicator<>))]
  34. [MetaPartsRequired(typeof(IfsfMessageBase))]
  35. [MetaPartsDescriptor(
  36. "lang-zh-cn:HS DIT TQC加油机lang-en-us:HS DIT TQC pump",
  37. "lang-zh-cn:用于驱动基于TCP连接的IFSF协议的 恒山-DIT 加油机lang-en-us:Used for driven HengShan-DIT dispensers which using IFSF protocol on TCP",
  38. new[] { "lang-zh-cn:加油机lang-en-us:Pump" })]
  39. public class DitPumpGroupHandler : IEnumerable<IFdcPumpController>, IDeviceHandler<byte[], IfsfMessageBase>
  40. {
  41. #region Ctor parameters
  42. public class PumpGroupConfiguration
  43. {
  44. //public byte AmountDecimalDigits { get; set; }
  45. //public byte VolumeDecimalDigits { get; set; }
  46. //public byte PriceDecimalDigits { get; set; }
  47. //public byte VolumeTotalizerDecimalDigits { get; set; }
  48. public byte FccIfsfSubnetValue { get; set; }
  49. public byte FccIfsfNodeValue { get; set; }
  50. /// <summary>
  51. /// all pumps in this group share one SubNet value
  52. /// </summary>
  53. public byte PumpGroupIfsfSubnetValue { get; set; }
  54. public List<PumpConfiguration> PumpConfigurations { get; set; }
  55. }
  56. public class PumpConfiguration
  57. {
  58. public byte PumpIfsfNodeValue { get; set; }
  59. public byte PumpId { get; set; }
  60. /// <summary>
  61. /// 0x21 - 0x24
  62. /// </summary>
  63. public byte PhysicalId { get; set; }
  64. public List<NozzleConfiguration> NozzleConfigurations { get; set; }
  65. }
  66. public class NozzleConfiguration
  67. {
  68. public byte LogicalId { get; set; }
  69. /// <summary>
  70. /// 0x11 - 0x18
  71. /// </summary>
  72. public byte PhysicalId { get; set; }
  73. }
  74. #endregion
  75. private object syncObject = new object();
  76. private Guid uniqueId = Guid.NewGuid();
  77. private IEnumerable<NozzleExtraInfo> nozzleExtraInfos;
  78. /// <summary>
  79. /// Each ifsf node device need a standalone initializer.
  80. /// </summary>
  81. internal IEnumerable<TqcPumpGroupInitializerSimple> tqcPumpGroupInitializers;
  82. //protected static ILog logger = log4net.LogManager.GetLogger("PumpHandler");
  83. protected static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("PumpHandler");
  84. protected IContext<byte[], IfsfMessageBase> context;
  85. protected List<DitPumpHandler> pumpHandlers = new List<DitPumpHandler>();
  86. /// <summary>
  87. /// the pump side ifsf Subnet value.
  88. /// </summary>
  89. protected byte recipientSubnet = 1;
  90. /// <summary>
  91. /// the pump side ifsf Node values under this PumpGroup.
  92. /// </summary>
  93. protected List<byte> recipientNodes = new List<byte>();
  94. /// <summary>
  95. /// ifsf subnet value for FCC.
  96. /// </summary>
  97. public static byte originatorSubnet = 2;
  98. /// <summary>
  99. /// Ifsf node value for FCC.
  100. /// </summary>
  101. public static byte originatorNode = 1;
  102. [ParamsJsonSchemas("DitPumpGroupHandlerCtorParamsJsonSchemas")]
  103. public DitPumpGroupHandler(PumpGroupConfiguration pumpGroupConfiguration, IServiceProvider services)
  104. {
  105. originatorSubnet = pumpGroupConfiguration.FccIfsfSubnetValue;
  106. originatorNode = pumpGroupConfiguration.FccIfsfNodeValue;
  107. this.recipientSubnet = pumpGroupConfiguration.PumpGroupIfsfSubnetValue;
  108. this.recipientNodes.AddRange(pumpGroupConfiguration.PumpConfigurations.Select(pc => pc.PumpIfsfNodeValue));
  109. logger.Info("node " + this.recipientNodes.Select(n => n.ToString()).Aggregate((acc, n) => acc + ", " + n) + ", Will create " + pumpGroupConfiguration.PumpConfigurations.Count
  110. + " pump handlers for this TQC connection according from pumpGroupConfiguration");
  111. this.CreatePumpHandlers(pumpGroupConfiguration);
  112. }
  113. /// <summary>
  114. ///
  115. /// </summary>
  116. /// <param name="pumpsXmlConfiguration"></param>
  117. public DitPumpGroupHandler(int fccIfsfSubnetValue, int fccIfsfNodeValue, string pumpsXmlConfiguration)
  118. {
  119. originatorSubnet = (byte)fccIfsfSubnetValue;
  120. originatorNode = (byte)fccIfsfNodeValue;
  121. // sample, a PumpGroup has a shared(for all pumps) ifsfSubNet value, each pump has an ifsfNode value
  122. //<PumpGroup ifsfSubNet='1'>
  123. // <Pump id='1' physicalId='0x21' ifsfNode='6'>
  124. // <Nozzles>
  125. // <Nozzle logicalId='1' physicalId='0x11' />
  126. // <Nozzle logicalId='2' physicalId='0x12' />
  127. // </Nozzles>
  128. // </Pump>
  129. // <Pump id='2' physicalId='0x22' ifsfNode='5'>
  130. // <Nozzles>
  131. // <Nozzle logicalId='1' physicalId='0x11' />
  132. // <Nozzle logicalId='2' physicalId='0x12' "/>
  133. // </Nozzles>
  134. // </Pump>
  135. // <Pump id='3' physicalId='0x23' ifsfNode='3'>
  136. // <Nozzles>
  137. // <Nozzle logicalId='1' physicalId='0x13' />
  138. // <Nozzle logicalId='2' physicalId='0x14' />
  139. // </Nozzles>
  140. // </Pump>
  141. // <Pump id='4' physicalId='0x24' ifsfNode='2'>
  142. // <Nozzles>
  143. // <Nozzle logicalId='1' physicalId='0x15' />
  144. // <Nozzle logicalId='2' physicalId='0x16' />
  145. // </Nozzles>
  146. // </Pump>
  147. //</PumpGroup>
  148. XmlDocument xmlDocument = new XmlDocument();
  149. xmlDocument.LoadXml(pumpsXmlConfiguration);
  150. var pumpGroupElement = xmlDocument.GetElementsByTagName("PumpGroup").Cast<XmlElement>().First();
  151. this.recipientSubnet = byte.Parse(pumpGroupElement.Attributes["ifsfSubNet"].Value);
  152. var pumpElements = xmlDocument.GetElementsByTagName("Pump");
  153. foreach (var pumpXmlNode in pumpElements.Cast<XmlNode>())
  154. if (pumpXmlNode.Attributes["ifsfNode"] != null)
  155. {
  156. this.recipientNodes.Add(byte.Parse(pumpXmlNode.Attributes["ifsfNode"].Value));
  157. }
  158. else
  159. throw new ArgumentNullException("Must specify attribute: 'ifsfNode' for each pump XmlNode");
  160. logger.Info("node " + this.recipientNodes.Select(n => n.ToString()).Aggregate((acc, n) => acc + ", " + n) + ", Will create " + pumpElements.Count
  161. + " pump handlers for this TQC connection according from local config");
  162. this.CreatePumpHandlers(pumpElements);
  163. }
  164. protected virtual void CreatePumpHandlers(XmlNodeList pumpElements)
  165. {
  166. foreach (XmlNode pumpElement in pumpElements)
  167. {
  168. byte pumpPhysicalIdValue;
  169. var rawPumpPhysicalId = pumpElement.Attributes["physicalId"].Value;//.Substring(2);
  170. if (rawPumpPhysicalId.StartsWith("0x") || rawPumpPhysicalId.StartsWith("0X"))
  171. pumpPhysicalIdValue = byte.Parse(pumpElement.Attributes["physicalId"].Value.Substring(2), NumberStyles.HexNumber);
  172. else
  173. pumpPhysicalIdValue = byte.Parse(pumpElement.Attributes["physicalId"].Value);
  174. if (pumpPhysicalIdValue < 0x21 || pumpPhysicalIdValue > 0x24)
  175. throw new ArgumentException("ifsf fuel point id must be range from 0x21 to 0x24, while the configuration passed in " + rawPumpPhysicalId);
  176. DitPumpHandler pumpHandler = new DitPumpHandler(this,
  177. int.Parse(pumpElement.Attributes["pumpId"].Value),
  178. pumpPhysicalIdValue,
  179. this.recipientSubnet,
  180. byte.Parse(pumpElement.Attributes["ifsfNode"].Value),
  181. pumpElement.InnerXml, GetNewMessageToken);
  182. this.pumpHandlers.Add(pumpHandler);
  183. }
  184. }
  185. protected virtual void CreatePumpHandlers(PumpGroupConfiguration pumpGroupConfiguration)
  186. {
  187. foreach (var pumpConfig in pumpGroupConfiguration.PumpConfigurations)
  188. {
  189. DitPumpHandler pumpHandler = new DitPumpHandler(this,
  190. pumpConfig.PumpId,
  191. pumpConfig.PhysicalId,
  192. this.recipientSubnet,
  193. pumpConfig.PumpIfsfNodeValue,
  194. pumpConfig.NozzleConfigurations,
  195. GetNewMessageToken);
  196. this.pumpHandlers.Add(pumpHandler);
  197. }
  198. }
  199. /// <summary>
  200. /// will be called at the Init stage of FdcServerApp, that means before the calling the Start() for all the Processors.
  201. /// </summary>
  202. /// <param name="parameters"></param>
  203. public void OnFdcServerInit(Dictionary<string, object> parameters)
  204. {
  205. if (parameters != null && parameters.TryGetValue("NozzleProductMapping", out object param))
  206. {
  207. this.nozzleExtraInfos = param as IEnumerable<NozzleExtraInfo>;
  208. }
  209. this.tqcPumpGroupInitializers = this.recipientNodes.Select(rcpNodeValue =>
  210. {
  211. var phs = this.pumpHandlers.Where(ph => ph.recipientNode == rcpNodeValue);
  212. logger.Info("Creating a TqcPumpGroupInitializer for node: " + rcpNodeValue
  213. + ", managed pump Id(s): " + phs.Select(ph => ph.PumpId.ToString()).Aggregate((acc, n) => acc + ", " + n));
  214. var initor = new TqcPumpGroupInitializerSimple(this.nozzleExtraInfos, originatorSubnet, originatorNode,
  215. this.recipientSubnet, rcpNodeValue, phs, this.GetNewMessageToken);
  216. initor.OnInitTimeout += (cc, dd) =>
  217. {
  218. };
  219. initor.OnInitDone += (ee, ff) =>
  220. {
  221. };
  222. //logger.Info(" created a TqcPumpGroupInitializer for node: " + rcpNodeValue);
  223. return initor;
  224. }).ToList();
  225. this.pumpHandlers.ForEach(ph => ph.OnFdcServerInit(parameters));
  226. }
  227. public virtual void Init(IContext<byte[], IfsfMessageBase> context)
  228. {
  229. this.context = context;
  230. bool isCommConnected = false;
  231. this.context.Communicator.OnConnected += async (e, f) =>
  232. {
  233. var comm = context.Communicator as HengShan_TQC_IFsfMessageTcpIpCommunicator<IfsfMessageBase>;
  234. logger.Info("TqcPumpGroupInitializer, Communicator Connected(to pump server: " + (comm?.pumpServerTcpListeningIpAddress ?? "") + "), will start init each Initializer...");
  235. isCommConnected = true;
  236. foreach (var initor in this.tqcPumpGroupInitializers)
  237. {
  238. while (isCommConnected)
  239. {
  240. initor.Reset();
  241. if (initor.CurrentStatus == TqcPumpGroupInitializerSimple.Status.UnInit)
  242. initor.FeedIn(this.context);
  243. else
  244. logger.Info("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + " is already in a initing process, just wait for finish...");
  245. // may refine in future for wait the init Done event.
  246. var initResult = await initor.InitDoneTaskCompletionSource.Task;
  247. if (initResult)
  248. {
  249. logger.Info("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + ", init done, will query all FP status and routed following msg to PumpHandlers");
  250. // query all FP status, answer will be routed to each PumpHandler to handle.
  251. this.context.Outgoing.Write(
  252. new FuellingPointDbRequest_Read_FuelPointState(
  253. this.recipientSubnet,
  254. initor.ifsfRecipientNode,
  255. originatorSubnet,
  256. originatorNode,
  257. this.GetNewMessageToken(),
  258. 0x20));
  259. //var nozzleProductConfig
  260. // = Configurator.Default.NozzleExtraInfoConfiguration.Mapping;
  261. initor.pumpHandlers.SelectMany(p => p.Nozzles).ToList().ForEach(n =>
  262. {
  263. var boundProductNo = this.nozzleExtraInfos.First(c => c.PumpId == n.PumpId && c.NozzleLogicalId == n.LogicalId).ProductBarcode;
  264. logger.Info("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode
  265. + ", will write back price to nozzle on pump " + n.PumpId + ", logicalNozzleId: " + n.LogicalId + ", boundProductNo: " + boundProductNo
  266. + " to new Init Price(without decimal points): " + initor.PriceBook[boundProductNo]);
  267. n.RealPriceOnPhysicalPump = initor.PriceBook[boundProductNo];
  268. });
  269. break;
  270. }
  271. else
  272. {
  273. logger.Info("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + ", init timed out, will start it over...");
  274. //initor.Reset();
  275. //initor.FeedIn(this.context);
  276. }
  277. }
  278. }
  279. };
  280. this.context.Communicator.OnDisconnected += (e, f) =>
  281. {
  282. var comm = context.Communicator as HengShan_TQC_IFsfMessageTcpIpCommunicator<IfsfMessageBase>;
  283. logger.Info("TqcPumpGroupInitializer, Communicator Disconnected(to pump server: " + (comm?.pumpServerTcpListeningIpAddress ?? "") + ")...");
  284. foreach (var initor in this.tqcPumpGroupInitializers)
  285. {
  286. initor.Reset();
  287. isCommConnected = false;
  288. initor.pumpHandlers.ToList().ForEach(p => p.HandleFpStatusChange(FuellingPointStatus.Inoperative, null));
  289. }
  290. };
  291. this.pumpHandlers.ForEach(p => p.Init(this.context));
  292. }
  293. private byte rotateMsgToken = 0;
  294. public byte GetNewMessageToken()
  295. {
  296. if (rotateMsgToken == (0xFF & 0x01F))
  297. rotateMsgToken = 0;
  298. else
  299. rotateMsgToken++;
  300. return rotateMsgToken;
  301. }
  302. public virtual Task Process(IContext<byte[], IfsfMessageBase> context)
  303. {
  304. this.context = context;
  305. var initor = this.tqcPumpGroupInitializers?.FirstOrDefault(i => i.ifsfRecipientNode == context.Incoming.Message.OriginatorNode);
  306. if (initor == null)
  307. {
  308. // must has initor created first
  309. return Task.CompletedTask;
  310. }
  311. if (!initor.IsInitDone)
  312. {
  313. initor.FeedIn(context);
  314. return Task.CompletedTask;
  315. }
  316. this.RouteMessageToHandlers(context);
  317. return Task.CompletedTask;
  318. }
  319. protected virtual void RouteMessageToHandlers(IContext<byte[], IfsfMessageBase> context)
  320. {
  321. switch (context.Incoming.Message)
  322. {
  323. case FuellingPointDb_FpStatus_Event fpStatusEvent:
  324. this.pumpHandlers.Where(h => h.PumpPhysicalId == fpStatusEvent.TargetFuelPointId
  325. && h.recipientNode == fpStatusEvent.OriginatorNode).ToList().ForEach(p => p.Process(context));
  326. break;
  327. case FuellingPointDb_FpStatus_Answer fpStatusAnswer:
  328. this.pumpHandlers.Where(h => h.PumpPhysicalId == fpStatusAnswer.TargetFuelPointId
  329. && h.recipientNode == fpStatusAnswer.OriginatorNode).ToList().ForEach(p => p.Process(context));
  330. break;
  331. case FuellingPointDb_FpRunningTransaction_Event fpRunningTrxEvent:
  332. this.pumpHandlers.Where(h => h.PumpPhysicalId == fpRunningTrxEvent.TargetFuelPointId
  333. && h.recipientNode == fpRunningTrxEvent.OriginatorNode).ToList().ForEach(p => p.Process(context));
  334. break;
  335. case FuellingPointDb_FpRunningTransaction_Event_ACK fpRunningTrxEvent:
  336. this.pumpHandlers.Where(h => h.PumpPhysicalId == fpRunningTrxEvent.TargetFuelPointId
  337. && h.recipientNode == fpRunningTrxEvent.OriginatorNode).ToList().ForEach(p => p.Process(context));
  338. break;
  339. case FuellingTrxDb_TransactionBufferStatus_Event trxBufferStatusEvent:
  340. this.pumpHandlers.Where(h => h.PumpPhysicalId == trxBufferStatusEvent.TargetFuelPointId
  341. && h.recipientNode == trxBufferStatusEvent.OriginatorNode).ToList().ForEach(p => p.Process(context));
  342. break;
  343. case FuellingTrxDb_TransactionBufferStatus_Event_ACK trxBufferStatusEvent:
  344. this.pumpHandlers.Where(h => h.PumpPhysicalId == trxBufferStatusEvent.TargetFuelPointId
  345. && h.recipientNode == trxBufferStatusEvent.OriginatorNode).ToList().ForEach(p => p.Process(context));
  346. break;
  347. case FuellingTrxDb_TransactionBufferStatus_Answer trxBufferStatusAnswer:
  348. this.pumpHandlers.Where(h => h.PumpPhysicalId == trxBufferStatusAnswer.TargetFuelPointId
  349. && h.recipientNode == trxBufferStatusAnswer.OriginatorNode).ToList().ForEach(p => p.Process(context));
  350. break;
  351. //default: this.pumpHandlers.ForEach(p => p.Process(context)); break;
  352. }
  353. }
  354. public IEnumerator<IFdcPumpController> GetEnumerator()
  355. {
  356. return this.pumpHandlers.GetEnumerator();
  357. }
  358. IEnumerator IEnumerable.GetEnumerator()
  359. {
  360. return this.pumpHandlers.GetEnumerator();
  361. }
  362. public Guid Id => this.uniqueId;
  363. internal class TqcPumpGroupInitializerSimple
  364. {
  365. System.Timers.Timer initTimeoutTimer = new Timer(initTimeout);
  366. const int initTimeout = 10000;
  367. /// <summary>
  368. /// will fire when reached timed out time, and state is still not Done.
  369. /// </summary>
  370. public event EventHandler OnInitTimeout;
  371. /// <summary>
  372. /// false indicates init timed out, true indicates init done successfully.
  373. /// </summary>
  374. public TaskCompletionSource<bool> InitDoneTaskCompletionSource = new TaskCompletionSource<bool>();
  375. public event EventHandler OnInitDone;
  376. /// <summary>
  377. /// init process will firstly read all necessary config values in TQC, and then execute the write operations.
  378. /// this event will be fired once those config values were read, and write operation is still not perform.
  379. /// </summary>
  380. public event EventHandler OnTqcExistedConfigRead;
  381. private List<FuellingPointDb_FpStatus_Answer> fpStatus_Answers
  382. = new List<FuellingPointDb_FpStatus_Answer>();
  383. private List<LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer> logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers
  384. = new List<LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer>();
  385. /// <summary>
  386. /// Gets pre-config value in TQC pump.
  387. /// </summary>
  388. public IEnumerable<LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer> LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers => this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers;
  389. private List<ProductDb_ProductNo_Answer> productDb_ProductNo_Answers
  390. = new List<ProductDb_ProductNo_Answer>();
  391. /// <summary>
  392. /// Gets pre-config value in TQC pump.
  393. /// </summary>
  394. public IEnumerable<ProductDb_ProductNo_Answer> ProductDb_ProductNo_Answers => this.productDb_ProductNo_Answers;
  395. private List<ProductPerFuellingModeDb_ProductPrice_Answer> productPerFuellingModeDb_ProductPrice_Answers
  396. = new List<ProductPerFuellingModeDb_ProductPrice_Answer>();
  397. /// <summary>
  398. /// Gets pre-config value in TQC pump.
  399. /// </summary>
  400. public IEnumerable<ProductPerFuellingModeDb_ProductPrice_Answer> ProductPerFuellingModeDb_ProductPrice_Answers => this.productPerFuellingModeDb_ProductPrice_Answers;
  401. private Dictionary<int, int> priceBook = new Dictionary<int, int>();
  402. /// <summary>
  403. /// Gets or set the init price for each product.
  404. /// default price init policy is read from pricebook, if found then apply, otherwise use pre-config value read from pump.
  405. /// should follow-> barcode:rawFormatPriceWithoutDecimalPoints
  406. /// </summary>
  407. public Dictionary<int, int> PriceBook => this.priceBook;
  408. private byte ifsfSelfSubnet;
  409. private byte ifsfSelfNode;
  410. private byte ifsfRecipientSubnet;
  411. public byte ifsfRecipientNode;
  412. public IEnumerable<DitPumpHandler> pumpHandlers;
  413. Func<byte> msgTokenGenerator;
  414. private List<int> thisTqcProductBarcodes;
  415. private Status currentStatus;
  416. public Status CurrentStatus
  417. {
  418. get { return this.currentStatus; }
  419. private set
  420. {
  421. //logger.Debug("PumpInitializer, node " + ifsfRecipientNode + ", Status switched from " + this.currentStatus + " to " + value);
  422. this.currentStatus = value;
  423. }
  424. }
  425. //private DateTime previousRequestingTime;
  426. private byte? pendingForAckMsgToken;
  427. public enum Status
  428. {
  429. UnInit = 0,
  430. Wait_Answer_Query_All_FuelPointState,
  431. Wait_Answer_Close_All_FuelPoint,
  432. Wait_Answer_Query_Caculator_Overall_Info,
  433. Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId,
  434. Wait_Answer_Read_All_ProductNumber,
  435. Wait_Answer_Read_All_ProductPrice,
  436. //Sending_Add_Recipient_Addr,
  437. Wait_ACK_Add_Recipient_Addr,
  438. //Sending_Clear_ProductNumber_In_Caculator,
  439. Wait_ACK_Clear_ProductNumber_In_ProductDb,
  440. //Sending_Set_ProductNumber_In_Caculator,
  441. Wait_ACK_Set_ProductNumber_In_ProductDb,
  442. //Sending_Link_Meter_To_CaculatorSlot,
  443. Wait_ACK_Link_Meter_To_ProductDb_ProductNoSlot,
  444. //Sending_Set_AUTH_MODE,
  445. Wait_ACK_Set_AUTH_MODE,
  446. //Sending_Set_Price,
  447. Wait_ACK_Set_Price,
  448. //Sending_Set_Nozzle_To_CacalatorSlot,
  449. Wait_ACK_Set_Nozzle_To_ProductDb_ProductNoSlot,
  450. //Sending_Set_FP_Default_FuellingMode,
  451. Wait_ACK_Set_FP_Default_FuellingMode,
  452. //Send_Open_FP,
  453. //Wait_ACK_Open_FP,
  454. Done
  455. }
  456. /// <summary>
  457. ///
  458. /// </summary>
  459. /// <param name="selfSubnet">fc ifsf subnet value</param>
  460. /// <param name="selfNode">fc ifsf node value</param>
  461. /// <param name="ifsfRecipientSubnet">remote ifsf recipient subnet value</param>
  462. /// <param name="ifsfRecipientNode">remote ifsf recipient node value</param>
  463. /// <param name="pumpHandlers">pump handlers created from the pump Group handler</param>
  464. /// <param name="msgTokenGenerator"></param>
  465. public TqcPumpGroupInitializerSimple(IEnumerable<NozzleExtraInfo> nozzleExtraInfos, byte selfSubnet, byte selfNode, byte ifsfRecipientSubnet, byte ifsfRecipientNode,
  466. IEnumerable<DitPumpHandler> pumpHandlers, Func<byte> msgTokenGenerator)
  467. {
  468. this.ifsfSelfSubnet = selfSubnet;
  469. this.ifsfSelfNode = selfNode;
  470. this.ifsfRecipientSubnet = ifsfRecipientSubnet;
  471. this.ifsfRecipientNode = ifsfRecipientNode;
  472. this.pumpHandlers = pumpHandlers;
  473. this.msgTokenGenerator = msgTokenGenerator;
  474. this.thisTqcProductBarcodes =
  475. pumpHandlers.SelectMany(ph => ph.Nozzles)
  476. .Select(n => nozzleExtraInfos.FirstOrDefault(npc => npc.PumpId == n.PumpId && npc.NozzleLogicalId == n.LogicalId)?.ProductBarcode)
  477. .Where(n => n != null).Select(n => n.Value).Distinct().OrderBy(o => o).ToList();
  478. //this.thisTqcProductBarcodes = nozzleProductConfig.Select(s => s.ProductBarcode).Distinct().OrderBy(o => o).ToList();
  479. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " current TQC local configuration indicates total have "
  480. + this.thisTqcProductBarcodes.Count + " product barcodes: " + this.thisTqcProductBarcodes.Select(s => s.ToString()).Aggregate((acc, n) => acc + ", " + n));
  481. this.initTimeoutTimer.Elapsed += (a, b) =>
  482. {
  483. this.initTimeoutTimer.Stop();
  484. if (!this.IsInitDone)
  485. {
  486. this.InitDoneTaskCompletionSource.SetResult(false);
  487. var safe = this.OnInitTimeout;
  488. safe(this, null);
  489. }
  490. };
  491. }
  492. private byte next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
  493. private byte next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
  494. //private byte next_Link_Meter_to_ProductNumber_Index = 1;
  495. private byte next_site_nozzle_Index = 1;
  496. private byte next_Fp_Index = 1;
  497. /// <summary>
  498. /// if it was Done, return false. otherwise, the init is kicking off, return true.
  499. /// </summary>
  500. /// <param name="context"></param>
  501. /// <returns></returns>
  502. public bool FeedIn(IContext<byte[], IfsfMessageBase> context)
  503. {
  504. var comm = context.Communicator as HengShan_TQC_IFsfMessageTcpIpCommunicator<IfsfMessageBase>;
  505. if (!this.initTimeoutTimer.Enabled) this.initTimeoutTimer.Start();
  506. if (IsInitDone) return false;
  507. switch (this.CurrentStatus)
  508. {
  509. case Status.UnInit:
  510. {
  511. logger.Info("PumpInitializerSimple, node " + ifsfRecipientNode + " start Init(to pump server: " + (comm?.pumpServerTcpListeningIpAddress ?? "") + "), firstly Query_Caculator_Overall_Info...");
  512. this.next_Fp_Index = 1;
  513. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending CaculatorDbRequest_Read_FuelPoint_Product_FuelMode_Meter_Info...");
  514. this.pendingForAckMsgToken = this.msgTokenGenerator();
  515. context.Outgoing.Write(
  516. new CaculatorDbRequest_Read_FuelPoint_Product_FuelMode_Meter_Info(this.ifsfRecipientSubnet,
  517. this.ifsfRecipientNode,
  518. this.ifsfSelfSubnet,
  519. this.ifsfSelfNode,
  520. this.pendingForAckMsgToken.Value
  521. ));
  522. this.CurrentStatus = Status.Wait_Answer_Query_Caculator_Overall_Info;
  523. break;
  524. }
  525. case Status.Wait_Answer_Query_Caculator_Overall_Info:
  526. {
  527. if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
  528. && ifsfMsg.MessageToken == this.pendingForAckMsgToken)
  529. {
  530. var dataParser = DatabaseDataParser.New().Convert(ifsfMsg.RawDatabaseData.ToArray());
  531. var numberOfProducts = dataParser.DataIds.First(i => i.Item1 == 0x02).Item2.ToInt32();
  532. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " CaculatorDbRequest_Read_FuelPoint_Product_FuelMode_Meter_Info Acked,\r\n" +
  533. "Read caculatorDbOverallInfo, No_Products: " + numberOfProducts
  534. + ", No_Fuelling_Modes: " + dataParser.DataIds.First(i => i.Item1 == 0x03).Item2.ToInt32()
  535. + ", No_Meters: " + (dataParser.DataIds.FirstOrDefault(i => i.Item1 == 0x04)?.Item2?.ToInt32() ?? -1)
  536. + ", No_FP: " + dataParser.DataIds.First(i => i.Item1 == 0x05).Item2.ToInt32()
  537. + ", Auth_State_Mode: " + dataParser.DataIds.First(i => i.Item1 == 0x0B).Item2.ToInt32());
  538. if (numberOfProducts != this.thisTqcProductBarcodes.Count)
  539. {
  540. logger.Info("!!!!!!PumpInitializer, node " + ifsfRecipientNode + " This TQC MUST config " + numberOfProducts + " products, but now trying to load local config with " + this.thisTqcProductBarcodes.Count + " products");
  541. }
  542. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Send LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId for FP: 0x"
  543. + pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString());
  544. this.pendingForAckMsgToken = this.msgTokenGenerator();
  545. context.Outgoing.Write(
  546. new LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId(this.ifsfRecipientSubnet,
  547. this.ifsfRecipientNode,
  548. this.ifsfSelfSubnet,
  549. this.ifsfSelfNode,
  550. this.pendingForAckMsgToken.Value, (byte)(pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 0x10));
  551. this.CurrentStatus = Status.Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId;
  552. }
  553. break;
  554. }
  555. case Status.Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId:
  556. {
  557. if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
  558. && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
  559. {
  560. var historyIncoming = context.Incoming as HistoryKeepIncoming<IfsfMessageBase>;
  561. this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers.AddRange(
  562. historyIncoming.History.Where(h => (h.Item1 is LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer im)
  563. && im.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
  564. && im.MessageToken == this.pendingForAckMsgToken)
  565. .Select(s => s.Item1 as LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer).OrderBy(a => a.LogicalNozzleId));
  566. if (++next_Fp_Index <= pumpHandlers.Count())
  567. {
  568. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId Acked");
  569. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Send LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId for FP: 0x"
  570. + pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString());
  571. this.pendingForAckMsgToken = this.msgTokenGenerator();
  572. context.Outgoing.Write(
  573. new LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId(this.ifsfRecipientSubnet,
  574. this.ifsfRecipientNode,
  575. this.ifsfSelfSubnet,
  576. this.ifsfSelfNode,
  577. this.pendingForAckMsgToken.Value, (byte)(pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 0x10));
  578. this.CurrentStatus = Status.Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId;
  579. }
  580. else
  581. {
  582. foreach (var answer in this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers)
  583. logger.Info(" node " + ifsfRecipientNode + " Read nozzles info for FP: 0x" + answer.TargetFuelPointId.ToHexLogString()
  584. + ", with logicalId: 0x" + answer.LogicalNozzleId.ToHexLogString()
  585. + ", with physicalId: 0x" + answer.PhyscialNozzleId.ToHexLogString()
  586. + ", with product slotId: 0x" + answer.LinkedProductSlotId_InProductDbProdcutNoSlot.ToHexLogString());
  587. next_Fp_Index = 1;
  588. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending ProductDbRequest_Read_ProductNo");
  589. this.pendingForAckMsgToken = this.msgTokenGenerator();
  590. // will receive several answers and an ACK messages since this is a query all request.
  591. context.Outgoing.Write(new ProductDbRequest_Read_ProductNo(this.ifsfRecipientSubnet,
  592. this.ifsfRecipientNode,
  593. this.ifsfSelfSubnet,
  594. this.ifsfSelfNode,
  595. this.pendingForAckMsgToken.Value, 0));
  596. this.CurrentStatus = Status.Wait_Answer_Read_All_ProductNumber;
  597. }
  598. }
  599. break;
  600. }
  601. case Status.Wait_Answer_Read_All_ProductNumber:
  602. {
  603. if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
  604. && ifsfMsg.MessageToken == this.pendingForAckMsgToken)
  605. {
  606. var historyIncoming = context.Incoming as HistoryKeepIncoming<IfsfMessageBase>;
  607. //logger.Debug("************" + historyIncoming.History.Select(a => a.Item1.GetType().Name).Aggregate((acc, n) => acc + ", " + n));
  608. //logger.Debug("************historyIncoming.History.Count: " + historyIncoming.History.Count);
  609. this.productDb_ProductNo_Answers.AddRange(
  610. historyIncoming.History.Where(h => (h.Item1 is ProductDb_ProductNo_Answer im)
  611. && im.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
  612. && im.MessageToken == this.pendingForAckMsgToken).Select(s => s.Item1 as ProductDb_ProductNo_Answer).OrderBy(a => a.ProductSlotId));
  613. if (this.productDb_ProductNo_Answers.Any())
  614. {
  615. logger.Info(" node " + ifsfRecipientNode + " Read all existed product number in a TQC which are: " + this.productDb_ProductNo_Answers.Select(s => s.ProductNumber + "(in 0x" + s.ProductSlotId.ToHexLogString() + ")").Aggregate((acc, n) => acc + ", " + n));
  616. 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();
  617. this.pendingForAckMsgToken = this.msgTokenGenerator();
  618. // each Product will receive a serial of answers for its differnt FuelMode since we input 0x10 as query all FM.
  619. context.Outgoing.Write(new ProductPerFuellingModeDbRequest_Read_ProductPrice(this.ifsfRecipientSubnet,
  620. this.ifsfRecipientNode,
  621. this.ifsfSelfSubnet,
  622. this.ifsfSelfNode,
  623. this.pendingForAckMsgToken.Value, actualProductNumber, 0x11));
  624. this.CurrentStatus = Status.Wait_Answer_Read_All_ProductPrice;
  625. }
  626. else
  627. {
  628. /* TQC without any product configed, happened for fresh new TQC pump (get hardware reset or just shipped from factory??), handle here */
  629. logger.Info(" node " + ifsfRecipientNode + " has no ProductDb_ProductNo_Answer received, seems there're no product configurated in this TQC yet, will directly Clear Set_ProductNumber_In_ProductDb");
  630. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending Clear ProductNumber in ProductDb slot index: " + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index);
  631. this.pendingForAckMsgToken = this.msgTokenGenerator();
  632. context.Outgoing.Write(
  633. new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet,
  634. this.ifsfRecipientNode,
  635. this.ifsfSelfSubnet,
  636. this.ifsfSelfNode,
  637. this.pendingForAckMsgToken.Value,
  638. next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index, null));
  639. this.CurrentStatus = Status.Wait_ACK_Set_ProductNumber_In_ProductDb;
  640. }
  641. }
  642. break;
  643. }
  644. case Status.Wait_Answer_Read_All_ProductPrice:
  645. {
  646. if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
  647. && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
  648. {
  649. var historyIncoming = context.Incoming as HistoryKeepIncoming<IfsfMessageBase>;
  650. var resultSetsForOneProduct = historyIncoming.History.Where(h => (h.Item1 is ProductPerFuellingModeDb_ProductPrice_Answer im)
  651. && im.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
  652. && im.MessageToken == this.pendingForAckMsgToken).Select(s => s.Item1 as ProductPerFuellingModeDb_ProductPrice_Answer);
  653. logger.Info(" node " + ifsfRecipientNode + ", Read one existed product price with product number: "
  654. + resultSetsForOneProduct.Last().ProductNumber
  655. + ", price are: " + resultSetsForOneProduct.OrderBy(a => a.FuelModeId).Select(s => s.Price + " in FM:0x" + s.FuelModeId.ToString("X")).Aggregate((acc, n) => acc + ", " + n));
  656. this.productPerFuellingModeDb_ProductPrice_Answers.AddRange(resultSetsForOneProduct);
  657. if (this.productDb_ProductNo_Answers.Count() >= ++next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index)
  658. {
  659. 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();
  660. this.pendingForAckMsgToken = this.msgTokenGenerator();
  661. context.Outgoing.Write(new ProductPerFuellingModeDbRequest_Read_ProductPrice(this.ifsfRecipientSubnet,
  662. this.ifsfRecipientNode,
  663. this.ifsfSelfSubnet,
  664. this.ifsfSelfNode,
  665. this.pendingForAckMsgToken.Value, actualProductNumber, 0x10));
  666. this.CurrentStatus = Status.Wait_Answer_Read_All_ProductPrice;
  667. }
  668. else
  669. {
  670. next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
  671. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Read_All_ProductPrice done!");
  672. var safe = this.OnTqcExistedConfigRead;
  673. safe?.Invoke(this, null);
  674. next_site_nozzle_Index = 1;
  675. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol");
  676. this.pendingForAckMsgToken = this.msgTokenGenerator();
  677. context.Outgoing.Write(
  678. new CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol(this.ifsfRecipientSubnet,
  679. this.ifsfRecipientNode,
  680. this.ifsfSelfSubnet,
  681. this.ifsfSelfNode,
  682. this.pendingForAckMsgToken.Value,
  683. CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol.Caculator_Auth_State_Mode.AUTH_State_Allowed));
  684. this.CurrentStatus = Status.Wait_ACK_Set_AUTH_MODE;
  685. }
  686. }
  687. break;
  688. }
  689. case Status.Wait_ACK_Set_AUTH_MODE:
  690. {
  691. if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  692. && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
  693. {
  694. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol Acked.");
  695. var targetBarcode = this.thisTqcProductBarcodes[next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1];
  696. int newInitPriceWithoutDecimalPoints;
  697. if (this.PriceBook.ContainsKey(targetBarcode))
  698. newInitPriceWithoutDecimalPoints = this.PriceBook[targetBarcode];
  699. else
  700. {
  701. // try load the existed pre-configed price value which just read from TQC pump.
  702. var preConfigedPriceOnPump = this.productPerFuellingModeDb_ProductPrice_Answers.FirstOrDefault(p => p.FuelModeId == 0x11 && p.ProductNumber == targetBarcode.ToString().PadLeft(8, '0'))?.Price;
  703. if (string.IsNullOrEmpty(preConfigedPriceOnPump))
  704. newInitPriceWithoutDecimalPoints = 1883 + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index;
  705. else
  706. newInitPriceWithoutDecimalPoints = int.Parse(preConfigedPriceOnPump);
  707. this.PriceBook.Add(targetBarcode, newInitPriceWithoutDecimalPoints);
  708. }
  709. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending ProductPerFuellingModeDbRequest_Write_SetPrice, productNo: "
  710. + targetBarcode.ToString()
  711. + ", new price(without decimalPoints): " + newInitPriceWithoutDecimalPoints.ToString());
  712. this.pendingForAckMsgToken = this.msgTokenGenerator();
  713. context.Outgoing.Write(
  714. new ProductPerFuellingModeDbRequest_Write_SetPrice(this.ifsfRecipientSubnet,
  715. this.ifsfRecipientNode,
  716. this.ifsfSelfSubnet,
  717. this.ifsfSelfNode,
  718. this.pendingForAckMsgToken.Value,
  719. targetBarcode.ToString(), newInitPriceWithoutDecimalPoints.ToString()
  720. , 0x11)
  721. );
  722. this.CurrentStatus = Status.Wait_ACK_Set_Price;
  723. }
  724. break;
  725. }
  726. case Status.Wait_ACK_Set_Price:
  727. {
  728. if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  729. && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
  730. {
  731. if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived)
  732. logger.Error(" node " + ifsfRecipientNode + ", set price for product failed!");
  733. if (this.thisTqcProductBarcodes.Count >= ++next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index)
  734. {
  735. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " ProductPerFuellingModeDbRequest_Write_SetPrice Acked.");
  736. var targetBarcode = this.thisTqcProductBarcodes[next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1];
  737. int newInitPriceWithoutDecimalPoints;
  738. if (this.PriceBook != null && this.PriceBook.ContainsKey(targetBarcode))
  739. newInitPriceWithoutDecimalPoints = this.PriceBook[targetBarcode];
  740. else
  741. {
  742. // try load the existed pre-configed price value which just read from TQC pump.
  743. var preConfigedPriceOnPump = this.productPerFuellingModeDb_ProductPrice_Answers.FirstOrDefault(p => p.FuelModeId == 0x11 && p.ProductNumber == targetBarcode.ToString().PadLeft(8, '0'))?.Price;
  744. if (string.IsNullOrEmpty(preConfigedPriceOnPump))
  745. newInitPriceWithoutDecimalPoints = 1883 + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index;
  746. else
  747. newInitPriceWithoutDecimalPoints = int.Parse(preConfigedPriceOnPump);
  748. this.PriceBook.Add(targetBarcode, newInitPriceWithoutDecimalPoints);
  749. }
  750. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending ProductPerFuellingModeDbRequest_Write_SetPrice, productNo: "
  751. + targetBarcode.ToString()
  752. + ", new price(without decimalPoints): " + newInitPriceWithoutDecimalPoints.ToString());
  753. this.pendingForAckMsgToken = this.msgTokenGenerator();
  754. //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());
  755. context.Outgoing.Write(
  756. new ProductPerFuellingModeDbRequest_Write_SetPrice(this.ifsfRecipientSubnet,
  757. this.ifsfRecipientNode,
  758. this.ifsfSelfSubnet,
  759. this.ifsfSelfNode,
  760. this.pendingForAckMsgToken.Value, targetBarcode.ToString(), newInitPriceWithoutDecimalPoints.ToString(), 0x11)
  761. );
  762. this.CurrentStatus = Status.Wait_ACK_Set_Price;
  763. }
  764. else
  765. {
  766. next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
  767. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " set product Price(8 products) in fuel mode 0x11 done!");
  768. //this.CurrentStatus = Status.Sending_Set_Nozzle_To_CacalatorSlot;
  769. // now we only have fuelling mode 0x11.
  770. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Send FuellingPointDbRequest_Write_SetDefaultFuellingMode,\r\n" +
  771. "FP: 0x" + pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString() + " set to fuelling mode 0x11");
  772. this.pendingForAckMsgToken = this.msgTokenGenerator();
  773. context.Outgoing.Write(
  774. new FuellingPointDbRequest_Write_SetDefaultFuellingMode(this.ifsfRecipientSubnet,
  775. this.ifsfRecipientNode,
  776. this.ifsfSelfSubnet,
  777. this.ifsfSelfNode,
  778. this.pendingForAckMsgToken.Value, (byte)(pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 1));
  779. this.CurrentStatus = Status.Wait_ACK_Set_FP_Default_FuellingMode;
  780. }
  781. }
  782. break;
  783. }
  784. case Status.Wait_ACK_Set_FP_Default_FuellingMode:
  785. {
  786. if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
  787. && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
  788. {
  789. if (++next_Fp_Index <= pumpHandlers.Count())
  790. {
  791. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " FuellingPointDbRequest_Write_SetDefaultFuellingMode Acked");
  792. // now we only have fuelling mode 0x11.
  793. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Send FuellingPointDbRequest_Write_SetDefaultFuellingMode,\r\n" +
  794. "FP Default fuelling mode, FP: 0x" + pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString());
  795. this.pendingForAckMsgToken = this.msgTokenGenerator();
  796. context.Outgoing.Write(
  797. new FuellingPointDbRequest_Write_SetDefaultFuellingMode(this.ifsfRecipientSubnet,
  798. this.ifsfRecipientNode,
  799. this.ifsfSelfSubnet,
  800. this.ifsfSelfNode,
  801. this.pendingForAckMsgToken.Value, (byte)(pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 1));
  802. this.CurrentStatus = Status.Wait_ACK_Set_FP_Default_FuellingMode;
  803. }
  804. else
  805. {
  806. next_Fp_Index = 1;
  807. logger.Info("PumpInitializer, node " + ifsfRecipientNode + " set fp default fuelling mode all done!");
  808. logger.Info("PumpInitializer, node " + ifsfRecipientNode + "(pump server ip: " + (comm?.pumpServerTcpListeningIpAddress ?? "") + ") DONE!!!");
  809. this.CurrentStatus = Status.Done;
  810. this.InitDoneTaskCompletionSource.SetResult(true);
  811. var safe = this.OnInitDone;
  812. safe?.Invoke(this, null);
  813. }
  814. }
  815. break;
  816. }
  817. }
  818. return true;
  819. }
  820. public bool IsInitDone => this.CurrentStatus == Status.Done;
  821. public void Reset()
  822. {
  823. this.CurrentStatus = Status.UnInit;
  824. this.initTimeoutTimer.Stop();
  825. this.InitDoneTaskCompletionSource = new TaskCompletionSource<bool>();
  826. this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers.Clear();
  827. this.productDb_ProductNo_Answers.Clear();
  828. this.productPerFuellingModeDb_ProductPrice_Answers.Clear();
  829. }
  830. }
  831. #region IDisposable Support
  832. private bool disposedValue = false; // To detect redundant calls
  833. protected virtual void Dispose(bool disposing)
  834. {
  835. if (!disposedValue)
  836. {
  837. if (disposing)
  838. {
  839. // TODO: dispose managed state (managed objects).
  840. }
  841. // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
  842. // TODO: set large fields to null.
  843. disposedValue = true;
  844. }
  845. }
  846. // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
  847. // ~PumpHandler() {
  848. // // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
  849. // Dispose(false);
  850. // }
  851. // This code added to correctly implement the disposable pattern.
  852. public void Dispose()
  853. {
  854. // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
  855. Dispose(true);
  856. // TODO: uncomment the following line if the finalizer is overridden above.
  857. // GC.SuppressFinalize(this);
  858. }
  859. IEnumerator<IFdcPumpController> IEnumerable<IFdcPumpController>.GetEnumerator()
  860. {
  861. return this.pumpHandlers.GetEnumerator();
  862. }
  863. #endregion
  864. }
  865. }