PumpHandler.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  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. public class PumpHandler : IFdcPumpController, IDeviceHandler<byte[], NonICMessageTemplateBase>
  20. {
  21. //static ILog fdcLogger = log4net.LogManager.GetLogger("FdcServer");
  22. //static ILog logger = log4net.LogManager.GetLogger("PumpHandler");
  23. static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("PumpHandler");
  24. protected IContext<byte[], NonICMessageTemplateBase> context;
  25. public event EventHandler<FdcPumpControllerOnStateChangeEventArg> OnStateChange;
  26. /// <summary>
  27. /// fired on fueling process is on going, the fuel amount should keep changing.
  28. /// </summary>
  29. public event EventHandler<FdcTransactionDoneEventArg> OnCurrentFuellingStatusChange;
  30. protected LogicalDeviceState lastLogicalDeviceState = LogicalDeviceState.FDC_CLOSED;
  31. private DateTime lastLogicalDeviceStateReceivedTime;
  32. // by seconds
  33. private const int lastLogicalDeviceStateExpiredTime = 6;
  34. /// <summary>
  35. /// Indicator for OnFdcServiceInit function called, the Process() will be called eariler that this function,
  36. /// </summary>
  37. protected bool isOnFdcServerInitCalled = false;
  38. private Guid uniqueId = Guid.NewGuid();
  39. private int pumpId = -1;
  40. protected List<LogicalNozzle> nozzles = new List<LogicalNozzle>();
  41. /// <summary>
  42. /// this type of pump state change is detected by FC actively polling, then state is always delay reported, so there's a corner case that in a fueling process,
  43. /// the attendants put back and pull out nozzle very quickly and it happened exactly in the middle of a polling, meanwhile,
  44. /// an auth request was done to auth the pump again(most likely the autoAuthCallingPump set with True),
  45. /// then the pump state returned from physical pump will still be read as a fueling state, but actually the
  46. /// 2nd fueling process is started, so detect the pump state is not enough, need detect if the fueling seq number reset to null(if place back nozzle detected) or not.
  47. /// </summary>
  48. protected GetNozzleStatusResponse previousUnfinishedFuelingNozzleStatus;
  49. public IEnumerable<LogicalNozzle> Nozzles => this.nozzles;
  50. protected void FireOnStateChangeEvent(LogicalDeviceState state)
  51. {
  52. var safe = this.OnStateChange;
  53. safe?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(state, this.nozzles.First()));
  54. }
  55. protected void FireOnCurrentFuellingStatusChangeEvent(FdcTransaction trx)
  56. {
  57. var safe = this.OnCurrentFuellingStatusChange;
  58. safe?.Invoke(this, new FdcTransactionDoneEventArg(trx));
  59. }
  60. /// <summary>
  61. /// 恒山非IC油机
  62. /// </summary>
  63. /// <param name="pumpId"></param>
  64. /// <param name="nozzlesXmlConfiguration"></param>
  65. public PumpHandler(int pumpId, string nozzlesXmlConfiguration)
  66. {
  67. this.pumpId = pumpId;
  68. // real nozzle Id put hardcode 1 here since HengShan_pump_nonIC only have 1 nozzle per pump.
  69. // price is not dectected yet, put Null.
  70. this.nozzles.Add(new LogicalNozzle(pumpId, 1, 1, null));
  71. }
  72. //protected bool isSafeForSend = false;
  73. public void Init(IContext<byte[], NonICMessageTemplateBase> context)
  74. {
  75. this.context = context;
  76. //this.polling = new Timer(this.pollingInterval);
  77. //this.polling.Elapsed += (_, __) =>
  78. //{
  79. // lock (this.syncObject)
  80. // {
  81. // this.isSafeForSend = false;
  82. // context.Outgoing.Write(new GetNozzleStatusRequest());
  83. // }
  84. //};
  85. //this.polling.Start();
  86. var timeWindowWithActivePollingOutgoing =
  87. this.context.Outgoing as TimeWindowWithActivePollingOutgoing<byte[], NonICMessageTemplateBase>;
  88. timeWindowWithActivePollingOutgoing.PollingMsgProducer = () => new GetNozzleStatusRequest();
  89. }
  90. public virtual async Task Process(IContext<byte[], NonICMessageTemplateBase> context)
  91. {
  92. if (!isOnFdcServerInitCalled) return;
  93. this.context = context;
  94. if (context.Incoming.Message is GetNozzleStatusResponse getNozzleStatusResponse)
  95. {
  96. //this.isSafeForSend = true;
  97. this.lastLogicalDeviceStateReceivedTime = DateTime.Now;
  98. // put the price by reading the real price.
  99. this.nozzles.First().RealPriceOnPhysicalPump = getNozzleStatusResponse.单价;
  100. var latestStatus = getNozzleStatusResponse.GetPumpStatus();
  101. if (latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.油枪打开)
  102. && latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.加油结束)
  103. && latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.不允许加油)
  104. && latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.电机关)
  105. && !latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.加油过程))
  106. {
  107. /* 非加油状态下的提枪 */
  108. if (this.lastLogicalDeviceState == LogicalDeviceState.FDC_AUTHORISED)
  109. {
  110. logger.Info("Pump: " + this.pumpId + ", " + "收到新状态: 提枪");
  111. //说明是未提枪时就已经auth成功了,所以还是保持发送 FDC_AUTHORISED 的状态给FdcClient。
  112. logger.Debug("Pump: " + this.pumpId + ", " + " 未提枪时就已经auth成功了, do nothing");
  113. }
  114. else
  115. {
  116. if (this.previousUnfinishedFuelingNozzleStatus != null)
  117. {
  118. this.previousUnfinishedFuelingNozzleStatus = null;
  119. // no data loss since getNozzleStatusResponse will still carry last trx data until an auth.
  120. 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)," +
  121. "\r\n will fire the trx done event for earlier trx(no data loss)->\r\n "
  122. + "seqNo: " + getNozzleStatusResponse.流水号
  123. + ", amount: " + getNozzleStatusResponse.加油金额
  124. + ", volume: " + getNozzleStatusResponse.加油量
  125. + ", price: " + getNozzleStatusResponse.单价);
  126. logger.Info("Pump: " + this.pumpId + ", " + " State switched to FDC_READY(simulate)");
  127. this.lastLogicalDeviceState = LogicalDeviceState.FDC_READY;
  128. this.FireOnStateChangeEvent(LogicalDeviceState.FDC_READY);
  129. var previousFuelingAmount = getNozzleStatusResponse.加油金额;
  130. var previousFuelingVol = getNozzleStatusResponse.加油量;
  131. var previousPrice = getNozzleStatusResponse.单价;
  132. var previousSeqNo = getNozzleStatusResponse.流水号;
  133. if (this.logicalNozzleIdToLastFuelSaleTrxMapping.ContainsKey(1))
  134. this.logicalNozzleIdToLastFuelSaleTrxMapping[1] = new FuelSaleTransaction() { TransactionSeqNumberFromPhysicalPump = getNozzleStatusResponse.流水号.ToString() };
  135. else
  136. this.logicalNozzleIdToLastFuelSaleTrxMapping.Add(1, new FuelSaleTransaction() { TransactionSeqNumberFromPhysicalPump = getNozzleStatusResponse.流水号.ToString() });
  137. //ThreadPool.QueueUserWorkItem(o =>
  138. //{
  139. var totalizer = await this.QueryTotalizerAsync(1);
  140. this.FireOnCurrentFuellingStatusChangeEvent(new FdcTransaction()
  141. {
  142. // 恒山油机只有一把枪
  143. Nozzle = this.nozzles.First(),
  144. Amount = previousFuelingAmount,
  145. Volumn = previousFuelingVol,
  146. Price = previousPrice,
  147. SequenceNumberGeneratedOnPhysicalPump = previousSeqNo,
  148. AmountTotalizer = totalizer.Item1,
  149. VolumeTotalizer = totalizer.Item2,
  150. Finished = true,
  151. });
  152. //});
  153. }
  154. if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_CALLING)
  155. {
  156. logger.Info("Pump: " + this.pumpId + ", " + "收到新状态: 提枪");
  157. logger.Debug("Pump: " + this.pumpId + ", " + " State switched to FDC_CALLING");
  158. //直接提枪了
  159. this.lastLogicalDeviceState = LogicalDeviceState.FDC_CALLING;
  160. var safe = this.OnStateChange;
  161. safe?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_CALLING, this.nozzles.First()));
  162. }
  163. }
  164. }
  165. else if (latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.允许加油)
  166. && latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.油枪打开)
  167. && (latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.加油过程)
  168. || latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.电机打开)))
  169. {
  170. /* 正处于加油状态下 */
  171. this.previousUnfinishedFuelingNozzleStatus = getNozzleStatusResponse;
  172. if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_FUELLING)
  173. {
  174. //status code: B1
  175. logger.Info("Pump: " + this.pumpId + ", " + "收到新状态: 加油状态中");
  176. logger.Debug("Pump: " + this.pumpId + ", " + " State switched to FDC_FUELLING");
  177. this.lastLogicalDeviceState = LogicalDeviceState.FDC_FUELLING;
  178. var safe0 = this.OnStateChange;
  179. safe0?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_FUELLING, this.nozzles.First()));
  180. }
  181. logger.Debug("Pump: " + this.pumpId + ", " + " fueling in progress with amt: " + getNozzleStatusResponse.加油量
  182. + ", vol: " + getNozzleStatusResponse.加油量 + ", seq: " + getNozzleStatusResponse.流水号);
  183. //fire fuelling progress.
  184. var safe1 = this.OnCurrentFuellingStatusChange;
  185. safe1?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction()
  186. {
  187. // 恒山油机只有一把枪
  188. Nozzle = this.nozzles.First(),
  189. Amount = getNozzleStatusResponse.加油金额,
  190. Volumn = getNozzleStatusResponse.加油量,
  191. Price = getNozzleStatusResponse.单价,
  192. SequenceNumberGeneratedOnPhysicalPump = getNozzleStatusResponse.流水号,
  193. Finished = false,
  194. }));
  195. }
  196. else if (latestStatus.Any(f => f == GetNozzleStatusResponse.PumpStatus.加油结束))
  197. {
  198. /* 油机首次上电也会进入此处,并不断发送上一次的加油记录,这种情况下的交易记录并无法判断其之前是否已经发送至系统(并存入数据库),
  199. 所以继续往系统里送入,由系统判断重复情况。 而其它正常加油过程中的交易记录仅在油机状态变化时才送入系统。*/
  200. // status code: 40
  201. //logger.Debug("Pump: " + this.pumpId + ", " + "收到状态: 加油结束");
  202. this.previousUnfinishedFuelingNozzleStatus = null;
  203. if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_READY)
  204. {
  205. logger.Debug("Pump: " + this.pumpId + ", " + "收到新状态: 加油结束, will send StopRequest(Unauth) anyway");
  206. this.context.Outgoing.WriteAsync(new StopRequest(),
  207. (request, testResponse) => testResponse is StopResponse,
  208. (request, response) =>
  209. {
  210. if (response == null) logger.Info("Pump: " + this.pumpId + ", " + "StopRequest(Unauth) timed out");
  211. else
  212. {
  213. var stopResponse = (StopResponse)response;
  214. if (stopResponse.EnumResult == NonICMessageTemplateBase.Result.失败)
  215. logger.Info("Pump: " + this.pumpId + ", " + "StopRequest(Unauth) respond a Failure");
  216. }
  217. }, 2000);
  218. logger.Debug("Pump: " + this.pumpId + ", " + " State switched to FDC_READY");
  219. lastLogicalDeviceState = LogicalDeviceState.FDC_READY;
  220. var safe0 = this.OnStateChange;
  221. safe0?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_READY, null));
  222. // zero trx, do nothing.
  223. if (getNozzleStatusResponse.加油量 == 0 || getNozzleStatusResponse.加油金额 == 0)
  224. return;
  225. // repeat received last fuel sale message, do nothing.
  226. if (this.logicalNozzleIdToLastFuelSaleTrxMapping.ContainsKey(1)
  227. && this.logicalNozzleIdToLastFuelSaleTrxMapping[1].TransactionSeqNumberFromPhysicalPump
  228. == getNozzleStatusResponse.流水号.ToString())
  229. return;
  230. // lastSale exists and 流水号 diff
  231. if (this.logicalNozzleIdToLastFuelSaleTrxMapping.ContainsKey(1)
  232. && this.logicalNozzleIdToLastFuelSaleTrxMapping[1].TransactionSeqNumberFromPhysicalPump
  233. != getNozzleStatusResponse.流水号.ToString())
  234. {
  235. logger.Info("Pump: " + this.pumpId
  236. + ", Detect a fuel trx with 流水号: " + getNozzleStatusResponse.流水号 + " which is diff from previous fuel trx(previous 流水号:" +
  237. this.logicalNozzleIdToLastFuelSaleTrxMapping[1].TransactionSeqNumberFromPhysicalPump + "), will generate a new fuel sale trx with vol: " + getNozzleStatusResponse.加油量);
  238. this.logicalNozzleIdToLastFuelSaleTrxMapping[1] = new FuelSaleTransaction() { TransactionSeqNumberFromPhysicalPump = getNozzleStatusResponse.流水号.ToString() };
  239. }
  240. // lastSale not exists
  241. if (!this.logicalNozzleIdToLastFuelSaleTrxMapping.ContainsKey(1))
  242. {
  243. logger.Info("Pump: " + this.pumpId + ", very first initial trx detected on this pump, will generate a new fuel sale trx(seq: " + getNozzleStatusResponse.流水号 + ", vol: " + getNozzleStatusResponse.加油量 + ", amt: " + getNozzleStatusResponse.加油金额 + ")");
  244. this.logicalNozzleIdToLastFuelSaleTrxMapping.Add(1, new FuelSaleTransaction() { TransactionSeqNumberFromPhysicalPump = getNozzleStatusResponse.流水号.ToString() });
  245. }
  246. var amount = getNozzleStatusResponse.加油金额;
  247. var fuelingVol = getNozzleStatusResponse.加油量;
  248. var price = getNozzleStatusResponse.单价;
  249. var seqNo = getNozzleStatusResponse.流水号;
  250. //ThreadPool.QueueUserWorkItem(o =>
  251. //{
  252. var totalizer = await this.QueryTotalizerAsync(1);
  253. logger.Info("Pump: " + this.pumpId + ", fire trx done with amt: " + amount + ", vol: " + fuelingVol
  254. + ", price: " + price + ", seqNo.: " + seqNo);
  255. var safe1 = this.OnCurrentFuellingStatusChange;
  256. safe1?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction()
  257. {
  258. // 恒山油机 一个加油点只有一把枪
  259. Nozzle = this.nozzles.First(),
  260. Amount = amount,
  261. Volumn = fuelingVol,
  262. Price = price,
  263. SequenceNumberGeneratedOnPhysicalPump = seqNo,
  264. AmountTotalizer = totalizer.Item1,
  265. VolumeTotalizer = totalizer.Item2,
  266. Finished = true,
  267. }));
  268. //});
  269. }
  270. }
  271. else
  272. {
  273. if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_ERRORSTATE)
  274. {
  275. this.lastLogicalDeviceState = LogicalDeviceState.FDC_ERRORSTATE;
  276. logger.Debug("Pump: " + this.pumpId + ", " + "收到未知状态: " + getNozzleStatusResponse.ToLogString() + ", \r\n switch to FDC_ERRORSTATE");
  277. }
  278. }
  279. }
  280. }
  281. public string Name => this.GetType().FullName;
  282. public Guid Id => this.uniqueId;
  283. /// <summary>
  284. /// Gets the Identification of the pump for the system. Is the logical number of the pump
  285. /// </summary>
  286. public int PumpId => this.pumpId;
  287. /// <summary>
  288. /// this pump have no way to share same comport since this HengShan protocol content does not contains
  289. /// any id info, so always static 0 here.
  290. /// 地址面地址
  291. /// </summary>
  292. public int PumpPhysicalId => 0;
  293. public int AmountDecimalDigits => 2;
  294. public int VolumeDecimalDigits => 2;
  295. public int PriceDecimalDigits => 2;
  296. public int VolumeTotalizerDecimalDigits => 2;
  297. public virtual async Task<LogicalDeviceState> QueryStatusAsync()
  298. {
  299. // if last state is expired, we return a OFFLINE here to FdcClient.
  300. if (DateTime.Now.Subtract(this.lastLogicalDeviceStateReceivedTime).TotalSeconds > lastLogicalDeviceStateExpiredTime)
  301. {
  302. if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_OFFLINE)
  303. {
  304. this.lastLogicalDeviceState = LogicalDeviceState.FDC_OFFLINE;
  305. logger.Info("Pump: " + this.pumpId + ", " + " State switched to FDC_OFFLINE due to cached state expired");
  306. var safe0 = this.OnStateChange;
  307. safe0?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_OFFLINE, null));
  308. }
  309. return LogicalDeviceState.FDC_OFFLINE;
  310. }
  311. return this.lastLogicalDeviceState;
  312. }
  313. /// <summary>
  314. ///
  315. /// </summary>
  316. /// <returns>MoneyTotalizer:VolumnTotalizer</returns>
  317. public async Task<Tuple<int, int>> QueryTotalizerAsync(byte logicalNozzleId)
  318. {
  319. logger.Info("Pump: " + this.pumpId + ", " + " Start QueryTotalizer for logicalNozzle: " + logicalNozzleId);
  320. if (this.lastLogicalDeviceState == LogicalDeviceState.FDC_CLOSED
  321. || this.lastLogicalDeviceState == LogicalDeviceState.FDC_OFFLINE)
  322. {
  323. logger.Info("Pump: " + this.pumpId + ", " + " Pump is in state FDC_CLOSED or FDC_OFFLINE, will return -1, -1");
  324. return new System.Tuple<int, int>(-1, -1);
  325. }
  326. var result = new System.Tuple<int, int>(-1, -1);
  327. var response = await this.context.Outgoing.WriteAsync(new GetAccumulateRequest(),
  328. (request, testResponse) => testResponse is GetAccumulateResponse, 2000);
  329. if (response == null) logger.Info("Pump: " + this.pumpId + ", " + "QueryTotalizer timed out");
  330. else
  331. {
  332. var accumResponse = (GetAccumulateResponse)response;
  333. result = new Tuple<int, int>(accumResponse.金额累计, accumResponse.升累计);
  334. }
  335. return result;
  336. }
  337. public virtual async Task<bool> ChangeFuelPriceAsync(int newPriceWithoutDecimalPoint, byte logicalNozzleId)
  338. {
  339. logger.Info("Pump: " + this.pumpId + ", " + " Start ChangeFuelPrice for logicalNozzle: " + logicalNozzleId + " with new price(without decimalPoints): " + newPriceWithoutDecimalPoint);
  340. if (this.lastLogicalDeviceState == LogicalDeviceState.FDC_CLOSED
  341. || this.lastLogicalDeviceState == LogicalDeviceState.FDC_OFFLINE)
  342. {
  343. logger.Info("Pump: " + this.pumpId + ", " + " Pump is in state FDC_CLOSED or FDC_OFFLINE, ChangeFuelPrice will return false");
  344. return false;
  345. }
  346. var succeed = false;
  347. var response = await this.context.Outgoing.WriteAsync(new SetFuelPriceRequest() { FuelPrice = newPriceWithoutDecimalPoint },
  348. (request, testResponse) => testResponse is SetFuelPriceResponse, 2500);
  349. if (response == null)
  350. logger.Info("Pump: " + this.pumpId + ", " + "ChangeFuelPrice timed out");
  351. else
  352. {
  353. var priceChangeResponse = (SetFuelPriceResponse)response;
  354. if (priceChangeResponse.EnumResult != NonICMessageTemplateBase.Result.成功)
  355. {
  356. logger.Info("Pump: " + this.pumpId + ", " + "ChangeFuelPriceResponse is NOT NonICMessageTemplateBase.Result.成功");
  357. succeed = false;
  358. }
  359. else
  360. {
  361. logger.Info("Pump: " + this.pumpId + ", " + "ChangeFuelPriceResponse succeed");
  362. succeed = true;
  363. }
  364. }
  365. return succeed;
  366. }
  367. /// <summary>
  368. ///
  369. /// </summary>
  370. /// <param name="logicalNozzleId">useless for this type of pump, it always one pump one nozzle</param>
  371. /// <returns></returns>
  372. public virtual async Task<bool> AuthorizeAsync(byte logicalNozzleId)
  373. {
  374. logger.Info("Pump: " + this.pumpId + ", " + "Start Authorize for logicalNozzle: " + logicalNozzleId);
  375. var succeed = false;
  376. var response = await this.context.Outgoing.WriteAsync(new StartRequest(),
  377. (request, testResponse) => testResponse is StartResponse, 2500);
  378. if (response == null) { logger.Info("Pump: " + this.pumpId + ", " + "Authorize timed out"); }
  379. else
  380. {
  381. var startResponse = (StartResponse)response;
  382. if (startResponse.EnumResult != NonICMessageTemplateBase.Result.成功)
  383. {
  384. logger.Info("Pump: " + this.pumpId + ", " + "StartResponse is NOT NonICMessageTemplateBase.Result.成功");
  385. succeed = false;
  386. }
  387. else
  388. {
  389. logger.Info("Pump: " + this.pumpId + ", " + "Authorize (StartResponse) succeed");
  390. succeed = true;
  391. }
  392. }
  393. return succeed;
  394. }
  395. /// <summary>
  396. ///
  397. /// </summary>
  398. /// <param name="moneyAmount"></param>
  399. /// <param name="logicalNozzleId">useless for this type of pump, it always one pump one nozzle</param>
  400. /// <returns></returns>
  401. public virtual async Task<bool> AuthorizeWithAmountAsync(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId)
  402. {
  403. logger.Info("Pump: " + this.pumpId + ", " + "Start AuthorizeWithAmount for logicalNozzle: " + logicalNozzleId + " with money(without decimalPoint): " + moneyAmountWithoutDecimalPoint);
  404. var succeed = false;
  405. var response = await this.context.Outgoing.WriteAsync(new AuthPumpWithAmountRequest() { Amount = moneyAmountWithoutDecimalPoint },
  406. (request, testResponse) => testResponse is AuthPumpWithAmountResponse, 4000);
  407. if (response == null) logger.Info("Pump: " + this.pumpId + ", " + "AuthorizeWithAmount timed out");
  408. var presetResponse = (AuthPumpWithAmountResponse)response;
  409. if (presetResponse.EnumResult != NonICMessageTemplateBase.Result.成功)
  410. {
  411. logger.Info("Pump: " + this.pumpId + ", " + "AuthPumpWithAmountResponse is NOT NonICMessageTemplateBase.Result.成功");
  412. succeed = false;
  413. }
  414. else
  415. {
  416. if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_AUTHORISED)
  417. {
  418. lastLogicalDeviceState = LogicalDeviceState.FDC_AUTHORISED;
  419. var safe = this.OnStateChange;
  420. safe?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_AUTHORISED, this.nozzles.First()));
  421. }
  422. Thread.Sleep(500);
  423. succeed = await this.AuthorizeAsync(logicalNozzleId);
  424. }
  425. return succeed;
  426. }
  427. /// <summary>
  428. ///
  429. /// </summary>
  430. /// <param name="volumn"></param>
  431. /// <param name="logicalNozzleId">useless for this type of pump, it always one pump one nozzle</param>
  432. /// <returns></returns>
  433. public virtual async Task<bool> AuthorizeWithVolumeAsync(int volumnWithoutDecimalPoint, byte logicalNozzleId)
  434. {
  435. logger.Info("Pump: " + this.pumpId + ", " + "Start AuthorizeWithVolumn for logicalNozzle: " + logicalNozzleId + " with vol(without decimalPoint): " + volumnWithoutDecimalPoint);
  436. var succeed = false;
  437. var response = await this.context.Outgoing.WriteAsync(new AuthPumpWithGallonRequest() { Gallon = volumnWithoutDecimalPoint },
  438. (request, testResponse) => testResponse is AuthPumpWithGallonResponse, 4000);
  439. if (response == null) logger.Info("Pump: " + this.pumpId + ", " + "AuthPumpWithGallonRequest timed out");
  440. var presetResponse = (AuthPumpWithGallonResponse)response;
  441. if (presetResponse.EnumResult != NonICMessageTemplateBase.Result.成功)
  442. {
  443. logger.Info("Pump: " + this.pumpId + ", " + "AuthorizeWithVolumnResponse is NOT NonICMessageTemplateBase.Result.成功");
  444. succeed = false;
  445. }
  446. else
  447. {
  448. if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_AUTHORISED)
  449. {
  450. lastLogicalDeviceState = LogicalDeviceState.FDC_AUTHORISED;
  451. var safe = this.OnStateChange;
  452. safe?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_AUTHORISED, this.nozzles.First()));
  453. }
  454. Thread.Sleep(500);
  455. succeed = await this.AuthorizeAsync(logicalNozzleId);
  456. }
  457. return succeed;
  458. }
  459. public virtual async Task<bool> FuelingRoundUpByAmountAsync(int amount)
  460. {
  461. logger.Info("Pump: " + this.pumpId + ", " + " Start FuelingRoundUpByAmount, amount: " + amount + " will be ignored due to hardware limit");
  462. var succeed = false;
  463. var response = await this.context.Outgoing.WriteAsync(new RoundUpByAmountRequest(),
  464. (request, testResponse) => testResponse is RoundUpByAmountResponse, 2500);
  465. if (response == null)
  466. {
  467. succeed = false;
  468. logger.Info("Pump: " + this.pumpId + ", " + "FuelingRoundUpByAmount timed out");
  469. }
  470. else
  471. {
  472. var roundUpResponse = (RoundUpByAmountResponse)response;
  473. if (roundUpResponse.EnumResult != NonICMessageTemplateBase.Result.成功)
  474. {
  475. logger.Info("Pump: " + this.pumpId + ", " + "roundUpResponse is NOT NonICMessageTemplateBase.Result.成功");
  476. succeed = false;
  477. }
  478. else
  479. succeed = true;
  480. }
  481. return succeed;
  482. }
  483. #region not implemented
  484. public async Task<bool> UnAuthorizeAsync(byte logicalNozzleId)
  485. {
  486. throw new NotImplementedException();
  487. }
  488. public async Task<bool> SuspendFuellingAsync()
  489. {
  490. throw new NotImplementedException();
  491. }
  492. public async Task<bool> ResumeFuellingAsync()
  493. {
  494. throw new NotImplementedException();
  495. }
  496. public async Task<bool> FuelingRoundUpByVolumeAsync(int volume)
  497. { throw new NotImplementedException(); }
  498. #endregion
  499. /// <summary>
  500. /// </summary>
  501. protected Dictionary<byte, FuelSaleTransaction> logicalNozzleIdToLastFuelSaleTrxMapping = new Dictionary<byte, FuelSaleTransaction>();
  502. public void OnFdcServerInit(Dictionary<string, object> parameters)
  503. {
  504. if (parameters.ContainsKey("LastPriceChange"))
  505. {
  506. }
  507. /* Load Last sale(from db) for void the case of FC accidently disconnect from Pump in fueling,
  508. and may cause a fueling trx gone from FC control */
  509. if (parameters.ContainsKey("LastFuelSaleTrx"))
  510. {
  511. // nozzle logical id:lastSale
  512. var lastFuelSaleTrxes = parameters["LastFuelSaleTrx"] as Dictionary<byte, FuelSaleTransaction>;
  513. foreach (var lastFuelSaleTrx in lastFuelSaleTrxes)
  514. {
  515. logger.Info("Pump: " + this.pumpId + ", OnFdcServerInit, load last fuel sale " +
  516. "on logical nozzle: " + lastFuelSaleTrx.Key + " with value: " + lastFuelSaleTrx.Value);
  517. this.logicalNozzleIdToLastFuelSaleTrxMapping.Remove(lastFuelSaleTrx.Key);
  518. this.logicalNozzleIdToLastFuelSaleTrxMapping.Add(lastFuelSaleTrx.Key, lastFuelSaleTrx.Value);
  519. }
  520. }
  521. this.isOnFdcServerInitCalled = true;
  522. }
  523. public async Task<bool> LockNozzleAsync(byte logicalNozzleId)
  524. {
  525. return false;
  526. }
  527. public async Task<bool> UnlockNozzleAsync(byte logicalNozzleId)
  528. {
  529. return false;
  530. }
  531. }
  532. }