PumpHandlerForSelfAuthPump.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.Configuration;
  5. using System.IO.Ports;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading;
  9. using Timer = System.Timers.Timer;
  10. using System.Collections;
  11. using HengShan_Pump_NonIC.MessageEntity;
  12. using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;
  13. using Wayne.FDCPOSLibrary;
  14. using System.Xml;
  15. using Edge.Core.Database.Models;
  16. using System.Threading.Tasks;
  17. namespace HengShan_Pump_NonIC
  18. {
  19. /// <summary>
  20. /// this is kind of HS non IC pump that have a build-in auto auth self function by hardware, so outside(fcc) can't auth it, it's
  21. /// a 'lift nozzle, and fueling pump'.
  22. /// basically it only have 2 states:
  23. /// idle: 不允许加油--加油结束--油枪关--电机关--D30--D20--金额加油
  24. /// fueling: 允许加油--加油过程--油枪打开--电机打开--D30--D20--金额加油
  25. /// </summary>
  26. public class PumpHandlerForSelfAuthPump : PumpHandler
  27. {
  28. private object syncObject = new object();
  29. // by seconds, change this value need change the correlated deviceOfflineCountdownTimer's interval as well
  30. private const int lastLogicalDeviceStateExpiredTime = 3;
  31. private DateTime lastLogicalDeviceStateReceivedTime;
  32. private System.Timers.Timer deviceOfflineCountdownTimer;
  33. //static ILog fdcLogger = log4net.LogManager.GetLogger("FdcServer");
  34. //static ILog logger = log4net.LogManager.GetLogger("PumpHandler");
  35. static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("PumpHandler");
  36. /// <summary>
  37. /// 恒山非IC油机
  38. /// </summary>
  39. /// <param name="pumpId"></param>
  40. /// <param name="nozzlesXmlConfiguration"></param>
  41. public PumpHandlerForSelfAuthPump(int pumpId, string nozzlesXmlConfiguration) : base(pumpId, nozzlesXmlConfiguration)
  42. {
  43. logger.Info("Pump: " + this.PumpId + ", PumpHandlerForSelfAuthPump");
  44. this.deviceOfflineCountdownTimer = new System.Timers.Timer(1000);
  45. this.deviceOfflineCountdownTimer.Elapsed += (_, __) =>
  46. {
  47. if (DateTime.Now.Subtract(this.lastLogicalDeviceStateReceivedTime).TotalSeconds
  48. >= lastLogicalDeviceStateExpiredTime)
  49. {
  50. //if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_OFFLINE)
  51. //{
  52. // this.lastLogicalDeviceState = LogicalDeviceState.FDC_OFFLINE;
  53. logger.Info("Pump: " + this.PumpId + ", " + " seems offline due to long time no see pump data incoming");
  54. // var safe0 = this.OnStateChange;
  55. // safe0?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_OFFLINE, null));
  56. // logger.Trace("Pump: " + this.pumpId + ", " + " OnStateChange event fired and back");
  57. //}
  58. }
  59. };
  60. this.deviceOfflineCountdownTimer.Start();
  61. }
  62. public override async Task Process(IContext<byte[], NonICMessageTemplateBase> context)
  63. {
  64. if (!isOnFdcServerInitCalled) return;
  65. this.lastLogicalDeviceStateReceivedTime = DateTime.Now;
  66. this.context = context;
  67. if (context.Incoming.Message is GetNozzleStatusResponse getNozzleStatusResponse)
  68. {
  69. //this.isSafeForSend = true;
  70. this.lastLogicalDeviceStateReceivedTime = DateTime.Now;
  71. //var getNozzleStatusResponse = context.Incoming.Message as GetNozzleStatusResponse;
  72. // put the price by reading the real price.
  73. this.nozzles.First().RealPriceOnPhysicalPump = getNozzleStatusResponse.单价;
  74. var latestStatus = getNozzleStatusResponse.GetPumpStatus();
  75. if (latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.允许加油)
  76. && latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.油枪打开)
  77. && latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.加油过程)
  78. && latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.电机打开))
  79. {
  80. /* 正处于加油状态下 */
  81. if (this.previousUnfinishedFuelingNozzleStatus != null
  82. && this.previousUnfinishedFuelingNozzleStatus.流水号 != getNozzleStatusResponse.流水号)
  83. {
  84. logger.Info("Pump: " + this.PumpId + ", " + "Detected a fast put back and pull out nozzle case(action time < polling time cause nozzle place back not detected)," +
  85. "\r\n will fire the trx done event for earlier trx(may have volume/money data loss!!!) with most recoverable data(most likely a bit less than real)->\r\n "
  86. + "seqNo: " + this.previousUnfinishedFuelingNozzleStatus.流水号
  87. + ", amount: " + this.previousUnfinishedFuelingNozzleStatus.加油金额
  88. + ", volume: " + this.previousUnfinishedFuelingNozzleStatus.加油量
  89. + ", price: " + this.previousUnfinishedFuelingNozzleStatus.单价);
  90. logger.Info("Pump: " + this.PumpId + ", " + " State switched to FDC_READY(simulate)");
  91. this.lastLogicalDeviceState = LogicalDeviceState.FDC_READY;
  92. this.FireOnStateChangeEvent(LogicalDeviceState.FDC_READY);
  93. var previousFuelingAmount = this.previousUnfinishedFuelingNozzleStatus.加油金额;
  94. var previousFuelingVol = this.previousUnfinishedFuelingNozzleStatus.加油量;
  95. var previousPrice = this.previousUnfinishedFuelingNozzleStatus.单价;
  96. var previousSeqNo = this.previousUnfinishedFuelingNozzleStatus.流水号;
  97. if (this.logicalNozzleIdToLastFuelSaleTrxMapping.ContainsKey(1))
  98. this.logicalNozzleIdToLastFuelSaleTrxMapping[1] = new FuelSaleTransaction()
  99. {
  100. TransactionSeqNumberFromPhysicalPump = this.previousUnfinishedFuelingNozzleStatus.流水号.ToString()
  101. };
  102. else
  103. this.logicalNozzleIdToLastFuelSaleTrxMapping.Add(1, new FuelSaleTransaction()
  104. {
  105. TransactionSeqNumberFromPhysicalPump = this.previousUnfinishedFuelingNozzleStatus.流水号.ToString()
  106. });
  107. //ThreadPool.QueueUserWorkItem(o =>
  108. //{
  109. var totalizer = await base.QueryTotalizerAsync(1);
  110. this.FireOnCurrentFuellingStatusChangeEvent(new FdcTransaction()
  111. {
  112. // use previousUnfinishedFuelingNozzleStatus could miss reading the last trx correct numbers(less than actual number)
  113. //, obviously this is caused by pump hardware design, impossible to recovery it back by our application.
  114. Nozzle = this.nozzles.First(),
  115. Amount = previousFuelingAmount,
  116. Volumn = previousFuelingVol,
  117. Price = previousPrice,
  118. SequenceNumberGeneratedOnPhysicalPump = previousSeqNo,
  119. AmountTotalizer = totalizer.Item1,
  120. VolumeTotalizer = totalizer.Item2,
  121. Finished = true,
  122. });
  123. //});
  124. }
  125. this.previousUnfinishedFuelingNozzleStatus = getNozzleStatusResponse;
  126. if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_FUELLING)
  127. {
  128. //status code: B1
  129. logger.Debug("Pump: " + this.PumpId + ", " + "收到新状态: 处于加油状态下");
  130. logger.Debug("Pump: " + this.PumpId + ", " + " State switched to FDC_CALLING(by simulate)");
  131. this.lastLogicalDeviceState = LogicalDeviceState.FDC_CALLING;
  132. base.FireOnStateChangeEvent(LogicalDeviceState.FDC_CALLING);
  133. logger.Debug("Pump: " + this.PumpId + ", " + " State switched to FDC_FUELLING(流水号: " + getNozzleStatusResponse.流水号 + ")");
  134. this.lastLogicalDeviceState = LogicalDeviceState.FDC_FUELLING;
  135. base.FireOnStateChangeEvent(LogicalDeviceState.FDC_FUELLING);
  136. }
  137. logger.Debug("Pump: " + this.PumpId + ", " + " fueling in progress with amt: " + getNozzleStatusResponse.加油金额
  138. + ", vol: " + getNozzleStatusResponse.加油量 + ", seq: " + getNozzleStatusResponse.流水号);
  139. //fire fuelling progress.
  140. base.FireOnCurrentFuellingStatusChangeEvent(new FdcTransaction()
  141. {
  142. // 恒山油机只有一把枪
  143. Nozzle = this.nozzles.First(),
  144. Amount = getNozzleStatusResponse.加油金额,
  145. Volumn = getNozzleStatusResponse.加油量,
  146. Price = getNozzleStatusResponse.单价,
  147. SequenceNumberGeneratedOnPhysicalPump = getNozzleStatusResponse.流水号,
  148. Finished = false,
  149. });
  150. }
  151. else if (latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.不允许加油)
  152. && latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.加油结束))
  153. {
  154. /* 油机首次上电也会进入此处,并不断发送上一次的加油记录,这种情况下的交易记录并无法判断其之前是否已经发送至上层系统(比如是否已经存入数据库),
  155. 所以此处不判断而是往上层系统里送入,由系统判断重复情况。 而其它正常加油过程中的交易记录仅在油机状态变化时才送入系统。*/
  156. // status code: 40
  157. this.previousUnfinishedFuelingNozzleStatus = null;
  158. if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_READY)
  159. {
  160. logger.Info("Pump: " + this.PumpId + ", " + "收到新状态: 加油结束(seq No.: " + getNozzleStatusResponse.流水号.ToString() + ")");
  161. logger.Info("Pump: " + this.PumpId + ", " + " State switched to FDC_READY");
  162. lastLogicalDeviceState = LogicalDeviceState.FDC_READY;
  163. base.FireOnStateChangeEvent(LogicalDeviceState.FDC_READY);
  164. // zero trx, do nothing.
  165. if (getNozzleStatusResponse.加油量 == 0 || getNozzleStatusResponse.加油金额 == 0)
  166. {
  167. logger.Debug("Pump: " + this.PumpId + ", " + "0 amount trx, will ignore");
  168. return;
  169. }
  170. // repeat received last fuel sale message, do nothing.
  171. if (base.logicalNozzleIdToLastFuelSaleTrxMapping.ContainsKey(1)
  172. && base.logicalNozzleIdToLastFuelSaleTrxMapping[1].TransactionSeqNumberFromPhysicalPump
  173. == getNozzleStatusResponse.流水号.ToString())
  174. {
  175. logger.Info("Pump: " + this.PumpId + ", fuel trx seq No. have NOT changed(previous seq No.: "
  176. + base.logicalNozzleIdToLastFuelSaleTrxMapping[1].TransactionSeqNumberFromPhysicalPump + "), will ignore");
  177. return;
  178. }
  179. // lastSale exists and 流水号 diff
  180. if (base.logicalNozzleIdToLastFuelSaleTrxMapping.ContainsKey(1)
  181. && base.logicalNozzleIdToLastFuelSaleTrxMapping[1].TransactionSeqNumberFromPhysicalPump
  182. != getNozzleStatusResponse.流水号.ToString())
  183. {
  184. logger.Info("Pump: " + base.PumpId
  185. + ", Detect a fule trx with 流水号: " + getNozzleStatusResponse.流水号 + " which is diff from previous fule trx(previous 流水号:" +
  186. this.logicalNozzleIdToLastFuelSaleTrxMapping[1].TransactionSeqNumberFromPhysicalPump + "), will generate a new fule sale trx with vol: " + getNozzleStatusResponse.加油量);
  187. this.logicalNozzleIdToLastFuelSaleTrxMapping[1] = new FuelSaleTransaction() { TransactionSeqNumberFromPhysicalPump = getNozzleStatusResponse.流水号.ToString() };
  188. }
  189. // lastSale not exists
  190. if (!this.logicalNozzleIdToLastFuelSaleTrxMapping.ContainsKey(1))
  191. {
  192. logger.Info("Pump: " + base.PumpId + ", very first initial trx detected on this pump, will generate a new fule sale trx(seq: " + getNozzleStatusResponse.流水号 + ", vol: " + getNozzleStatusResponse.加油量 + ", amt: " + getNozzleStatusResponse.加油金额 + ")");
  193. this.logicalNozzleIdToLastFuelSaleTrxMapping.Add(1, new FuelSaleTransaction() { TransactionSeqNumberFromPhysicalPump = getNozzleStatusResponse.流水号.ToString() });
  194. }
  195. var amount = getNozzleStatusResponse.加油金额;
  196. var fuelingVol = getNozzleStatusResponse.加油量;
  197. var price = getNozzleStatusResponse.单价;
  198. var seqNo = getNozzleStatusResponse.流水号;
  199. logger.Info("Pump: " + base.PumpId + ", fire trx done with amt: " + amount + ", vol: " + fuelingVol
  200. + ", price: " + price + ", seq No.: " + seqNo);
  201. //ThreadPool.QueueUserWorkItem(o =>
  202. //{
  203. var totalizer = await this.QueryTotalizerAsync(1);
  204. base.FireOnCurrentFuellingStatusChangeEvent(new FdcTransaction()
  205. {
  206. // 恒山油机 一个加油点只有一把枪
  207. Nozzle = this.nozzles.First(),
  208. Amount = amount,
  209. Volumn = fuelingVol,
  210. Price = price,
  211. SequenceNumberGeneratedOnPhysicalPump = seqNo,
  212. AmountTotalizer = totalizer.Item1,
  213. VolumeTotalizer = totalizer.Item2,
  214. Finished = true,
  215. });
  216. //});
  217. }
  218. }
  219. else
  220. {
  221. if (logger.IsTraceEnabled)
  222. logger.Trace("Unhandled GetNozzleStatusResponse is incoming: " + getNozzleStatusResponse.ToLogString());
  223. }
  224. }
  225. else
  226. {
  227. await base.Process(context);
  228. }
  229. }
  230. /// <summary>
  231. ///
  232. /// </summary>
  233. /// <param name="logicalNozzleId">useless for this type of pump, it always one pump one nozzle</param>
  234. /// <returns></returns>
  235. public override async Task<bool> AuthorizeAsync(byte logicalNozzleId)
  236. {
  237. return false;
  238. }
  239. /// <summary>
  240. ///
  241. /// </summary>
  242. /// <param name="moneyAmount"></param>
  243. /// <param name="logicalNozzleId">useless for this type of pump, it always one pump one nozzle</param>
  244. /// <returns></returns>
  245. public override async Task<bool> AuthorizeWithAmountAsync(int moneyAmount, byte logicalNozzleId)
  246. {
  247. return false;
  248. }
  249. /// <summary>
  250. ///
  251. /// </summary>
  252. /// <param name="volume"></param>
  253. /// <param name="logicalNozzleId">useless for this type of pump, it always one pump one nozzle</param>
  254. /// <returns></returns>
  255. public override async Task<bool> AuthorizeWithVolumeAsync(int volumn, byte logicalNozzleId)
  256. {
  257. return false;
  258. }
  259. }
  260. }