HengshanPayTermHandler.cs 24 KB

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