StatePumpHandler.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. using Edge.Core.Database.Models;
  2. using Edge.Core.Processor;
  3. using Edge.Core.IndustryStandardInterface.Pump;
  4. using Edge.Core.UniversalApi;
  5. using LanTian_Pump_664_Or_886.MessageEntity;
  6. using LanTian_Pump_664_Or_886.MessageEntity.Incoming;
  7. using LanTian_Pump_664_Or_886.MessageEntity.Outgoing;
  8. using Microsoft.Extensions.DependencyInjection;
  9. using Microsoft.Extensions.Logging;
  10. using Microsoft.Extensions.Logging.Abstractions;
  11. using NLog.Fluent;
  12. using Edge.Core.Parser.BinaryParser.Util;
  13. using Stateless;
  14. using System;
  15. using System.Collections.Generic;
  16. using System.Linq;
  17. using System.Text;
  18. using System.Threading;
  19. using System.Threading.Tasks;
  20. using System.Xml;
  21. using Wayne.FDCPOSLibrary;
  22. using static LanTian_Pump_664_Or_886.PumpGroupParameter;
  23. namespace LanTian_Pump_664_Or_886
  24. {
  25. public class StatePumpHandler : IFdcPumpController
  26. {
  27. private IServiceProvider services;
  28. static ILogger logger = NullLogger.Instance;
  29. private LogicalDeviceState lastLogicalDeviceState = LogicalDeviceState.FDC_OFFLINE;
  30. private DateTime lastLogicalDeviceStateReceivedTime;
  31. // by seconds, change this value need change the correlated deviceOfflineCountdownTimer's interval as well
  32. public const int lastLogicalDeviceStateExpiredTime = 6;
  33. private List<LogicalNozzle> nozzles = new List<LogicalNozzle>();
  34. private System.Timers.Timer deviceOfflineCountdownTimer;
  35. private IContext<byte[], MessageBase> context;
  36. private bool isOnFdcServerInitCalled = false;
  37. private int pumpId;
  38. /// <summary>
  39. /// hardware address value of this pump, must be set in physical dispenser side.
  40. /// </summary>
  41. private byte address;
  42. private PumpModelEnum pumpModel;
  43. private PumpAuthorizeModeEnum pumpAuthorizeMode;
  44. protected enum Trigger
  45. {
  46. //AnyPumpMsgReceived,
  47. AnyPumpMsgHaveNotReceivedForWhile,
  48. NozzleLifted_And_开机,
  49. NozzleLifted_And_停机,
  50. NozzleReplaced_And_开机,
  51. NozzleReplaced_And_停机,
  52. NozzleFuelNumbersIsRunning,
  53. //PumpAuthorizedByFC,
  54. }
  55. StateMachine<LogicalDeviceState, Trigger> stateless
  56. = new StateMachine<LogicalDeviceState, Trigger>(LogicalDeviceState.FDC_OFFLINE);
  57. private int amountDecimalDigits;
  58. private int volumeDecimalDigits;
  59. private int priceDecimalDigits;
  60. private int volumeTotalizerDecimalDigits;
  61. private StateMachine<LogicalDeviceState, Trigger>.TriggerWithParameters<FdcTransaction> nozzleFuelNumbersIsRunningTrigger;
  62. /// <summary>
  63. /// for avoid a case that FC may miss a pump status change event(pump side issue? or wire issue?),
  64. /// we timely actively request the pump state.
  65. /// </summary>
  66. //private System.Timers.Timer pollingPumpStatus;
  67. //private int pollingPumpStatusInterval = 30000;
  68. #region
  69. public string Name => "LanTian_Pump_664_Or_886";
  70. public int PumpId => this.pumpId;
  71. public IEnumerable<LogicalNozzle> Nozzles => this.nozzles;
  72. public int AmountDecimalDigits => this.amountDecimalDigits;
  73. public int VolumeDecimalDigits => this.volumeDecimalDigits;
  74. public int PriceDecimalDigits => this.priceDecimalDigits;
  75. public int VolumeTotalizerDecimalDigits => this.volumeDecimalDigits;
  76. public Guid Id => Guid.NewGuid();
  77. public int PumpPhysicalId => this.address;
  78. public event EventHandler<FdcPumpControllerOnStateChangeEventArg> OnStateChange;
  79. public event EventHandler<FdcTransactionDoneEventArg> OnCurrentFuellingStatusChange;
  80. public async Task<global::Wayne.FDCPOSLibrary.LogicalDeviceState> QueryStatusAsync()
  81. {
  82. return this.lastLogicalDeviceState;
  83. }
  84. public async Task<bool> LockNozzleAsync(byte logicalNozzleId)
  85. {
  86. return false;
  87. }
  88. public async Task<bool> UnlockNozzleAsync(byte logicalNozzleId)
  89. {
  90. return false;
  91. }
  92. #endregion
  93. public StatePumpHandler(int pumpId, byte address,
  94. PumpModelEnum pumpModel, PumpAuthorizeModeEnum pumpAuthorizeMode,
  95. int amountDecimalDigits, int volumeDecimalDigits,
  96. int priceDecimalDigits, int volumeTotalizerDecimalDigits,
  97. IServiceProvider services)
  98. {
  99. this.services = services;
  100. var loggerFactory = services.GetRequiredService<ILoggerFactory>();
  101. logger = loggerFactory.CreateLogger("PumpHandler");
  102. try
  103. {
  104. this.pumpId = pumpId;
  105. this.address = address;
  106. this.pumpModel = pumpModel;
  107. this.pumpAuthorizeMode = pumpAuthorizeMode;
  108. this.amountDecimalDigits = amountDecimalDigits;
  109. this.volumeDecimalDigits = volumeDecimalDigits;
  110. this.priceDecimalDigits = priceDecimalDigits;
  111. this.volumeTotalizerDecimalDigits = volumeTotalizerDecimalDigits;
  112. this.pumpId = pumpId;
  113. // real nozzle Id put hardcode 1 here since HengShan_pump_nonIC only have 1 nozzle per pump.
  114. // price is not dectected yet, put Null.
  115. this.nozzles.Add(new LogicalNozzle(pumpId, this.address, 1, null));
  116. this.deviceOfflineCountdownTimer = new System.Timers.Timer(2000);
  117. this.deviceOfflineCountdownTimer.Elapsed += async (_, __) =>
  118. {
  119. if (DateTime.Now.Subtract(this.lastLogicalDeviceStateReceivedTime).TotalSeconds
  120. >= lastLogicalDeviceStateExpiredTime)
  121. await this.stateless.FireAsync(Trigger.AnyPumpMsgHaveNotReceivedForWhile);
  122. };
  123. this.deviceOfflineCountdownTimer.Start();
  124. this.stateless.OnTransitioned(async (transition) =>
  125. {
  126. if (transition.Destination != transition.Source)
  127. {
  128. this.lastLogicalDeviceState = transition.Destination;
  129. logger.LogInformation("Pump: " + this.pumpId + ", " + " State switched from: " + transition.Source + " to " + transition.Destination);
  130. this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(transition.Destination, this.nozzles.FirstOrDefault()));
  131. }
  132. if (transition.Source == LogicalDeviceState.FDC_OFFLINE)
  133. {
  134. //always send a query to try to align latest price when switched to Online.
  135. this.context.Outgoing.Write(new ReadPriceRequest() { Adrs = this.address });
  136. }
  137. });
  138. this.stateless.Configure(LogicalDeviceState.FDC_OFFLINE)
  139. .OnEntryAsync(async () => { })
  140. .OnEntryAsync(async () => { })
  141. //.Ignore(Trigger.AnyPumpMsgReceived)
  142. .Ignore(Trigger.AnyPumpMsgHaveNotReceivedForWhile)
  143. .PermitIf(Trigger.NozzleLifted_And_停机, LogicalDeviceState.FDC_CALLING, () => true)
  144. .PermitIf(Trigger.NozzleLifted_And_开机, LogicalDeviceState.FDC_AUTHORISED, () => true)
  145. .PermitIf(Trigger.NozzleReplaced_And_停机, LogicalDeviceState.FDC_READY, () => true)
  146. .PermitIf(Trigger.NozzleReplaced_And_开机, LogicalDeviceState.FDC_AUTHORISED, () => true);
  147. //once a fule sale is done, a retrieving process is triggered, before the process done, new pump Calling will be blocked.
  148. bool isOnRetrievingLastFuelSale = false;
  149. this.stateless.Configure(LogicalDeviceState.FDC_READY)
  150. .OnEntryFromAsync(Trigger.NozzleReplaced_And_停机, async (transition) =>
  151. {
  152. if (transition.Source == LogicalDeviceState.FDC_FUELLING)
  153. {
  154. isOnRetrievingLastFuelSale = true;
  155. ReadFuelDataResponse finalTrxDataReading;
  156. try
  157. {
  158. logger.LogInformation("Pump: " + this.pumpId + ", A fueling just done, Retrieving last fuel sale trx...");
  159. finalTrxDataReading =
  160. await this.context.Outgoing.WriteAsync(new ReadFuelDataRequest() { Adrs = this.address },
  161. (_, testResponse) => testResponse is ReadFuelDataResponse rp && rp.Adrs == this.address, 3000) as ReadFuelDataResponse;
  162. if (finalTrxDataReading == null)
  163. {
  164. finalTrxDataReading =
  165. await this.context.Outgoing.WriteAsync(new ReadFuelDataRequest() { Adrs = this.address },
  166. (_, testResponse) => testResponse is ReadFuelDataResponse rp && rp.Adrs == this.address, 2000) as ReadFuelDataResponse;
  167. if (finalTrxDataReading == null)
  168. {
  169. logger.LogWarning("Pump: " + this.pumpId + ", fuel sale trx is missing for final read(auto tried one more time and still failed), trx will lost");
  170. return;
  171. }
  172. }
  173. }
  174. finally
  175. {
  176. isOnRetrievingLastFuelSale = false;
  177. }
  178. // at least within 65 years, exception will not throw here
  179. int newTrxSeqNumber = (int)(DateTime.Now.Subtract(new DateTime(2020, 6, 5)).TotalSeconds);
  180. logger.LogDebug("Pump: " + this.pumpId + ", Retrieved fuel sale trx, amt: " + finalTrxDataReading.Amount + ", vol: " + finalTrxDataReading.Volume + ", seqNo.: " + newTrxSeqNumber);
  181. var newTrx = new FdcTransaction()
  182. {
  183. // 只有一把枪
  184. Nozzle = this.nozzles.First(),
  185. Amount = finalTrxDataReading.Amount,
  186. Volumn = finalTrxDataReading.Volume,
  187. Price = this.nozzles.First().RealPriceOnPhysicalPump ?? -1,
  188. SequenceNumberGeneratedOnPhysicalPump = newTrxSeqNumber,
  189. Finished = true,
  190. };
  191. this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(newTrx));
  192. }
  193. })
  194. .OnEntryAsync(async () => { })
  195. //.Ignore(Trigger.AnyPumpMsgReceived)
  196. .Ignore(Trigger.NozzleReplaced_And_停机)
  197. .Ignore(Trigger.NozzleFuelNumbersIsRunning)
  198. .PermitIf(Trigger.NozzleLifted_And_停机, LogicalDeviceState.FDC_CALLING, () => !isOnRetrievingLastFuelSale)
  199. .PermitIf(Trigger.NozzleLifted_And_开机, LogicalDeviceState.FDC_AUTHORISED, () => true)
  200. .PermitIf(Trigger.AnyPumpMsgHaveNotReceivedForWhile, LogicalDeviceState.FDC_OFFLINE, () => true);
  201. this.stateless.Configure(LogicalDeviceState.FDC_CALLING)
  202. .OnEntryAsync(async () => { })
  203. .OnEntryAsync(async () => { })
  204. //.Ignore(Trigger.AnyPumpMsgReceived)
  205. .Ignore(Trigger.NozzleLifted_And_停机)
  206. .PermitIf(Trigger.NozzleReplaced_And_停机, LogicalDeviceState.FDC_READY, () => true)
  207. .PermitIf(Trigger.NozzleLifted_And_开机, LogicalDeviceState.FDC_AUTHORISED, () => true)
  208. .PermitIf(Trigger.AnyPumpMsgHaveNotReceivedForWhile, LogicalDeviceState.FDC_OFFLINE, () => true);
  209. this.nozzleFuelNumbersIsRunningTrigger =
  210. this.stateless.SetTriggerParameters<FdcTransaction>(Trigger.NozzleFuelNumbersIsRunning);
  211. this.stateless.Configure(LogicalDeviceState.FDC_AUTHORISED)
  212. .OnEntryAsync(() =>
  213. Task.Run(() => this.context.Outgoing.Write(new ReadFuelDataRequest() { Adrs = this.address }))
  214. )
  215. .OnExitAsync(async () => { })
  216. //.Ignore(Trigger.AnyPumpMsgReceived)
  217. .Ignore(Trigger.NozzleLifted_And_开机)
  218. .PermitIf(Trigger.NozzleFuelNumbersIsRunning, LogicalDeviceState.FDC_FUELLING, () => true)
  219. .PermitIf(Trigger.AnyPumpMsgHaveNotReceivedForWhile, LogicalDeviceState.FDC_OFFLINE, () => true);
  220. this.stateless.Configure(LogicalDeviceState.FDC_FUELLING)
  221. .OnEntryFromAsync(this.nozzleFuelNumbersIsRunningTrigger, async (arg) =>
  222. {
  223. this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(arg));
  224. this.context.Outgoing.Write(new ReadFuelDataRequest() { Adrs = this.address });
  225. //for detecting nozzle placed back.
  226. this.context.Outgoing.Write(new ReadPumpStateRequest() { Adrs = this.address });
  227. })
  228. .OnEntryAsync(async () => { })
  229. .OnExitAsync(async () => { })
  230. .PermitReentry(Trigger.NozzleFuelNumbersIsRunning)
  231. //.Ignore(Trigger.AnyPumpMsgReceived)
  232. .Ignore(Trigger.NozzleLifted_And_开机)
  233. .PermitIf(Trigger.NozzleReplaced_And_停机, LogicalDeviceState.FDC_READY, () => true)
  234. .PermitIf(Trigger.NozzleReplaced_And_开机, LogicalDeviceState.FDC_AUTHORISED, () => true)
  235. .PermitIf(Trigger.AnyPumpMsgHaveNotReceivedForWhile, LogicalDeviceState.FDC_OFFLINE, () => true);
  236. }
  237. catch (Exception exxx)
  238. {
  239. logger.LogError("Constructing LanTian_Pump_664_Or_886.StatePumpHanlder exceptioned: " + exxx);
  240. }
  241. }
  242. public void OnFdcServerInit(Dictionary<string, object> parameters)
  243. {
  244. if (parameters.ContainsKey("LastPriceChange"))
  245. {
  246. // nozzle logical id:rawPrice
  247. var lastPriceChanges = parameters["LastPriceChange"] as Dictionary<byte, int>;
  248. foreach (var priceChange in lastPriceChanges)
  249. {
  250. logger.LogInformation("Pump: " + this.pumpId + ", " + "Pump " + this.pumpId + " OnFdcServerInit, load last price change " +
  251. "on logical nozzle: " + priceChange.Key + " with price: " + priceChange.Value);
  252. this.nozzles.First(n => n.LogicalId == priceChange.Key).ExpectingPriceOnFcSide = priceChange.Value;
  253. }
  254. }
  255. /* Load Last sale trx(from db) for void the case of FC accidently disconnect from Pump in fueling,
  256. and may cause a fueling trx gone from FC control */
  257. if (parameters.ContainsKey("LastFuelSaleTrx"))
  258. {
  259. // nozzle logical id:LastSale
  260. var lastFuelSaleTrxes = parameters["LastFuelSaleTrx"] as Dictionary<byte, FuelSaleTransaction>;
  261. foreach (var lastFuelSaleTrx in lastFuelSaleTrxes)
  262. {
  263. logger.LogInformation("Pump: " + this.pumpId + ", OnFdcServerInit, load last volume Totalizer " +
  264. "on logical nozzle: " + lastFuelSaleTrx.Key + " with volume value: " + lastFuelSaleTrx.Value.VolumeTotalizer);
  265. this.nozzles.First(n => n.LogicalId == lastFuelSaleTrx.Key).VolumeTotalizer = lastFuelSaleTrx.Value.VolumeTotalizer;
  266. }
  267. }
  268. this.isOnFdcServerInitCalled = true;
  269. }
  270. public void Init(IContext<byte[], MessageBase> context)
  271. {
  272. this.context = context;
  273. var timeWindowWithActivePollingOutgoing =
  274. this.context.Outgoing as TimeWindowWithActivePollingOutgoing<byte[], MessageBase>;
  275. timeWindowWithActivePollingOutgoing.PollingMsgProducer = () => new ReadPumpStateRequest();
  276. }
  277. public async Task Process(IContext<byte[], MessageBase> context)
  278. {
  279. try
  280. {
  281. if (!isOnFdcServerInitCalled) return;
  282. this.lastLogicalDeviceStateReceivedTime = DateTime.Now;
  283. //await this.stateless.FireAsync(Trigger.AnyPumpMsgReceived);
  284. if (context.Incoming.Message is ReadPumpStateResponse readPumpStateResponse)
  285. {
  286. #region AcquireControl if pump authorize mode is FC_Authorize
  287. if (readPumpStateResponse.ControlState == ReadPumpStateResponse.ControlStateEnum.自控
  288. && this.pumpAuthorizeMode == PumpAuthorizeModeEnum.FC_Authorize)
  289. {
  290. var acquireControlResponse = await this.context.Outgoing.WriteAsync(
  291. new AcquireControlRequest() { Adrs = this.address },
  292. (request, response) => response is AcquireControlResponse rp && rp.Adrs == this.address, 3000).ConfigureAwait(false) as AcquireControlResponse;
  293. if (acquireControlResponse == null)
  294. {
  295. logger.LogInformation("Pump: " + this.pumpId + ", " + " AcquireControlResponse first time timedout, will send 2nd time...");
  296. acquireControlResponse = await this.context.Outgoing.WriteAsync(
  297. new AcquireControlRequest() { Adrs = this.address },
  298. (request, response) => response is AcquireControlResponse rp && rp.Adrs == this.address, 3000).ConfigureAwait(false) as AcquireControlResponse;
  299. if (acquireControlResponse == null)
  300. logger.LogInformation("Pump: " + this.pumpId + ", " + " AcquireControlResponse 2nd time timedout, will ignore this error, then the pump may have wrong authorize mode");
  301. }
  302. }
  303. #endregion
  304. if (readPumpStateResponse.NozzleState == ReadPumpStateResponse.NozzleStateEnum.提枪)
  305. {
  306. if (readPumpStateResponse.StartOrStopState == ReadPumpStateResponse.StartOrStopStateEnum.开机)
  307. await this.stateless.FireAsync(Trigger.NozzleLifted_And_开机);
  308. else
  309. await this.stateless.FireAsync(Trigger.NozzleLifted_And_停机);
  310. }
  311. else
  312. {
  313. if (readPumpStateResponse.StartOrStopState == ReadPumpStateResponse.StartOrStopStateEnum.开机)
  314. await this.stateless.FireAsync(Trigger.NozzleReplaced_And_开机);
  315. else
  316. await this.stateless.FireAsync(Trigger.NozzleReplaced_And_停机);
  317. }
  318. }
  319. else if (context.Incoming.Message is ReadFuelDataResponse readFuelDataResponse)
  320. {
  321. //await this.stateless.FireAsync(Trigger.NozzleFuelNumbersIsRunning);
  322. await this.stateless.FireAsync(this.nozzleFuelNumbersIsRunningTrigger,
  323. new FdcTransaction()
  324. {
  325. // 只有一把枪
  326. Nozzle = this.nozzles.First(),
  327. Amount = readFuelDataResponse.Amount,
  328. Volumn = readFuelDataResponse.Volume,
  329. Price = this.nozzles.First().RealPriceOnPhysicalPump ?? -1,
  330. Finished = false,
  331. });
  332. }
  333. else if (context.Incoming.Message is ReadPriceResponse readPriceResponse)
  334. {
  335. if (!this.nozzles.First().RealPriceOnPhysicalPump.HasValue ||
  336. this.nozzles.First().RealPriceOnPhysicalPump.Value != readPriceResponse.Price)
  337. logger.LogInformation("Pump " + this.pumpId + ", detected the pump side price changed from: "
  338. + (this.nozzles.First().RealPriceOnPhysicalPump ?? -1) + " to: " + readPriceResponse.Price + ", will updating to local");
  339. this.nozzles.First().RealPriceOnPhysicalPump = readPriceResponse.Price;
  340. }
  341. }
  342. catch (Exception eee)
  343. {
  344. logger.LogInformation("Process(...) exceptioned: " + eee);
  345. }
  346. }
  347. /// <summary>
  348. /// no decimal point value.
  349. /// </summary>
  350. /// <returns>MoneyTotalizer:VolumnTotalizer</returns>
  351. public async Task<System.Tuple<int, int>> QueryTotalizerAsync(byte logicalNozzleId)
  352. {
  353. var readTotalizerResponse = await this.context.Outgoing.WriteAsync(new ReadTotalizerRequest() { Adrs = this.address },
  354. (_, testResponse) => testResponse is ReadTotalizerResponse rp && rp.Adrs == this.address, 3000) as ReadTotalizerResponse;
  355. if (readTotalizerResponse == null) return new System.Tuple<int, int>(-1, -1);
  356. return new System.Tuple<int, int>((int)readTotalizerResponse.AmountTotalizer, (int)readTotalizerResponse.VolumeTotalizer);
  357. }
  358. public async Task<bool> SuspendFuellingAsync()
  359. {
  360. logger.LogDebug("Pump " + this.pumpId + ", SuspendFuellingAsync...");
  361. var suspendFuelResponse = await this.context.Outgoing.WriteAsync(new SuspendFuelRequest() { Adrs = this.address },
  362. (_, testResponse) => testResponse is SuspendFuelResponse rp && rp.Adrs == this.address, 3000) as SuspendFuelResponse;
  363. var result = suspendFuelResponse != null;
  364. logger.LogDebug("Pump " + this.pumpId + ", SuspendFuellingAsync result: " + result);
  365. return result;
  366. }
  367. public async Task<bool> ResumeFuellingAsync()
  368. {
  369. logger.LogDebug("Pump " + this.pumpId + ", ResumeFuellingAsync...");
  370. var resumeFuelResponse = await this.context.Outgoing.WriteAsync(new ResumeFuelRequest() { Adrs = this.address },
  371. (_, testResponse) => testResponse is ResumeFuelResponse rp && rp.Adrs == this.address, 3000) as ResumeFuelResponse;
  372. var result = resumeFuelResponse != null;
  373. logger.LogDebug("Pump " + this.pumpId + ", ResumeFuellingAsync result: " + result);
  374. return result;
  375. }
  376. public async Task<bool> ChangeFuelPriceAsync(int newPriceWithoutDecimalPoint, byte logicalNozzleId)
  377. {
  378. logger.LogInformation("Pump: " + this.pumpId + ", " + " ChangeFuelPriceAsync, new price: " + newPriceWithoutDecimalPoint);
  379. var readPriceResponse = await this.context.Outgoing.WriteAsync(new ReadPriceRequest() { Adrs = this.address },
  380. (request, response) => response is ReadPriceResponse rp && rp.Adrs == this.address, 3000).ConfigureAwait(false) as ReadPriceResponse;
  381. if (readPriceResponse == null)
  382. {
  383. logger.LogInformation("Pump: " + this.pumpId + ", " + " Reading pump side price timedout, will ignore this error and won't send a ChangePriceRequest here but may need a manual Price change if find price discrepancy");
  384. return false;
  385. }
  386. this.nozzles.First().RealPriceOnPhysicalPump = readPriceResponse.Price;
  387. if (readPriceResponse.Price != newPriceWithoutDecimalPoint)
  388. {
  389. //logger.LogInformation("Pump: " + this.pumpId + ", "
  390. // + " Read the pump side price(without decimal points): " + readPriceResponse.Price + " which NOT Equals with the price as FC expect, will start a change price internally...");
  391. var changePriceResponse = await this.context.Outgoing.WriteAsync(
  392. new ChangePriceRequest(newPriceWithoutDecimalPoint, this.pumpModel == PumpModelEnum.Model_664 ? (byte)2 : (byte)3) { Adrs = this.address },
  393. (request, response) => response is ChangePriceResponse rp && rp.Adrs == this.address, 3000).ConfigureAwait(false) as ChangePriceResponse;
  394. if (changePriceResponse == null || !changePriceResponse.Succeed)
  395. {
  396. logger.LogInformation("Pump: " + this.pumpId + ", " + " change price timedout or failed, may need a manual price change later if find price discrepancy");
  397. return false;
  398. }
  399. var doubleConfirm_readPriceResponse = await this.context.Outgoing.WriteAsync(new ReadPriceRequest() { Adrs = this.address },
  400. (request, response) => response is ReadPriceResponse rp && rp.Adrs == this.address, 3000).ConfigureAwait(false) as ReadPriceResponse;
  401. if (doubleConfirm_readPriceResponse == null)
  402. {
  403. logger.LogInformation("Pump: " + this.pumpId + ", " + " Double confirm change price timedout, so FC can't sure if the new price applied, or a manual Price change is needed if find price discrepancy");
  404. return false;
  405. }
  406. else if (doubleConfirm_readPriceResponse.Price == newPriceWithoutDecimalPoint)
  407. {
  408. this.nozzles.First().RealPriceOnPhysicalPump = doubleConfirm_readPriceResponse.Price;
  409. logger.LogInformation("Pump: " + this.pumpId + ", " + " Double confirm succeed for new FC price(without decimal points): " + newPriceWithoutDecimalPoint + " have been applied to pump.");
  410. return true;
  411. }
  412. else
  413. {
  414. logger.LogInformation("Pump: " + this.pumpId + ", " + " Double confirm failed for new FC price(without decimal points): " + newPriceWithoutDecimalPoint + " failed applying to pump as pump side still report its price(without decimal point): " + doubleConfirm_readPriceResponse.Price + ", a manual Change Price is needed");
  415. return false;
  416. }
  417. }
  418. else
  419. {
  420. logger.LogInformation("Pump: " + this.pumpId + ", " + " Read the pump side price(without decimal points): " + readPriceResponse.Price + ", no need to align with FC(FC has no expecting price)");
  421. return true;
  422. }
  423. }
  424. public async Task<bool> AuthorizeAsync(byte logicalNozzleId)
  425. {
  426. logger.LogDebug("Pump " + this.pumpId + ", AuthorizeAsync...");
  427. var openResponse = await this.context.Outgoing.WriteAsync(new OpenRequest() { Adrs = this.address },
  428. (_, testResponse) => testResponse is OpenResponse rp && rp.Adrs == this.address, 3000) as OpenResponse;
  429. if (openResponse == null)
  430. {
  431. logger.LogInformation("Pump " + this.pumpId + ", AuthorizeAsync failed due to timedout");
  432. return false;
  433. }
  434. logger.LogInformation("Pump " + this.pumpId + ", AuthorizeAsync result: " + openResponse.Succeed);
  435. return openResponse.Succeed;
  436. }
  437. public async Task<bool> UnAuthorizeAsync(byte logicalNozzleId)
  438. {
  439. return false;
  440. }
  441. public async Task<bool> AuthorizeWithAmountAsync(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId)
  442. {
  443. logger.LogDebug("Pump " + this.pumpId + ", AuthorizeWithAmountAsync(amt: " + moneyAmountWithoutDecimalPoint + ")...");
  444. var presetAmountResponse = await this.context.Outgoing.WriteAsync(
  445. new PresetAmountRequest(moneyAmountWithoutDecimalPoint,
  446. this.pumpModel == PumpModelEnum.Model_664 ? (byte)3 : (byte)4)
  447. { Adrs = this.address },
  448. (_, testResponse) => testResponse is PresetAmountResponse rp && rp.Adrs == this.address, 3000) as PresetAmountResponse;
  449. if (presetAmountResponse == null)
  450. {
  451. logger.LogInformation("Pump " + this.pumpId + ", AuthorizeWithAmountAsync failed due to PresetAmountRequest timedout");
  452. return false;
  453. }
  454. logger.LogInformation("Pump " + this.pumpId + ", AuthorizeWithAmountAsync PresetAmountRequest succeed, will AuthorizePump...");
  455. return await this.AuthorizeAsync(logicalNozzleId);
  456. }
  457. public async Task<bool> AuthorizeWithVolumeAsync(int volumnWithoutDecimalPoint, byte logicalNozzleId)
  458. {
  459. logger.LogDebug("Pump " + this.pumpId + ", AuthorizeWithVolumeAsync(amt: " + volumnWithoutDecimalPoint + ")...");
  460. var presetVolumeResponse = await this.context.Outgoing.WriteAsync(
  461. new PresetVolumeRequest(volumnWithoutDecimalPoint,
  462. this.pumpModel == PumpModelEnum.Model_664 ? (byte)3 : (byte)4)
  463. { Adrs = this.address },
  464. (_, testResponse) => testResponse is PresetVolumeResponse rp && rp.Adrs == this.address, 3000) as PresetVolumeResponse;
  465. if (presetVolumeResponse == null)
  466. {
  467. logger.LogInformation("Pump " + this.pumpId + ", AuthorizeWithVolumeAsync failed due to PresetVolumeRequest timedout");
  468. return false;
  469. }
  470. logger.LogInformation("Pump " + this.pumpId + ", AuthorizeWithVolumeAsync PresetVolumeRequest succeed, will AuthorizePump...");
  471. return await this.AuthorizeAsync(logicalNozzleId);
  472. }
  473. public Task<bool> FuelingRoundUpByAmountAsync(int amount)
  474. {
  475. return Task.FromResult(false);
  476. }
  477. public Task<bool> FuelingRoundUpByVolumeAsync(int volume)
  478. {
  479. return Task.FromResult(false);
  480. }
  481. }
  482. }