PumpHandler.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. using Edge.Core.IndustryStandardInterface.Pump;
  2. using Edge.Core.Processor;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. using System.Xml;
  11. using System.Xml.Serialization;
  12. using Wayne.FDCPOSLibrary;
  13. namespace Bogus_Pump
  14. {
  15. public class PumpHandler : IFdcPumpController, IDisposable
  16. {
  17. private bool enableFuelSimulation;
  18. private int pumpId;
  19. private int pumpPhysicalId;
  20. private IEnumerable<LogicalNozzle> nozzles;
  21. private LogicalDeviceState currentState = LogicalDeviceState.FDC_READY;
  22. public string Name => "Bogus_Pump handler";
  23. public int PumpId => this.pumpId;
  24. public int PumpPhysicalId => this.pumpPhysicalId;
  25. public IEnumerable<LogicalNozzle> Nozzles => this.nozzles;
  26. public int AmountDecimalDigits => 2;
  27. public int VolumeDecimalDigits => 2;
  28. public int PriceDecimalDigits => 2;
  29. public int VolumeTotalizerDecimalDigits => 2;
  30. public event EventHandler<FdcPumpControllerOnStateChangeEventArg> OnStateChange;
  31. public event EventHandler<FdcTransactionDoneEventArg> OnCurrentFuellingStatusChange;
  32. private System.Timers.Timer autoCallingTimer;
  33. /// <summary>
  34. ///
  35. /// </summary>
  36. /// <param name="pumpId"></param>
  37. /// <param name="nozzleCount"></param>
  38. /// <param name="enableFuelSimulation">set to true to enable auto calling, fueling, and idle. otherwise, nozzle keeps idle.</param>
  39. public PumpHandler(int pumpId, byte nozzleCount, bool enableFuelSimulation)
  40. {
  41. this.enableFuelSimulation = enableFuelSimulation;
  42. this.pumpId = pumpId;
  43. this.pumpPhysicalId = pumpId;
  44. this.nozzles = Enumerable.Range(1, nozzleCount).Select(i => new LogicalNozzle(pumpId, (byte)i, (byte)i, 831)).ToList();
  45. if (this.enableFuelSimulation)
  46. {
  47. this.autoCallingTimer = new System.Timers.Timer(new Random().Next(60, 200) * 100);
  48. this.autoCallingTimer.Elapsed += (_, __) =>
  49. {
  50. if (this.currentState == LogicalDeviceState.FDC_AUTHORISED
  51. || this.currentState == LogicalDeviceState.FDC_FUELLING)
  52. {
  53. return;
  54. }
  55. //now simply only allow first nozzle
  56. var operatingNozzle = this.nozzles.FirstOrDefault();
  57. if (this.currentState == LogicalDeviceState.FDC_CALLING)
  58. {
  59. this.currentState = LogicalDeviceState.FDC_READY;
  60. this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(this.currentState, operatingNozzle));
  61. return;
  62. }
  63. if (this.currentState == LogicalDeviceState.FDC_READY)
  64. {
  65. this.currentState = LogicalDeviceState.FDC_CALLING;
  66. this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(this.currentState, operatingNozzle));
  67. }
  68. };
  69. }
  70. }
  71. public async Task<bool> AuthorizeAsync(byte logicalNozzleId)
  72. {
  73. //if (this.enableFuelSimulation)
  74. // return false;
  75. if (this.currentState == LogicalDeviceState.FDC_AUTHORISED
  76. || this.currentState == LogicalDeviceState.FDC_FUELLING)
  77. return false;
  78. LogicalNozzle n = this.nozzles.FirstOrDefault(n => n.LogicalId == logicalNozzleId);
  79. if (n == null) return await Task.FromResult(false);
  80. await Task.Delay(1000);
  81. this.currentState = LogicalDeviceState.FDC_AUTHORISED;
  82. this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(this.currentState, n));
  83. await Task.Delay(1000);
  84. this.currentState = LogicalDeviceState.FDC_FUELLING;
  85. this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(this.currentState, n));
  86. await Task.Delay(300);
  87. _ = Task.Run(() =>
  88. {
  89. int amt = 0;
  90. int vol = 0;
  91. int seqNumber = (int)(DateTime.Now.Subtract(new DateTime(2021, 1, 1)).TotalSeconds);
  92. const int FireMaxTimes = 12;
  93. int firedTimes = 0;
  94. while (true)
  95. {
  96. amt += new Random().Next(500, 1000);
  97. vol += new Random().Next(100, 300);
  98. this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction()
  99. {
  100. Amount = amt,
  101. Volumn = vol,
  102. Nozzle = n,
  103. Price = n.RealPriceOnPhysicalPump ?? -1,
  104. SequenceNumberGeneratedOnPhysicalPump = seqNumber,
  105. Finished = false
  106. }));
  107. Thread.Sleep(1000);
  108. firedTimes++;
  109. if (firedTimes > FireMaxTimes)
  110. {
  111. this.currentState = LogicalDeviceState.FDC_READY;
  112. this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(this.currentState, n));
  113. this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction()
  114. {
  115. Amount = amt,
  116. Volumn = vol,
  117. Nozzle = n,
  118. Price = n.RealPriceOnPhysicalPump ?? -1,
  119. SequenceNumberGeneratedOnPhysicalPump = seqNumber,
  120. AmountTotalizer = new Random().Next(9999, 99999999),
  121. VolumeTotalizer = new Random().Next(1000, 11999999),
  122. Finished = true
  123. }));
  124. break;
  125. }
  126. }
  127. });
  128. return await Task.FromResult(true);
  129. }
  130. public Task<bool> AuthorizeWithAmountAsync(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId)
  131. {
  132. return this.AuthorizeAsync(logicalNozzleId);
  133. }
  134. public Task<bool> AuthorizeWithVolumeAsync(int volumnWithoutDecimalPoint, byte logicalNozzleId)
  135. {
  136. return this.AuthorizeAsync(logicalNozzleId);
  137. }
  138. public Task<bool> ChangeFuelPriceAsync(int newPriceWithoutDecimalPoint, byte logicalNozzleId)
  139. {
  140. if (this.enableFuelSimulation)
  141. return Task.FromResult(false);
  142. var targetNozzle = this.nozzles.FirstOrDefault(n => n.LogicalId == logicalNozzleId);
  143. if (targetNozzle != null)
  144. {
  145. targetNozzle.RealPriceOnPhysicalPump = newPriceWithoutDecimalPoint;
  146. targetNozzle.ExpectingPriceOnFcSide = newPriceWithoutDecimalPoint;
  147. return Task.FromResult(true);
  148. }
  149. else
  150. return Task.FromResult(false);
  151. }
  152. public Task<bool> FuelingRoundUpByAmountAsync(int amount)
  153. {
  154. return Task.FromResult(true);
  155. }
  156. public Task<bool> FuelingRoundUpByVolumeAsync(int volume)
  157. {
  158. return Task.FromResult(true);
  159. }
  160. public Task<bool> LockNozzleAsync(byte logicalNozzleId)
  161. {
  162. return Task.FromResult(true);
  163. }
  164. public void OnFdcServerInit(Dictionary<string, object> parameters)
  165. {
  166. if (parameters.ContainsKey("LastPriceChange"))
  167. {
  168. // nozzle logical id:rawPrice
  169. var lastPriceChanges = parameters["LastPriceChange"] as Dictionary<byte, int>;
  170. foreach (var priceChange in lastPriceChanges)
  171. {
  172. var targetNozzle = this.nozzles.FirstOrDefault(n => n.LogicalId == priceChange.Key);
  173. if (targetNozzle != null)
  174. {
  175. targetNozzle.RealPriceOnPhysicalPump = priceChange.Value;
  176. targetNozzle.ExpectingPriceOnFcSide = priceChange.Value;
  177. }
  178. }
  179. }
  180. this.autoCallingTimer?.Start();
  181. }
  182. public Task<LogicalDeviceState> QueryStatusAsync()
  183. {
  184. return Task.FromResult(this.currentState);
  185. }
  186. public Task<Tuple<int, int>> QueryTotalizerAsync(byte logicalNozzleId)
  187. {
  188. if (this.enableFuelSimulation)
  189. Task.FromResult(new Tuple<int, int>(-1, -1));
  190. return Task.FromResult(
  191. new Tuple<int, int>(
  192. new Random().Next(100000, 999999),
  193. new Random().Next(50000, 99999)));
  194. }
  195. public Task<bool> ResumeFuellingAsync()
  196. {
  197. return Task.FromResult(true);
  198. }
  199. public Task<bool> SuspendFuellingAsync()
  200. {
  201. return Task.FromResult(true);
  202. }
  203. public Task<bool> UnAuthorizeAsync(byte logicalNozzleId)
  204. {
  205. return Task.FromResult(true);
  206. }
  207. public Task<bool> UnlockNozzleAsync(byte logicalNozzleId)
  208. {
  209. return Task.FromResult(true);
  210. }
  211. public void Dispose()
  212. {
  213. this.autoCallingTimer?.Dispose();
  214. }
  215. internal void DrivenPumpStateTo(byte? logicalNozzleId, LogicalDeviceState pumpNewState,
  216. int? amountWithoutDecimalPoint, int? volumeWithoutDecimalPoint, int? priceWithoutDecimalPoint)
  217. {
  218. var targetNozzle = this.nozzles?.FirstOrDefault(n => n.LogicalId == logicalNozzleId);
  219. if (pumpNewState == LogicalDeviceState.FDC_FUELLING)
  220. {
  221. this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction()
  222. {
  223. Amount = amountWithoutDecimalPoint ?? 0,
  224. Volumn = volumeWithoutDecimalPoint ?? 0,
  225. Nozzle = targetNozzle,
  226. Price = priceWithoutDecimalPoint ?? 0,
  227. //SequenceNumberGeneratedOnPhysicalPump = seqNumber,
  228. Finished = false
  229. }));
  230. }
  231. if (this.currentState == pumpNewState) return;
  232. this.currentState = pumpNewState;
  233. this.OnStateChange?.Invoke(this,
  234. new FdcPumpControllerOnStateChangeEventArg(this.currentState, targetNozzle));
  235. }
  236. internal void DrivenPumpRaiseNewTrx(byte? logicalNozzleId,
  237. int amountWithoutDecimalPoint, int volumeWithoutDecimalPoint, int priceWithoutDecimalPoint, int trxSeqNumber)
  238. {
  239. var targetNozzle = this.nozzles?.FirstOrDefault(n => n.LogicalId == logicalNozzleId);
  240. this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction()
  241. {
  242. Amount = amountWithoutDecimalPoint,
  243. Volumn = volumeWithoutDecimalPoint,
  244. Nozzle = targetNozzle,
  245. Price = priceWithoutDecimalPoint,
  246. SequenceNumberGeneratedOnPhysicalPump = trxSeqNumber,
  247. Finished = true
  248. }));
  249. }
  250. }
  251. }