CardTrxMonitorApp.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. using Edge.Core.Processor;
  2. using Edge.Core.IndustryStandardInterface.Pump;
  3. using Dfs.WayneChina.CardTrxMonitor.Models;
  4. using System;
  5. using System.Linq;
  6. using System.Collections.Generic;
  7. using System.Text;
  8. using Wayne.FDCPOSLibrary;
  9. using System.Threading.Tasks;
  10. using System.Threading;
  11. using Dfs.WayneChina.CardTrxManager;
  12. using Dfs.WayneChina.CardTrxManager.TrxSubmitter;
  13. namespace Dfs.WayneChina.CardTrxMonitor
  14. {
  15. /// <summary>
  16. /// Entity that pulls a finished IC card transaction from MySQL and publishes it to FdcServer.
  17. /// </summary>
  18. public class CardTrxMonitorApp : IAppProcessor, IFdcPumpController
  19. {
  20. #region Properties
  21. public string Name => "CardTrxMonitor";
  22. public int PumpId { get; private set; }
  23. public byte NozzleId { get; private set; }
  24. //This is a fake pump.
  25. public int PumpPhysicalId => 0;
  26. private List<LogicalNozzle> nozzles = new List<LogicalNozzle>();
  27. public IEnumerable<LogicalNozzle> Nozzles => nozzles;
  28. //China domestic standard
  29. public int AmountDecimalDigits => 2;
  30. public int VolumeDecimalDigits => 2;
  31. public int PriceDecimalDigits => 2;
  32. public int VolumeTotalizerDecimalDigits => 2;
  33. public string MetaConfigName { get; set; }
  34. #endregion
  35. #region Fields
  36. private System.Timers.Timer _timer;
  37. private int scanInterval;
  38. private int siteNozzleNo;
  39. private int bindingBarcode;
  40. private CloudCredential cloudCredential;
  41. private TrxSubmitter submitter;
  42. #endregion
  43. #region Logger
  44. NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("CardTrxMonitor");
  45. #endregion
  46. #region Constructor
  47. public CardTrxMonitorApp(int pumpId, int nozzleId, int siteNozzleNo, int barcode, int scanInterval,
  48. string username, string password, string authServiceBaseUrl, string transactionServiceBaseUrl, string deviceSN)
  49. {
  50. PumpId = pumpId;
  51. NozzleId = Convert.ToByte(nozzleId);
  52. this.siteNozzleNo = siteNozzleNo;
  53. bindingBarcode = barcode;
  54. nozzles.Add(new LogicalNozzle(pumpId, 1, NozzleId, null));
  55. this.scanInterval = scanInterval;
  56. cloudCredential = new CloudCredential
  57. {
  58. UserName = username,
  59. Password = password,
  60. AuthServiceBaseUrl = authServiceBaseUrl,
  61. TransactionServiceBaseUrl = transactionServiceBaseUrl,
  62. DeviceSN = deviceSN
  63. };
  64. submitter = new TrxSubmitter(PumpId, cloudCredential);
  65. }
  66. #endregion
  67. #region IApplication implementation
  68. public void Init(IEnumerable<IProcessor> processors)
  69. {
  70. }
  71. public Task<bool> Start()
  72. {
  73. _timer = new System.Timers.Timer();
  74. _timer.Interval = scanInterval * 1000;
  75. _timer.Elapsed += TimeElapsed;
  76. _timer.Start();
  77. return Task.FromResult(true);
  78. }
  79. private void TimeElapsed(object sender, System.Timers.ElapsedEventArgs e)
  80. {
  81. Log("Timer reset...");
  82. try
  83. {
  84. FindTransaction();
  85. }
  86. catch (Exception ex)
  87. {
  88. Log($"{ex}");
  89. }
  90. }
  91. private void Log(string message)
  92. {
  93. logger.Info($"Monitor{PumpId}, {message}");
  94. }
  95. private void FindTransaction()
  96. {
  97. Log("start to check db...");
  98. try
  99. {
  100. using (var _context = new SpsDbContext())
  101. {
  102. var cardTrx = _context.TCardtrx.FirstOrDefault(t => t.PumpNo == PumpId);
  103. if (cardTrx != null && IsTrdTypeAllowed(cardTrx.TrdType) && cardTrx.Mon != 0)
  104. {
  105. //Before processing is done against the found transaction, stop the timer in case that
  106. //it takes a while and the timer is triggerred for a second time and exception raised.
  107. _timer.Stop();
  108. if (cardTrx.TrdType == 6)
  109. {
  110. Log("Non-card fuelling");
  111. }
  112. else if (cardTrx.TrdType == 1)
  113. {
  114. Log("Gray card transaction, remove it otherwise it will block further transactions");
  115. RemoveTransaction(cardTrx, _context);
  116. }
  117. //103, Operator card
  118. if (cardTrx.PaymodeId == 103)
  119. {
  120. Log($"Found finished IC card transaction: Pump: {cardTrx.PumpNo}, Volume: {cardTrx.Vol}, Amount: {cardTrx.Mon}");
  121. FdcTransaction fdcTransaction = new FdcTransaction();
  122. fdcTransaction.Amount = cardTrx.Mon.Value;
  123. fdcTransaction.Volumn = Convert.ToInt32(cardTrx.Vol.Value);
  124. fdcTransaction.VolumeTotalizer = Convert.ToInt32(cardTrx.EndPumpId.Value);
  125. fdcTransaction.Price = Convert.ToInt32(cardTrx.Prc.Value);
  126. fdcTransaction.SequenceNumberGeneratedOnPhysicalPump = Convert.ToInt32(cardTrx.Gid);
  127. fdcTransaction.Nozzle = new LogicalNozzle(cardTrx.PumpNo, 0, Convert.ToByte(NozzleId), 0);
  128. fdcTransaction.Finished = true;
  129. OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(fdcTransaction));
  130. RemoveTransaction(cardTrx, _context);
  131. }
  132. else if (cardTrx.PaymodeId == 100) //100, Customer card 定位卡问题
  133. {
  134. Log($"Found customer card transaction: Pump: {cardTrx.PumpNo}, Volume: {cardTrx.Vol}, Amount: {cardTrx.Mon}");
  135. var submitResult = Task.Run(async () =>
  136. {
  137. var result = await SubmitTrxAsync(cardTrx, bindingBarcode);
  138. return result;
  139. });
  140. Log($"submit transaction, result: {submitResult.Result}");
  141. RemoveTransaction(cardTrx, _context);
  142. }
  143. else
  144. {
  145. //105:
  146. //108:
  147. }
  148. }
  149. else if (cardTrx != null && cardTrx.Mon == 0)
  150. {
  151. Log($"Zero amount transaction, Pump No: {cardTrx.PumpNo}, SeqNo: {cardTrx.SeqNo}, delete it!");
  152. RemoveTransaction(cardTrx, _context);
  153. }
  154. else if (cardTrx != null && cardTrx.Gid != 0)
  155. {
  156. Log($"Trd Type: {cardTrx?.TrdType}");
  157. RemoveTransaction(cardTrx, _context);
  158. }
  159. //Reacitivate the timer.
  160. _timer.Start();
  161. }
  162. }
  163. catch (Exception ex)
  164. {
  165. Log($"Database operation: {ex}");
  166. }
  167. }
  168. /// <summary>
  169. ///00:正常卡交易
  170. ///01:灰卡交易
  171. ///02:解灰交易(解灰后即为正常交易)
  172. ///04:现金后台授权交易
  173. ///05:撤消授权卡交易
  174. ///06:非卡交易
  175. ///07:撤消授权非卡交易
  176. ///08:油价下载记录
  177. /// </summary>
  178. /// <param name="trdType"></param>
  179. /// <returns></returns>
  180. private bool IsTrdTypeAllowed(byte trdType)
  181. {
  182. if (trdType == 0 || trdType == 2 || trdType == 6)
  183. return true;
  184. return false;
  185. }
  186. private void RemoveTransaction(TCardtrx cardTrx, SpsDbContext context)
  187. {
  188. try
  189. {
  190. context.Remove(cardTrx);
  191. context.SaveChanges();
  192. }
  193. catch (Exception ex)
  194. {
  195. Log($"Exception in removing trx: {ex}");
  196. }
  197. }
  198. private async Task<bool> SubmitTrxAsync(TCardtrx cardTrx, int barcode)
  199. {
  200. var clientTrxInfo = new ClientTrxInfo
  201. {
  202. CardNo = cardTrx.CardNo,
  203. CurrentCardBalance = Convert.ToDecimal(cardTrx.CardBal.Value) / 100,
  204. UnitPrice = Convert.ToDecimal(cardTrx.Prc) / 100,
  205. Amount = Convert.ToDecimal(cardTrx.Mon) / 100,
  206. PayAmount = Convert.ToDecimal(cardTrx.RealMon) / 100,
  207. Volume = Convert.ToDecimal(cardTrx.Vol) / 100,
  208. PumpId = cardTrx.PumpNo,
  209. NozzleId = Convert.ToByte(cardTrx.NozNo),
  210. SiteNozzleNo = siteNozzleNo,
  211. Barcode = barcode,
  212. FuelingStartTime = cardTrx.Ttctime,
  213. FuelingFinishedTime = cardTrx.TtctimeEnd.Value,
  214. SeqNo = Convert.ToInt32(cardTrx.SeqNo)
  215. };
  216. var result = await submitter.SubmitTrxAsync(clientTrxInfo);
  217. return result;
  218. }
  219. public Task<bool> Stop()
  220. {
  221. if (_timer != null)
  222. {
  223. _timer.Dispose();
  224. }
  225. return Task.FromResult(true);
  226. }
  227. #endregion
  228. #region Event handlers
  229. public event EventHandler<FdcPumpControllerOnStateChangeEventArg> OnStateChange;
  230. public event EventHandler<FdcTransactionDoneEventArg> OnCurrentFuellingStatusChange;
  231. #endregion
  232. #region Methods, not useful
  233. public bool Authorize(byte logicalNozzleId)
  234. {
  235. return false;
  236. }
  237. public bool AuthorizeWithAmount(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId)
  238. {
  239. return false;
  240. }
  241. public bool AuthorizeWithVolumn(int volumnWithoutDecimalPoint, byte logicalNozzleId)
  242. {
  243. return false;
  244. }
  245. public bool ChangeFuelPrice(int newPriceWithoutDecimalPoint, byte logicalNozzleId)
  246. {
  247. return false;
  248. }
  249. public bool FuelingRoundUpByAmount(int amount)
  250. {
  251. return false;
  252. }
  253. public bool FuelingRoundUpByVolumn(int volume)
  254. {
  255. return false;
  256. }
  257. public void OnFdcServerInit(Dictionary<string, object> parameters)
  258. {
  259. //the parameters don't make any sense for this fake pump handler.
  260. }
  261. public LogicalDeviceState QueryStatus()
  262. {
  263. return LogicalDeviceState.FDC_READY;
  264. }
  265. public Tuple<int, int> QueryTotalizer(byte logicalNozzleId)
  266. {
  267. return new Tuple<int, int>(-1, -1);
  268. }
  269. public bool ResumeFuelling()
  270. {
  271. return false;
  272. }
  273. public bool SuspendFuelling()
  274. {
  275. return false;
  276. }
  277. public bool UnAuthorize(byte logicalNozzleId)
  278. {
  279. return false;
  280. }
  281. public Task<LogicalDeviceState> QueryStatusAsync()
  282. {
  283. throw new NotImplementedException();
  284. }
  285. public Task<Tuple<int, int>> QueryTotalizerAsync(byte logicalNozzleId)
  286. {
  287. return Task.FromResult(new Tuple<int, int>(-1, -1));
  288. }
  289. public Task<bool> SuspendFuellingAsync()
  290. {
  291. throw new NotImplementedException();
  292. }
  293. public Task<bool> ResumeFuellingAsync()
  294. {
  295. throw new NotImplementedException();
  296. }
  297. public Task<bool> ChangeFuelPriceAsync(int newPriceWithoutDecimalPoint, byte logicalNozzleId)
  298. {
  299. throw new NotImplementedException();
  300. }
  301. public Task<bool> AuthorizeAsync(byte logicalNozzleId)
  302. {
  303. throw new NotImplementedException();
  304. }
  305. public Task<bool> UnAuthorizeAsync(byte logicalNozzleId)
  306. {
  307. throw new NotImplementedException();
  308. }
  309. public Task<bool> AuthorizeWithAmountAsync(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId)
  310. {
  311. throw new NotImplementedException();
  312. }
  313. public Task<bool> AuthorizeWithVolumeAsync(int volumnWithoutDecimalPoint, byte logicalNozzleId)
  314. {
  315. throw new NotImplementedException();
  316. }
  317. public Task<bool> FuelingRoundUpByAmountAsync(int amount)
  318. {
  319. throw new NotImplementedException();
  320. }
  321. public Task<bool> FuelingRoundUpByVolumeAsync(int volume)
  322. {
  323. throw new NotImplementedException();
  324. }
  325. public async Task<bool> LockNozzleAsync(byte logicalNozzleId)
  326. {
  327. return false;
  328. }
  329. public async Task<bool> UnlockNozzleAsync(byte logicalNozzleId)
  330. {
  331. return false;
  332. }
  333. #endregion
  334. }
  335. }