HengshanPayTermHandler.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. using Edge.Core.Processor;
  2. using Edge.Core.IndustryStandardInterface.Pump;
  3. using Dfs.WayneChina.HengshanPayTerminal.MessageEntity;
  4. using Dfs.WayneChina.HengshanPayTerminal.MessageEntity.Incoming;
  5. using Dfs.WayneChina.HengshanPayTerminal.Support;
  6. using System;
  7. using System.Collections;
  8. using System.Collections.Concurrent;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11. using System.Threading.Tasks;
  12. using Wayne.FDCPOSLibrary;
  13. using Edge.Core.Processor.Dispatcher.Attributes;
  14. using System.Text.RegularExpressions;
  15. using System.Text.Json;
  16. namespace Dfs.WayneChina.HengshanPayTerminal
  17. {
  18. /// <summary>
  19. /// Handler that communicates directly with the Hengshan Payment Terminal for card handling and pump handling via serial port.
  20. /// </summary>
  21. [MetaPartsDescriptor(
  22. "lang-zh-cn:恒山IC卡终端(UI板) Applang-en-us:Hengshan IC card terminal (UI Board)",
  23. "lang-zh-cn:用于与UI板通讯控制加油机" +
  24. "lang-en-us:Used for terminal communication to control pumps",
  25. new[]
  26. {
  27. "lang-zh-cn:恒山IC卡终端lang-en-us:HengshanICTerminal"
  28. })]
  29. public class HengshanPayTermHandler : IEnumerable<IFdcPumpController>, IDeviceHandler<byte[], CardMessageBase>
  30. {
  31. #region Fields
  32. private string pumpIds;
  33. private string pumpSubAddresses;
  34. private string pumpNozzles;
  35. private string pumpSiteNozzleNos;
  36. private string nozzleLogicIds;
  37. private IContext<byte[], CardMessageBase> _context;
  38. private List<HengshanPumpHandler> pumpHandlers = new List<HengshanPumpHandler>();
  39. public Queue<CardMessageBase> queue = new Queue<CardMessageBase>();
  40. private object syncObj = new object();
  41. private ConcurrentDictionary<int, PumpStateHolder> statusDict = new ConcurrentDictionary<int, PumpStateHolder>();
  42. public ConcurrentDictionary<int, PumpStateHolder> PumpStatusDict => statusDict;
  43. private Dictionary<int, int> pumpIdSubAddressDict;
  44. public Dictionary<int, List<int>> PumpNozzlesDict { get; private set; }
  45. public Dictionary<int, int> NozzleLogicIdDict { get; private set; }
  46. public Dictionary<int, List<int>> PumpSiteNozzleNoDict { get; private set; }
  47. #endregion
  48. #region Logger
  49. private static NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("IPosPlusApp");
  50. #endregion
  51. #region Constructor
  52. //private static List<object> ResolveCtorMetaPartsConfigCompatibility(string incompatibleCtorParamsJsonStr)
  53. //{
  54. // var jsonParams = JsonDocument.Parse(incompatibleCtorParamsJsonStr).RootElement.EnumerateArray().ToArray();
  55. // //sample: "UITemplateVersion":"1.0"
  56. // string uiTemplateVersionRegex = @"(?<=""UITemplateVersion""\:\"").+?(?="")";
  57. // var match = Regex.Match(jsonParams.First().GetRawText(), uiTemplateVersionRegex, RegexOptions.IgnoreCase | RegexOptions.Multiline);
  58. // if (match.Success)
  59. // {
  60. // var curVersion = match.Value;
  61. // if (curVersion == "1.0")
  62. // {
  63. // var existsAppConfigV1 = JsonSerializer.Deserialize(jsonParams.First().GetRawText(), typeof(HengshanPayTerminalHanlderGroupConfigV1));
  64. // }
  65. // else
  66. // {
  67. // }
  68. // }
  69. // return null;
  70. //}
  71. [ParamsJsonSchemas("TermHandlerGroupCtorParamsJsonSchemas")]
  72. public HengshanPayTermHandler(HengshanPayTerminalHanlderGroupConfigV2 config)
  73. : this(config.PumpIds,
  74. string.Join(";", config.PumpSubAddresses.Select(m => $"{m.PumpId}={m.SubAddress}")),
  75. string.Join(";", config.PumpNozzleLogicIds.Select(m => $"{m.PumpId}={m.LogicIds}")),
  76. string.Join(";", config.PumpSiteNozzleNos.Select(m => $"{m.PumpId}={m.SiteNozzleNos}")),
  77. string.Join(";", config.NozzleLogicIds.Select(m => $"{m.NozzleNo}={m.LogicId}")))
  78. {
  79. }
  80. public HengshanPayTermHandler(
  81. string pumpIds,
  82. string pumpSubAddresses,
  83. string pumpNozzles,
  84. string pumpSiteNozzleNos,
  85. string nozzleLogicIds)
  86. {
  87. this.pumpIds = pumpIds;
  88. this.pumpSubAddresses = pumpSubAddresses;
  89. this.pumpNozzles = pumpNozzles;
  90. this.pumpSiteNozzleNos = pumpSiteNozzleNos;
  91. this.nozzleLogicIds = nozzleLogicIds;
  92. AssociatedPumpIds = GetPumpIdList(pumpIds);
  93. pumpIdSubAddressDict = InitializePumpSubAddressMapping();
  94. PumpNozzlesDict = ParsePumpNozzlesList(pumpNozzles);
  95. PumpSiteNozzleNoDict = ParsePumpSiteNozzleNoList(pumpSiteNozzleNos);
  96. NozzleLogicIdDict = InitializeNozzleLogicIdMapping(nozzleLogicIds);
  97. InitializePumpHandlers();
  98. }
  99. #endregion
  100. public void OnFdcServerInit(Dictionary<string, object> parameters)
  101. {
  102. logger.Info("OnFdcServerInit called");
  103. if (parameters.ContainsKey("LastPriceChange"))
  104. {
  105. // nozzle logical id:rawPrice
  106. var lastPriceChanges = parameters["LastPriceChange"] as Dictionary<byte, int>;
  107. foreach (var priceChange in lastPriceChanges)
  108. {
  109. }
  110. }
  111. }
  112. #region Event handler
  113. public event EventHandler<TerminalMessageEventArgs> OnTerminalMessageReceived;
  114. public event EventHandler<TotalizerDataEventArgs> OnTotalizerReceived;
  115. public event EventHandler<FuelPriceChangeRequestEventArgs> OnFuelPriceChangeRequested;
  116. public event EventHandler<FuelPriceDownloadRequestedEventArgs> OnTerminalFuelPriceDownloadRequested;
  117. public event EventHandler<CheckCommandEventArgs> OnCheckCommandReceived;
  118. public event EventHandler<LockUnlockEventArgs> OnLockUnlockCompleted;
  119. #endregion
  120. #region Properties
  121. public List<int> AssociatedPumpIds { get; private set; }
  122. public IContext<byte[], CardMessageBase> Context
  123. {
  124. get { return _context; }
  125. }
  126. public string PumpIdList => pumpIds;
  127. //public LockUnlockOperation LockUnlockOperationType { get; set; } = LockUnlockOperation.Undefined;
  128. #endregion
  129. #region Methods
  130. public int GetSubAddressForPump(int pumpId)
  131. {
  132. return pumpIdSubAddressDict.First(d => d.Key == pumpId).Value;
  133. }
  134. private List<int> GetPumpIdList(string pumpIds)
  135. {
  136. var pumpIdList = new List<int>();
  137. if (!string.IsNullOrEmpty(pumpIds) && pumpIds.Contains(',')) //multiple pumps per serial port, Hengshan TQC pump
  138. {
  139. var arr = pumpIds.Split(',');
  140. foreach (var item in arr)
  141. {
  142. pumpIdList.Add(int.Parse(item));
  143. }
  144. return pumpIdList;
  145. }
  146. else if (!string.IsNullOrEmpty(pumpIds) && pumpIds.Length == 1 || pumpIds.Length == 2) //only 1 pump per serial port, Hengshan pump
  147. {
  148. return new List<int> { int.Parse(pumpIds) };
  149. }
  150. else
  151. {
  152. throw new ArgumentException("Pump id list not specified!");
  153. }
  154. }
  155. private Dictionary<int, int> InitializePumpSubAddressMapping()
  156. {
  157. var dict = new Dictionary<int, int>();
  158. if (!string.IsNullOrEmpty(pumpSubAddresses))
  159. {
  160. var sequence = pumpSubAddresses.Split(';')
  161. .Select(s => s.Split('='))
  162. .Select(a => new { PumpId = int.Parse(a[0]), SubAddress = int.Parse(a[1]) });
  163. foreach (var pair in sequence)
  164. {
  165. if (!dict.ContainsKey(pair.PumpId))
  166. {
  167. dict.Add(pair.PumpId, pair.SubAddress);
  168. }
  169. }
  170. return dict;
  171. }
  172. else
  173. {
  174. throw new ArgumentException("Pump id and sub address mapping does not exist");
  175. }
  176. }
  177. private Dictionary<int, List<int>> ParsePumpNozzlesList(string pumpNozzles)
  178. {
  179. Dictionary<int, List<int>> pumpNozzlesDict = new Dictionary<int, List<int>>();
  180. if (!string.IsNullOrEmpty(pumpNozzles) && pumpNozzles.Contains(';'))
  181. {
  182. var arr = pumpNozzles.Split(';');
  183. foreach (var subMapping in arr)
  184. {
  185. var pair = new KeyValuePair<int, int>(int.Parse(subMapping.Split('=')[0]), int.Parse(subMapping.Split('=')[1]));
  186. Console.WriteLine($"{pair.Key}, {pair.Value}");
  187. if (!pumpNozzlesDict.ContainsKey(pair.Key))
  188. {
  189. pumpNozzlesDict.Add(pair.Key, new List<int> { pair.Value });
  190. }
  191. else
  192. {
  193. List<int> nozzlesForThisPump;
  194. pumpNozzlesDict.TryGetValue(pair.Key, out nozzlesForThisPump);
  195. if (nozzlesForThisPump != null && !nozzlesForThisPump.Contains(pair.Value))
  196. {
  197. nozzlesForThisPump.Add(pair.Value);
  198. }
  199. }
  200. }
  201. }
  202. else if (!string.IsNullOrEmpty(pumpNozzles) && pumpNozzles.Count(c => c == '=') == 1) // only one pump per serial port
  203. {
  204. try
  205. {
  206. pumpNozzlesDict.Add(
  207. int.Parse(pumpNozzles.Split('=')[0]),
  208. new List<int> { int.Parse(pumpNozzles.Split('=')[1]) });
  209. }
  210. catch (Exception ex)
  211. {
  212. Console.WriteLine(ex);
  213. }
  214. }
  215. else
  216. {
  217. throw new ArgumentException("Wrong mapping between pump and its associated nozzles!");
  218. }
  219. return pumpNozzlesDict;
  220. }
  221. static Dictionary<int, List<int>> ParsePumpSiteNozzleNoList(string pumpSiteNozzleNos)
  222. {
  223. Dictionary<int, List<int>> pumpSiteNozzleNoDict = new Dictionary<int, List<int>>();
  224. if (!string.IsNullOrEmpty(pumpSiteNozzleNos) && pumpSiteNozzleNos.Contains(';'))
  225. {
  226. var arr = pumpSiteNozzleNos.Split(';');
  227. foreach (var subMapping in arr)
  228. {
  229. var pair = new KeyValuePair<int, List<int>>(
  230. int.Parse(subMapping.Split('=')[0]), subMapping.Split('=')[1].Split(',').Select(a => int.Parse(a)).ToList());
  231. Console.WriteLine($"{pair.Key}, {pair.Value}");
  232. if (!pumpSiteNozzleNoDict.ContainsKey(pair.Key))
  233. {
  234. pumpSiteNozzleNoDict.Add(pair.Key, pair.Value);
  235. }
  236. }
  237. }
  238. else if (!string.IsNullOrEmpty(pumpSiteNozzleNos) && pumpSiteNozzleNos.Count(c => c == '=') == 1)
  239. {
  240. try
  241. {
  242. string[] strArr = pumpSiteNozzleNos.Split('=');
  243. pumpSiteNozzleNoDict.Add(
  244. int.Parse(strArr[0]), new List<int> { int.Parse(strArr[1]) });
  245. }
  246. catch (Exception ex)
  247. {
  248. Console.WriteLine(ex);
  249. }
  250. }
  251. else
  252. {
  253. throw new ArgumentException("Wrong mapping between pump and its associated nozzles!");
  254. }
  255. return pumpSiteNozzleNoDict;
  256. }
  257. private Dictionary<int, int> InitializeNozzleLogicIdMapping(string nozzleLogicIds)
  258. {
  259. var dict = new Dictionary<int, int>();
  260. if (!string.IsNullOrEmpty(nozzleLogicIds))
  261. {
  262. var sequence = nozzleLogicIds.Split(';')
  263. .Select(s => s.Split('='))
  264. .Select(a => new { NozzleNo = int.Parse(a[0]), LogicId = int.Parse(a[1]) });
  265. foreach (var pair in sequence)
  266. {
  267. if (!dict.ContainsKey(pair.NozzleNo))
  268. {
  269. Console.WriteLine($"nozzle, logic id: {pair.NozzleNo} - {pair.LogicId}");
  270. dict.Add(pair.NozzleNo, pair.LogicId);
  271. }
  272. }
  273. return dict;
  274. }
  275. else if (!string.IsNullOrEmpty(nozzleLogicIds) && nozzleLogicIds.Count(c => c == '=') == 1)
  276. {
  277. try
  278. {
  279. string[] sequence = nozzleLogicIds.Split('=');
  280. dict.Add(int.Parse(sequence[0]), int.Parse(sequence[1]));
  281. }
  282. catch (Exception ex)
  283. {
  284. Console.WriteLine(ex);
  285. }
  286. return dict;
  287. }
  288. else
  289. {
  290. throw new ArgumentException("Pump id and sub address mapping does not exist");
  291. }
  292. }
  293. private void InitializePumpHandlers()
  294. {
  295. var pumpIdList = GetPumpIdList(pumpIds);
  296. foreach (var item in pumpIdList)
  297. {
  298. var nozzleList = GetNozzleListForPump(item);
  299. var siteNozzleNoList = PumpSiteNozzleNoDict[item];
  300. HengshanPumpHandler pumpHandler = new HengshanPumpHandler(this, $"Pump_{item}", item, nozzleList, siteNozzleNoList);
  301. pumpHandler.OnFuelPriceChangeRequested += PumpHandler_OnFuelPriceChangeRequested;
  302. pumpHandlers.Add(pumpHandler);
  303. }
  304. }
  305. private List<int> GetNozzleListForPump(int pumpId)
  306. {
  307. List<int> nozzles;
  308. PumpNozzlesDict.TryGetValue(pumpId, out nozzles);
  309. return nozzles;
  310. }
  311. private void PumpHandler_OnFuelPriceChangeRequested(object sender, FuelPriceChangeRequestEventArgs e)
  312. {
  313. InfoLog($"Change price, Pump {e.PumpId}, Nozzle {e.NozzleId}, Price {e.Price}");
  314. OnFuelPriceChangeRequested?.Invoke(sender, e);
  315. }
  316. IEnumerator<IFdcPumpController> IEnumerable<IFdcPumpController>.GetEnumerator()
  317. {
  318. return pumpHandlers.GetEnumerator();
  319. }
  320. #endregion
  321. #region IHandler implementation
  322. public void Init(IContext<byte[], CardMessageBase> context)
  323. {
  324. CommIdentity = context.Processor.Communicator.Identity;
  325. _context = context;
  326. }
  327. public string CommIdentity { get; private set; }
  328. public async Task Process(IContext<byte[], CardMessageBase> context)
  329. {
  330. if (context.Incoming.Message != null && context.Incoming.Message.Handle == (byte)Command.VolumeTotalizerResult)
  331. {
  332. var volumeTotalResult = context.Incoming.Message as VolumeTotal;
  333. if (volumeTotalResult != null)
  334. {
  335. byte nozzleNo = volumeTotalResult.VolumeTotalizers.First().NozzleNo;
  336. var volumeTotal = volumeTotalResult.VolumeTotalizers.
  337. First(n => AssociatedPumpIds.Contains(n.NozzleNo)).VolumeTotal.ToUInt32();
  338. var result = new Tuple<int, int>(-1, Convert.ToInt32(volumeTotal));
  339. OnTotalizerReceived?.Invoke(this, new TotalizerDataEventArgs(nozzleNo, result));
  340. }
  341. }
  342. else if (context.Incoming.Message != null && context.Incoming.Message.Handle == (byte)Command.ReturnTotalizer)
  343. {
  344. var totalizerResult = context.Incoming.Message as Totalizer;
  345. if (totalizerResult != null)
  346. {
  347. foreach (var item in totalizerResult.TotalizerData)
  348. {
  349. //var nozzleNo = totalizerResult.TotalizerData.First().NozzleNo;
  350. var nozzleNo = item.NozzleNo;
  351. //var volumeTotal = totalizerResult.TotalizerData.First(n => AssociatedPumpIds.Contains(n.NozzleNo)).VolumeTotal.ToUInt32();
  352. //var amountTotal = totalizerResult.TotalizerData.First(n => AssociatedPumpIds.Contains(n.NozzleNo)).AmountTotal.ToUInt32();
  353. var volumeTotal = item.VolumeTotal.ToUInt32();
  354. var amountTotal = item.AmountTotal.ToUInt32();
  355. var result = new Tuple<int, int>(Convert.ToInt32(amountTotal), Convert.ToInt32(volumeTotal));
  356. OnTotalizerReceived?.Invoke(this, new TotalizerDataEventArgs(nozzleNo, result));
  357. }
  358. }
  359. }
  360. else if (context.Incoming.Message != null && context.Incoming.Message.Handle == (byte)Command.LockOrUnlockPumpAck)
  361. {
  362. var lockUnlockResult = context.Incoming.Message as LockOrUnlockPumpAck;
  363. if (lockUnlockResult != null)
  364. {
  365. //PumpStateHolder lastState = null;
  366. //statusDict.TryGetValue(lockUnlockResult.FPCode.PumpNo, out lastState);
  367. //if (LockUnlockOperationType == LockUnlockOperation.Lock)
  368. //{
  369. // OnLockUnlockCompleted?.Invoke(this,
  370. // new LockUnlockEventArgs(LockUnlockOperationType, lockUnlockResult.DispenserState == DispenserState.Closed));
  371. //}
  372. //else if (LockUnlockOperationType == LockUnlockOperation.Unlock)
  373. //{
  374. // OnLockUnlockCompleted?.Invoke(this,
  375. // new LockUnlockEventArgs(LockUnlockOperationType, lockUnlockResult.DispenserState == DispenserState.Idle));
  376. //}
  377. }
  378. }
  379. else
  380. {
  381. if (context.Incoming.Message != null && context.Incoming.Message.Handle == (byte)Command.DataRequest)
  382. {
  383. var request = context.Incoming.Message as DataDownloadRequest;
  384. if (request != null && request.DataContentType == DataContentType.FuelPriceList)
  385. {
  386. InfoLog($"Fuel Price length download request from terminal address: {request.SourceAddress}");
  387. OnTerminalFuelPriceDownloadRequested?.Invoke(this, new FuelPriceDownloadRequestedEventArgs(true));
  388. }
  389. }
  390. else if (context.Incoming.Message != null && context.Incoming.Message.Handle == (byte)Command.Check_cmd)
  391. {
  392. CheckStatus((CheckCmdRequest)context.Incoming.Message);
  393. OnCheckCommandReceived?.Invoke(this, new CheckCommandEventArgs((CheckCmdRequest)context.Incoming.Message));
  394. }
  395. OnTerminalMessageReceived?.Invoke(this, new TerminalMessageEventArgs(pumpIds, context.Incoming.Message));
  396. }
  397. await Task.CompletedTask;
  398. }
  399. private void CheckStatus(CheckCmdRequest request)
  400. {
  401. if(!statusDict.ContainsKey(request.FuelingPoint.PumpNo))
  402. {
  403. var result = statusDict.TryAdd(request.FuelingPoint.PumpNo,
  404. new PumpStateHolder
  405. {
  406. PumpNo = request.FuelingPoint.PumpNo,
  407. NozzleNo = 1,
  408. State = request,
  409. OperationType = LockUnlockOperation.None
  410. });
  411. logger.Info($"Adding FuelingPoint {request.FuelingPoint.PumpNo} to dict");
  412. if (!result)
  413. {
  414. statusDict.TryAdd(request.FuelingPoint.PumpNo, null);
  415. }
  416. }
  417. else
  418. {
  419. PumpStateHolder stateHolder = null;
  420. statusDict.TryGetValue(request.FuelingPoint.PumpNo, out stateHolder);
  421. if (stateHolder != null)
  422. {
  423. logger.Debug($"State holder, PumpNo: {stateHolder.PumpNo}, dispenser state: {stateHolder.State.DispenserState}, " +
  424. $"operation: {stateHolder.OperationType}");
  425. }
  426. if (stateHolder != null && stateHolder.OperationType != LockUnlockOperation.None)
  427. {
  428. logger.Debug($"PumpNo: {request.FuelingPoint.PumpNo}, Last Dispenser State: {stateHolder.State.DispenserState}, " +
  429. $"Current Dispenser State: {request.DispenserState}");
  430. if (stateHolder.State.DispenserState == 3 && request.DispenserState == 2)
  431. {
  432. //Pump is locked due to lock operation
  433. if (stateHolder.OperationType != LockUnlockOperation.None)
  434. {
  435. logger.Info("Locking done!");
  436. stateHolder.State = request; //Update the state
  437. OnLockUnlockCompleted?.Invoke(this, new LockUnlockEventArgs(stateHolder.OperationType, true));
  438. }
  439. }
  440. else if (stateHolder.State.DispenserState == 2 && request.DispenserState == 3)
  441. {
  442. //Pump is unlocked due to unlock operation
  443. if (stateHolder.OperationType != LockUnlockOperation.None)
  444. {
  445. logger.Info($"Unlocking done!");
  446. stateHolder.State = request; //Update the state
  447. OnLockUnlockCompleted?.Invoke(this, new LockUnlockEventArgs(stateHolder.OperationType, true));
  448. }
  449. }
  450. }
  451. else if (stateHolder != null && stateHolder.OperationType == LockUnlockOperation.None)
  452. {
  453. if (stateHolder.State.DispenserState != request.DispenserState)
  454. {
  455. logger.Warn($"Observed a pump state change, {stateHolder.State.DispenserState} -> {request.DispenserState}");
  456. stateHolder.State = request; //Update the state.
  457. }
  458. }
  459. }
  460. }
  461. public void Write(CardMessageBase cardMessage)
  462. {
  463. _context.Outgoing.Write(cardMessage);
  464. }
  465. public async Task<CardMessageBase> WriteAsync(CardMessageBase request, Func<CardMessageBase, CardMessageBase, bool> responseCapture,
  466. int timeout)
  467. {
  468. var resp = await _context.Outgoing.WriteAsync(request, responseCapture, timeout);
  469. return resp;
  470. }
  471. #endregion
  472. #region IEnumerable<IFdcPumpController> implementation
  473. public IEnumerator<IFdcPumpController> GetEnumerator()
  474. {
  475. return pumpHandlers.GetEnumerator();
  476. }
  477. IEnumerator IEnumerable.GetEnumerator()
  478. {
  479. return pumpHandlers.GetEnumerator();
  480. }
  481. #endregion
  482. public void PendMessage(CardMessageBase message)
  483. {
  484. lock (syncObj)
  485. {
  486. queue.Enqueue(message);
  487. }
  488. }
  489. public bool TrySendNextMessage()
  490. {
  491. lock (syncObj)
  492. {
  493. if (queue.Count > 0)
  494. {
  495. DebugLog($"queue count: {queue.Count}");
  496. var message = queue.Dequeue();
  497. Write(message);
  498. return true;
  499. }
  500. }
  501. return false;
  502. }
  503. public void StoreLatestFrameSqNo(int pumpId, byte frameSqNo)
  504. {
  505. var pump = GetPump(pumpId);
  506. if (pump != null)
  507. {
  508. pump.FrameSqNo = frameSqNo;
  509. }
  510. }
  511. public void UpdatePumpState(int pumpId, int logicId, LogicalDeviceState state)
  512. {
  513. var currentPump = GetPump(pumpId);
  514. currentPump?.FirePumpStateChange(state, Convert.ToByte(logicId));
  515. }
  516. public void UpdateFuelingStatus(int pumpId, FdcTransaction fuelingTransaction)
  517. {
  518. var currentPump = GetPump(pumpId);
  519. currentPump?.FireFuelingStatusChange(fuelingTransaction);
  520. }
  521. private HengshanPumpHandler GetPump(int pumpId)
  522. {
  523. return pumpHandlers.FirstOrDefault(p => p.PumpId == pumpId);
  524. }
  525. public void SetRealPrice(int pumpId,int price)
  526. {
  527. var currentPump = GetPump(pumpId);
  528. var nozzle = currentPump?.Nozzles.FirstOrDefault();
  529. if (nozzle != null)
  530. nozzle.RealPriceOnPhysicalPump = price;
  531. }
  532. #region Log methods
  533. private void InfoLog(string info)
  534. {
  535. logger.Info("PayTermHdlr " + info);
  536. }
  537. private void DebugLog(string debugMsg)
  538. {
  539. logger.Debug("PayTermHdlr " + debugMsg);
  540. }
  541. #endregion
  542. }
  543. public class HengshanPayTerminalHanlderGroupConfigV1
  544. {
  545. public string PumpIds { get; set; }
  546. public List<PumpSubAddress> PumpSubAddresses { get; set; }
  547. }
  548. public class HengshanPayTerminalHanlderGroupConfigV2
  549. {
  550. public string PumpIds { get; set; }
  551. public List<PumpSubAddress> PumpSubAddresses { get; set; }
  552. public List<PumpNozzleLogicId> PumpNozzleLogicIds { get; set; }
  553. public List<PumpSiteNozzleNo> PumpSiteNozzleNos { get; set; }
  554. public List<NozzleLogicId> NozzleLogicIds { get; set; }
  555. }
  556. public class PumpSubAddress
  557. {
  558. public byte PumpId { get; set; }
  559. public byte SubAddress { get; set; }
  560. }
  561. public class PumpNozzleLogicId
  562. {
  563. public byte PumpId { get; set; }
  564. public string LogicIds { get; set; }
  565. }
  566. public class PumpSiteNozzleNo
  567. {
  568. public byte PumpId { get; set; }
  569. public string SiteNozzleNos { get; set; }
  570. }
  571. public class NozzleLogicId
  572. {
  573. public byte NozzleNo { get; set; }
  574. public byte LogicId { get; set; }
  575. }
  576. }