FuelingManager.cs 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145
  1. using Applications.FDC;
  2. using Edge.Core.Database;
  3. using Edge.Core.Database.Models;
  4. using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;
  5. using Dfs.WayneChina.HengshanFPos.FPosDbManager;
  6. using Dfs.WayneChina.HengshanTerminalWrapper.MessageEntity.Base;
  7. using Dfs.WayneChina.HengshanTerminalWrapper.MessageEntity.Outgoing;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11. using System.Text;
  12. using Wayne.FDCPOSLibrary;
  13. namespace Dfs.WayneChina.HengshanPos
  14. {
  15. /// <summary>
  16. /// Manages fueling transaction with Pump and IC terminal.
  17. /// </summary>
  18. public class FuelingManager
  19. {
  20. #region Fields
  21. private SqliteDbContext fdcDbContext;
  22. private FPosDbManager fPosDbManager;
  23. private FdcServerHostApp fdcServer;
  24. private object syncObj = new object();
  25. private int tempAuthAmount;
  26. private int tempAuthVolume;
  27. private CampaignEngine campaignEngine;
  28. private Fueling activeFueling;
  29. private object syncObjAF = new object();
  30. private bool? pumpUnauthorized = null;
  31. #endregion
  32. #region Logger
  33. static NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("FuelingManager");
  34. #endregion
  35. #region Constructor
  36. public FuelingManager(int pumpId, IFdcPumpController fdcPump, FdcServerHostApp fdcServer,
  37. SqliteDbContext fdcDbContext, FPosDbManager fPosDbManager, CampaignEngine campaignEngine)
  38. {
  39. PumpId = pumpId;
  40. FdcPump = fdcPump;
  41. this.fdcDbContext = new SqliteDbContext(); //fdcDbContext;
  42. this.fPosDbManager = fPosDbManager;
  43. this.fdcServer = fdcServer;
  44. this.campaignEngine = campaignEngine;
  45. }
  46. #endregion
  47. #region Properties
  48. public int PumpId { get; }
  49. public IFdcPumpController FdcPump { get; }
  50. public Fueling ActiveFueling
  51. {
  52. get
  53. {
  54. //lock (syncObjAF)
  55. {
  56. return activeFueling;
  57. }
  58. }
  59. set
  60. {
  61. //lock (syncObjAF)
  62. {
  63. activeFueling = value;
  64. }
  65. }
  66. }
  67. public Fueling LastFueling { get; private set; }
  68. #endregion
  69. #region Nozzle status
  70. public GetNozzleStatusResponse GetNozzleStatus()
  71. {
  72. DebugLog($"fueling manager {PumpId} handles nozzle status\n");
  73. var response = new GetNozzleStatusResponse();
  74. var pumpState = FdcPump.QueryStatusAsync().Result;
  75. DebugLog($"FdcPump: {PumpId}, State: {pumpState}");
  76. if (pumpState == LogicalDeviceState.FDC_READY)
  77. {
  78. HandleNozzleStatusFdcIsReady(response);
  79. }
  80. else if (pumpState == LogicalDeviceState.FDC_CALLING ||
  81. pumpState == LogicalDeviceState.FDC_STARTED ||
  82. pumpState == LogicalDeviceState.FDC_AUTHORISED)
  83. {
  84. HandleNozzleStatusReadyForFueling(response);
  85. }
  86. else if (pumpState == LogicalDeviceState.FDC_FUELLING)
  87. {
  88. HandleNozzleStatusFdcFuelling(response);
  89. }
  90. else if (pumpState == LogicalDeviceState.FDC_OFFLINE)
  91. {
  92. HandleNozzleStatusFdcOffline(response);
  93. }
  94. else
  95. {
  96. DebugLog("No-, entering shithole...");
  97. HandleNozzleStatusFdcOtherStates(response);
  98. }
  99. return response;
  100. }
  101. #endregion
  102. #region Get accumulator
  103. public GetAccumulateResponse GetAccumulator(byte logicalNozzleId)
  104. {
  105. InfoLog($"Pump {PumpId}, Query totalizer");
  106. //var pumpState = FdcPump.QueryStatus();
  107. ////If the pump is idle, the query totalizer request must be from PIN-pad
  108. //if (pumpState != LogicalDeviceState.FDC_CALLING)
  109. //{
  110. // int volumeTotal = 0;
  111. // foreach (var nozzle in FdcPump.Nozzles)
  112. // {
  113. // var result = FdcPump.QueryTotalizer(nozzle.LogicalId);
  114. // volumeTotal += result.Item2;
  115. // }
  116. // if (volumeTotal > 99999999)
  117. // {
  118. // InfoLog($"Volume totalizer exceeding max length limit");
  119. // string vt = Convert.ToString(volumeTotal);
  120. // volumeTotal = Convert.ToInt32(vt.Substring(vt.Length - 8));
  121. // }
  122. // return new GetAccumulateResponse
  123. // {
  124. // 升累计 = volumeTotal,
  125. // 金额累计 = 0
  126. // };
  127. //}
  128. GetAccumulateResponse accumulatorResponse;
  129. if (LastFueling == null)
  130. {
  131. InfoLog("Get totalizer directly from pump");
  132. var result = FdcPump.QueryTotalizerAsync(logicalNozzleId).Result;
  133. if (result != null)
  134. {
  135. //Interim solution because Hengshan IC terminal supports max 4 bytes BCD
  136. int volumeTotal = 0;
  137. if (result.Item2 > 99999999)
  138. {
  139. string vt = Convert.ToString(result.Item2);
  140. volumeTotal = Convert.ToInt32(vt.Substring(vt.Length - 8));
  141. }
  142. else
  143. {
  144. volumeTotal = result.Item2;
  145. }
  146. accumulatorResponse = new GetAccumulateResponse
  147. {
  148. 升累计 = volumeTotal < 0 ? 0 : volumeTotal,
  149. 金额累计 = result.Item1 == -1 ? 6 : result.Item1
  150. };
  151. }
  152. else
  153. {
  154. accumulatorResponse = new GetAccumulateResponse
  155. {
  156. 升累计 = 0,
  157. 金额累计 = 0
  158. };
  159. }
  160. }
  161. else
  162. {
  163. InfoLog("Used totalizer from last fueling");
  164. accumulatorResponse = new GetAccumulateResponse
  165. {
  166. 金额累计 = 0,
  167. 升累计 = LastFueling.FdcTransaction.VolumeTotalizer ?? -1
  168. };
  169. }
  170. logger.Debug($"Pump {PumpId}, {accumulatorResponse.ToLogString()}");
  171. return accumulatorResponse;
  172. }
  173. #endregion
  174. #region Rounding
  175. /// <summary>
  176. /// Rounding up amount(金额凑整)
  177. /// </summary>
  178. /// <returns></returns>
  179. public RoundingResponse GetRoundingResult()
  180. {
  181. InfoLog("Amount rounding");
  182. RoundingResponse response = new RoundingResponse();
  183. if (ActiveFueling != null && ActiveFueling.FdcTransaction != null)
  184. {
  185. bool success = FdcPump.FuelingRoundUpByAmountAsync(GetRoundingAmount(ActiveFueling.FdcTransaction)).Result;
  186. response.EnumResult = success ?
  187. NonCardDispenserMessageTemplateBase.Result.成功 : NonCardDispenserMessageTemplateBase.Result.失败;
  188. }
  189. else
  190. {
  191. response = new RoundingResponse
  192. {
  193. EnumResult = NonCardDispenserMessageTemplateBase.Result.失败
  194. };
  195. }
  196. return response;
  197. }
  198. /// <summary>
  199. /// Rounding up by volume(升凑整)
  200. /// </summary>
  201. /// <returns>Return failure for now, since DART doesn't support this.</returns>
  202. public RoundUpByVolumeResponse HandleVolumeRounding()
  203. {
  204. InfoLog("Volume rounding");
  205. RoundUpByVolumeResponse response = new RoundUpByVolumeResponse
  206. {
  207. EnumResult = NonCardDispenserMessageTemplateBase.Result.失败
  208. };
  209. return response;
  210. }
  211. #endregion
  212. #region Reserve pump with amount
  213. public ReservePumpWithAmountResponse ReservePumpWithAmount(int amount)
  214. {
  215. InfoLog($"ReservePumpWithAmount request, amount : {amount} for pump id: {PumpId}");
  216. ReservePumpWithAmountResponse response = new ReservePumpWithAmountResponse();
  217. if (ActiveFueling != null)
  218. {
  219. lock (syncObj)
  220. {
  221. InfoLog($"Pump id: {PumpId}, setting AuthAmount = {amount}");
  222. tempAuthVolume = 0;
  223. ActiveFueling.Gallon = 0;
  224. ActiveFueling.AuthAmount = amount;
  225. }
  226. response.EnumResult = NonCardDispenserMessageTemplateBase.Result.成功;
  227. }
  228. else
  229. {
  230. InfoLog($"No ActiveFueling now, store auth amount {amount} to tempAuthAmount");
  231. tempAuthVolume = 0;
  232. tempAuthAmount = amount;
  233. response.EnumResult = NonCardDispenserMessageTemplateBase.Result.成功;
  234. }
  235. return response;
  236. }
  237. #endregion
  238. #region Reserve pump with volume
  239. public ReservePumpWithGallonResponse ReservePumpWithGallon(int volume)
  240. {
  241. InfoLog($"ReservePumpWithGallon request, Gallon(volume) : {volume} for pump id: {PumpId}");
  242. ReservePumpWithGallonResponse response = new ReservePumpWithGallonResponse();
  243. if (ActiveFueling != null)
  244. {
  245. lock (syncObj)
  246. {
  247. InfoLog($"Pump id: {PumpId}, setting Gallon = {volume}");
  248. tempAuthAmount = 0;
  249. ActiveFueling.AuthAmount = 0;
  250. ActiveFueling.Gallon = volume;
  251. }
  252. response.EnumResult = NonCardDispenserMessageTemplateBase.Result.成功;
  253. }
  254. else
  255. {
  256. InfoLog($"No ActiveFueling now, store auth gallon {volume} to tempAuthVolume");
  257. tempAuthAmount = 0;
  258. tempAuthVolume = volume;
  259. response.EnumResult = NonCardDispenserMessageTemplateBase.Result.成功;
  260. }
  261. return response;
  262. }
  263. #endregion
  264. #region Authorize pump
  265. /// <summary>
  266. /// Authorize the current pump associated with this fueling manager.
  267. /// </summary>
  268. /// <returns>Authorization result, true = success, false = failure</returns>
  269. public bool AuthorizePump()
  270. {
  271. InfoLog($"Authorize current pump id {PumpId}");
  272. if (fdcServer != null)
  273. {
  274. var pumpState = FdcPump.QueryStatusAsync().Result;
  275. //If the pump is authorized, no need to authorize it again.
  276. if (pumpState == LogicalDeviceState.FDC_AUTHORISED || pumpState == LogicalDeviceState.FDC_FUELLING)
  277. {
  278. return true;
  279. }
  280. else if (pumpState == LogicalDeviceState.FDC_READY)
  281. {
  282. InfoLog("There is no pump calling, abort auth");
  283. return false;
  284. }
  285. InfoLog($"Pump id: {PumpId}, ActiveFueling, AuthAmount: {ActiveFueling.AuthAmount}");
  286. if (ActiveFueling.AuthAmount == 0)
  287. {
  288. InfoLog($"Used tempAuthAmount {tempAuthAmount} as AuthAmount");
  289. ActiveFueling.AuthAmount = tempAuthAmount;
  290. tempAuthAmount = 0;
  291. }
  292. if (ActiveFueling.Gallon == 0)
  293. {
  294. InfoLog($"Used tempAuthVolume {tempAuthVolume} as Gallon");
  295. ActiveFueling.Gallon = tempAuthVolume;
  296. tempAuthVolume = 0;
  297. }
  298. if (ActiveFueling.AuthAmount != 0 && ActiveFueling.Gallon == 0)
  299. {
  300. bool success = fdcServer.AuthorizePumpAsync(FdcPump.PumpId, (double)ActiveFueling.AuthAmount / 100, 0).Result;
  301. InfoLog($"AuthorizePump {PumpId} with amount, success? {success}");
  302. return success;
  303. }
  304. else if (ActiveFueling.AuthAmount == 0 && ActiveFueling.Gallon != 0)
  305. {
  306. bool success = fdcServer.AuthorizePumpAsync(FdcPump.PumpId, 0, (double)ActiveFueling.Gallon / 100).Result;
  307. InfoLog($"AuthorizePump {PumpId} with volume, success? {success}");
  308. return success;
  309. }
  310. else
  311. {
  312. InfoLog($"Authorize with default amount 9999 for pump {FdcPump.PumpId}");
  313. bool success = fdcServer.AuthorizePumpAsync(FdcPump.PumpId, 9998, 0).Result;
  314. InfoLog($"AuthorizePump {PumpId} with amount, success? {success}");
  315. return success;
  316. }
  317. }
  318. else
  319. {
  320. logger.Info("No FdcServer instance");
  321. return false;
  322. }
  323. }
  324. #endregion
  325. #region Unauthorize pump
  326. public bool UnauthorizePump()
  327. {
  328. InfoLog("Unauthorize pump");
  329. var currentFdcState = FdcPump.QueryStatusAsync().Result;
  330. if (currentFdcState == LogicalDeviceState.FDC_FUELLING || currentFdcState == LogicalDeviceState.FDC_STARTED || currentFdcState == LogicalDeviceState.FDC_AUTHORISED)
  331. {
  332. //if the pump is not unauthorized, we need to unauthorize it
  333. if (pumpUnauthorized.HasValue && pumpUnauthorized.Value == false)
  334. {
  335. pumpUnauthorized = FdcPump.UnAuthorizeAsync(FdcPump.Nozzles.First().LogicalId).Result;
  336. return pumpUnauthorized.Value;
  337. }
  338. else
  339. {
  340. return true;
  341. }
  342. }
  343. else
  344. {
  345. InfoLog($"Current pump status is not in a state where we have to Unauthorize it {currentFdcState}");
  346. return true;
  347. }
  348. }
  349. #endregion
  350. #region Fueling handling
  351. public void CreateFueling(byte activeNozzle)
  352. {
  353. DebugLog($"Start to create fueling for pump id {PumpId}, active nozzle {activeNozzle}");
  354. lock (syncObj)
  355. {
  356. if (ActiveFueling == null)
  357. {
  358. ActiveFueling = new Fueling
  359. {
  360. FdcTransaction = null,
  361. PumpId = PumpId,
  362. ActiveNozzle = activeNozzle,
  363. AuthorizedByCard = false,
  364. FuelingState = FuelingState.Ready,
  365. AuthAmount = 0,
  366. FuelingSqNo = 0,
  367. NozzleReturnedTime = null
  368. };
  369. InfoLog("Creating fueling...");
  370. }
  371. else
  372. {
  373. InfoLog("Active fueling exists");
  374. }
  375. }
  376. InfoLog($"Fueling created for pump id {PumpId}, active nozzle {activeNozzle}");
  377. }
  378. public void UpdateFueling(FdcTransaction fdcTransaction)
  379. {
  380. if (ActiveFueling != null)
  381. {
  382. DebugLog($"Start to update fueling");
  383. lock (syncObj)
  384. {
  385. if (ActiveFueling.FuelingState == FuelingState.Running)
  386. {
  387. ActiveFueling.FdcTransaction = fdcTransaction;
  388. }
  389. else if (ActiveFueling.FuelingState == FuelingState.Authorized)
  390. {
  391. if (ActiveFueling.FdcTransaction == null)
  392. {
  393. if (activeFueling.FuelingSqNo == 0)
  394. {
  395. ActiveFueling.FuelingSqNo =
  396. fPosDbManager.GetNewSqNo(fdcTransaction.Nozzle.PumpId, fdcTransaction.Nozzle.LogicalId);
  397. }
  398. ActiveFueling.FuelingState = FuelingState.Running;
  399. InfoLog("First time got FdcTransaction");
  400. }
  401. }
  402. }
  403. DebugLog("Update fueling...done");
  404. }
  405. else
  406. {
  407. throw new Exception("Active fueling does not exist!");
  408. }
  409. }
  410. public void SetFuelingCompleted(FdcTransaction fdcTransaction)
  411. {
  412. if (ActiveFueling.FuelingState == FuelingState.Completed)
  413. {
  414. var sqNo = fPosDbManager.SetFillingDone(fdcTransaction.Nozzle.PumpId, fdcTransaction.Nozzle.LogicalId);
  415. InfoLog($"Fueling completed, set filling done, FPos SqNo = {sqNo}");
  416. }
  417. }
  418. public void StoreLastFueling(FdcTransaction fdcTransaction)
  419. {
  420. lock (syncObj)
  421. {
  422. ActiveFueling.FdcTransaction = fdcTransaction;
  423. LastFueling = ActiveFueling;
  424. ActiveFueling = null;
  425. }
  426. InfoLog("Transfer active fueling to last fueling, drop active fueling");
  427. }
  428. public void UpdateFuelingState(FuelingState state)
  429. {
  430. InfoLog("Trying to update the fueling state");
  431. //lock (syncObj)
  432. {
  433. if (ActiveFueling != null && ActiveFueling.FuelingState == state)
  434. {
  435. return;
  436. }
  437. else
  438. {
  439. InfoLog($"Set Fueling state to: {state}");
  440. ActiveFueling.FuelingState = state;
  441. }
  442. }
  443. InfoLog("Fueling state updated");
  444. }
  445. public void SetFuelingAsAuthorizedByCard()
  446. {
  447. InfoLog("start to set fueling as authorized by card");
  448. lock (syncObj)
  449. {
  450. if (ActiveFueling != null)
  451. {
  452. ActiveFueling.AuthorizedByCard = true;
  453. }
  454. }
  455. InfoLog("Already set fueling as authorized by card");
  456. }
  457. #endregion
  458. #region Private methods
  459. /// <summary>
  460. /// Handles the GetNozzleStatusRequest from terminal, better do nothing when FDC is offline.
  461. /// </summary>
  462. /// <param name="response">The GetNozzleStatus response for terminal</param>
  463. private void HandleNozzleStatusFdcOffline(GetNozzleStatusResponse response)
  464. {
  465. DebugLog("Pump is offline");
  466. }
  467. private void HandleNozzleStatusFdcOtherStates(GetNozzleStatusResponse response)
  468. {
  469. DebugLog($"FdcOtherStates enter");
  470. InfoLog($"Fueling manager {PumpId}, FDC State abnormal");
  471. //TryPullTransaction(response);
  472. //SetPumpProfileReadyForFueling(response);
  473. DebugLog($"Pump {PumpId}, FdcOtherStates exit");
  474. }
  475. private void HandleNozzleStatusReadyForFueling(GetNozzleStatusResponse response)
  476. {
  477. DebugLog($"Pump {PumpId}, ReadyForFueling enter");
  478. if (ActiveFueling != null)
  479. {
  480. if (ActiveFueling.FdcTransaction != null)
  481. {
  482. InfoLog("Oops, something is wrong, the active fueling is not null");
  483. }
  484. else
  485. {
  486. InfoLog("Waiting for fueling to be started on pump");
  487. }
  488. }
  489. TryPullTransaction(response);
  490. ////Assume pump is already authorized
  491. if (ActiveFueling != null && ActiveFueling.FuelingState == FuelingState.Authorized)
  492. {
  493. DebugLog("Active fueling, pump authorized");
  494. if (ActiveFueling.FuelingSqNo == 0)
  495. {
  496. ActiveFueling.FuelingSqNo =
  497. fPosDbManager.GetNewSqNo(PumpId, ActiveFueling.ActiveNozzle);
  498. InfoLog($"Pump fake auth, retrieve new SqNo {ActiveFueling.FuelingSqNo}");
  499. }
  500. response.流水号 = ActiveFueling.FuelingSqNo;
  501. response.单价 = 600;
  502. response.加油量 = 0;
  503. response.加油金额 = 0;
  504. SetPumpProfileFueling(response);
  505. }
  506. else if (ActiveFueling != null && ActiveFueling.FuelingState == FuelingState.Ready)
  507. {
  508. SetPumpProfileReadyForFueling(response);
  509. }
  510. logger.Debug($"Pump {PumpId}, ReadyForFueling exit");
  511. }
  512. private void HandleNozzleStatusFdcFuelling(GetNozzleStatusResponse response)
  513. {
  514. if (ActiveFueling != null && ActiveFueling.FdcTransaction != null)
  515. {
  516. pumpUnauthorized = false;
  517. tempAuthVolume = 0;
  518. tempAuthAmount = 0;
  519. DebugLog($"Fueling, FdcTrx pump id: {ActiveFueling.FdcTransaction.Nozzle.PumpId} "
  520. + $"nozzle id: {ActiveFueling.FdcTransaction.Nozzle.LogicalId} "
  521. + $"volume: {ActiveFueling.FdcTransaction.Volumn} "
  522. + $"amount: {ActiveFueling.FdcTransaction.Amount}");
  523. response.流水号 = ActiveFueling.FuelingSqNo;
  524. response.加油量 = ActiveFueling.FdcTransaction.Volumn;
  525. //if (ActiveFueling.FdcTransaction.Price > 100)
  526. //{
  527. int price = ActiveFueling.FdcTransaction.Price;
  528. int total = ActiveFueling.FdcTransaction.Amount;
  529. campaignEngine.ApplyDiscount(ActiveFueling.FdcTransaction.Barcode,
  530. ActiveFueling.FdcTransaction.Volumn, ref price, ref total);
  531. DebugLog($"Price: {price}, Amount: {total}");
  532. response.单价 = price;
  533. response.加油金额 = total;
  534. //}
  535. //else
  536. //{
  537. // response.单价 = ActiveFueling.FdcTransaction.Price;
  538. // response.加油金额 = ActiveFueling.FdcTransaction.Amount;
  539. //}
  540. if (ActiveFueling != null && ActiveFueling.AuthAmount != 0)
  541. {
  542. response.定量 = Convert.ToInt32(ActiveFueling.AuthAmount * 100);
  543. DebugLog($"ActiveFueling AuthAmount: {ActiveFueling.AuthAmount}");
  544. }
  545. else if (ActiveFueling != null && ActiveFueling.Gallon != 0)
  546. {
  547. response.定量 = ActiveFueling.Gallon;
  548. DebugLog($"ActiveFueling AuthVolume: {ActiveFueling.AuthAmount}");
  549. }
  550. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.允许加油);
  551. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.加油过程);
  552. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.油枪打开);
  553. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.电机打开);
  554. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.金额加油);
  555. //SetTransactionDetails(response, ActiveFueling.FdcTransaction.Barcode, ActiveFueling.FuelingSqNo,
  556. // ActiveFueling.FdcTransaction.Volumn, ActiveFueling.FdcTransaction.Price, ActiveFueling.FdcTransaction.Amount);
  557. //SetPumpProfileFueling(response);
  558. }
  559. else if (ActiveFueling != null && ActiveFueling.FdcTransaction == null)
  560. {
  561. DebugLog("Fdc transaction not received yet...");
  562. //TryPullTransaction(response);
  563. response.流水号 = ActiveFueling.FuelingSqNo;
  564. response.单价 = 600;
  565. response.加油量 = 0;
  566. response.加油金额 = 0;
  567. if (ActiveFueling != null && ActiveFueling.AuthAmount != 0)
  568. {
  569. response.定量 = Convert.ToInt32(ActiveFueling.AuthAmount * 100);
  570. DebugLog($"ActiveFueling AuthAmount: {ActiveFueling.AuthAmount}");
  571. }
  572. else if (ActiveFueling != null && ActiveFueling.Gallon != 0)
  573. {
  574. response.定量 = ActiveFueling.Gallon;
  575. DebugLog($"ActiveFueling AuthVolume: {ActiveFueling.AuthAmount}");
  576. }
  577. //SetPumpProfileReadyForFueling(response);
  578. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.允许加油);
  579. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.加油过程);
  580. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.油枪打开);
  581. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.电机打开);
  582. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.金额加油);
  583. }
  584. else if (ActiveFueling == null)
  585. {
  586. InfoLog("Unbelievable, the current fueling is null");
  587. }
  588. }
  589. /// <summary>
  590. /// Handle the nozzle status request when pump is in state FDC_Ready (idle)
  591. /// Strategy:
  592. /// 1. Check if there is a running fueling
  593. /// 2. If no running fueling, check if there is a previous transaction
  594. /// 3. If no previous transaction, try pulling a paid transaction from FDC database
  595. /// 4. If no transaction from FC database, use false fueling data to make IC terminal happy
  596. /// </summary>
  597. /// <param name="response">The nozzle status response.</param>
  598. private void HandleNozzleStatusFdcIsReady(GetNozzleStatusResponse response)
  599. {
  600. DebugLog($"Pump {PumpId}, FdcIsReady enter");
  601. if (ActiveFueling != null)
  602. {
  603. DebugLog("ActiveFueling is not null");
  604. }
  605. DebugLog($"Fueling manager for pump id: {PumpId}");
  606. //Fueling completed event not received yet
  607. if (ActiveFueling != null && ActiveFueling.FdcTransaction != null)
  608. {
  609. if (ActiveFueling.NozzleReturnedTime == null)
  610. ActiveFueling.NozzleReturnedTime = DateTime.Now;
  611. SetTransactionDetails(response, ActiveFueling.FdcTransaction.Barcode, ActiveFueling.FuelingSqNo,
  612. ActiveFueling.FdcTransaction.Volumn, ActiveFueling.FdcTransaction.Price, ActiveFueling.FdcTransaction.Amount);
  613. //Terminate the fueling if the 'Done event' is not received after nozzle replace for 5 minutes
  614. if (DateTime.Now - ActiveFueling.NozzleReturnedTime > TimeSpan.FromMinutes(5))
  615. {
  616. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.不允许加油);
  617. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.加油结束);
  618. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.油枪关);
  619. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.电机关);
  620. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.金额加油);
  621. LastFueling = ActiveFueling;
  622. ActiveFueling = null;
  623. }
  624. else
  625. {
  626. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.允许加油);
  627. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.加油过程);
  628. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.油枪打开);
  629. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.电机打开);
  630. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.金额加油);
  631. //SetPumpProfileFueling(response);
  632. }
  633. return;
  634. }
  635. DebugLog($"Try pull trx");
  636. TryPullTransaction(response);
  637. SetPumpProfileFdcIsReady(response);
  638. DebugLog($"Pump {PumpId}, FdcIsReady exit");
  639. }
  640. private void TryPullTransaction(GetNozzleStatusResponse response)
  641. {
  642. DebugLog($"Pump {PumpId}, TryPullTransaction enter");
  643. //Fueling transaction stored in memory
  644. if (LastFueling != null)
  645. {
  646. DebugLog("Using data from LastFueling");
  647. SetTransactionDetails(response, LastFueling.FdcTransaction.Barcode, LastFueling.FuelingSqNo,
  648. LastFueling.FdcTransaction.Volumn, LastFueling.FdcTransaction.Price, LastFueling.FdcTransaction.Amount);
  649. }
  650. //This is probably after a restart of this software
  651. if (ActiveFueling == null && LastFueling == null)
  652. {
  653. DebugLog($"Fueling manager {PumpId}, Current and last fueling are both null");
  654. var lastFdcTransaction = GetFdcFuelSaleTransaction();
  655. if (lastFdcTransaction != null)
  656. {
  657. DebugLog($"Fueling manager {PumpId}, Found a transaction in Fdc database for this pump id= {PumpId}");
  658. var posSqNo = fPosDbManager.GetSqNoByMapping(
  659. lastFdcTransaction.PumpId,
  660. lastFdcTransaction.LogicalNozzleId,
  661. Convert.ToInt32(lastFdcTransaction.TransactionSeqNumberFromPhysicalPump));
  662. if (posSqNo == 0)
  663. {
  664. InfoLog("Probably last paid transaction in FC database was not authorized by card or didn't finish correctly");
  665. }
  666. SetTransactionDetails(response, -1,
  667. posSqNo, lastFdcTransaction.Volumn, lastFdcTransaction.UnitPrice, lastFdcTransaction.Amount);
  668. }
  669. else
  670. {
  671. DebugLog($"Fueling manager {PumpId}, No FdcTransaction found");
  672. //This is a fresh delopyment of the software
  673. SetTransactionDetails(response, -1, 0, 0, 1000, 0);
  674. }
  675. //SetTransactionDetails(response, 0, 1, 2, 2);
  676. }
  677. else if (ActiveFueling != null && ActiveFueling.FdcTransaction == null && LastFueling == null)
  678. {
  679. DebugLog("Using 0-0-1000-0");
  680. //Pump calling, fresh deployment
  681. SetTransactionDetails(response, -1, 0, 0, 1000, 0);
  682. }
  683. else if (ActiveFueling != null && ActiveFueling.FdcTransaction != null)
  684. {
  685. InfoLog("Nozzle returned and lifted before amount deducted");
  686. SetTransactionDetails(response, ActiveFueling.FdcTransaction.Barcode, ActiveFueling.FuelingSqNo,
  687. ActiveFueling.FdcTransaction.Volumn, ActiveFueling.FdcTransaction.Price, ActiveFueling.FdcTransaction.Amount);
  688. }
  689. DebugLog($"Pump {PumpId}, TryPullTransaction exit");
  690. }
  691. /// <summary>
  692. /// Set the pump transaction details
  693. /// </summary>
  694. /// <param name="response"></param>
  695. /// <param name="seqNo"></param>
  696. /// <param name="volume"></param>
  697. /// <param name="unitPrice"></param>
  698. /// <param name="amount"></param>
  699. private void SetTransactionDetails(
  700. GetNozzleStatusResponse response, int barcode, int seqNo, int volume, int unitPrice, int amount)
  701. {
  702. response.流水号 = seqNo;
  703. response.加油量 = volume;
  704. if (unitPrice > 100)
  705. {
  706. int price = unitPrice;
  707. int total = amount;
  708. campaignEngine.ApplyDiscount(barcode, volume, ref price, ref total);
  709. DebugLog($"Price: {price}, Amount: {total}");
  710. response.单价 = price;
  711. response.加油金额 = total;
  712. return;
  713. }
  714. response.单价 = unitPrice;
  715. response.加油金额 = amount;
  716. if (ActiveFueling != null && ActiveFueling.AuthAmount != 0)
  717. {
  718. response.定量 = Convert.ToInt32(ActiveFueling.AuthAmount * 100);
  719. DebugLog($"ActiveFueling AuthAmount: {ActiveFueling.AuthAmount}");
  720. }
  721. else if (ActiveFueling != null && ActiveFueling.Gallon != 0)
  722. {
  723. response.定量 = ActiveFueling.Gallon;
  724. DebugLog($"ActiveFueling AuthVolume: {ActiveFueling.Gallon}");
  725. }
  726. }
  727. /// <summary>
  728. /// Set the pump profile fields as it's idle
  729. /// </summary>
  730. /// <param name="response"></param>
  731. private void SetPumpProfileFdcIsReady(GetNozzleStatusResponse response)
  732. {
  733. DebugLog("Set pump profile idle");
  734. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.不允许加油);
  735. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.加油结束);
  736. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.油枪关);
  737. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.电机关);
  738. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.金额加油);
  739. }
  740. /// <summary>
  741. /// Set the pump profile fields as it's fueling.
  742. /// </summary>
  743. /// <param name="response"></param>
  744. private void SetPumpProfileFueling(GetNozzleStatusResponse response)
  745. {
  746. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.允许加油);
  747. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.加油过程);
  748. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.油枪打开);
  749. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.电机打开);
  750. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.金额加油);
  751. }
  752. /// <summary>
  753. /// Set the pump profile fields as it's ready to start a fueling, waiting for IC terminal's
  754. /// Reserve and Open requests.
  755. /// </summary>
  756. /// <param name="response">The nozzle status response.</param>
  757. private void SetPumpProfileReadyForFueling(GetNozzleStatusResponse response)
  758. {
  759. try
  760. {
  761. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.不允许加油);
  762. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.加油结束);
  763. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.油枪打开);
  764. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.电机关);
  765. response.AddPumpStatus(GetNozzleStatusResponse.PumpStatus.金额加油);
  766. }
  767. catch (Exception ex)
  768. {
  769. DebugLog($"{ex}");
  770. }
  771. DebugLog(response.ToLogString());
  772. }
  773. private FuelSaleTransaction GetFdcFuelSaleTransaction()
  774. {
  775. //Fow now return null, for performance reasons.
  776. return null;
  777. //return fdcDbContext.PumpTransactionModels
  778. // .Where(t => t.PumpId == PumpId)
  779. // .OrderByDescending(s => s.SaleStartTime)
  780. // .FirstOrDefault();
  781. }
  782. /// <summary>
  783. /// Gets the next possible preset amount.
  784. /// </summary>
  785. /// <param name="fdcTransaction">Current Fdc filling transaction.</param>
  786. /// <returns></returns>
  787. private int GetRoundingAmount(FdcTransaction fdcTransaction)
  788. {
  789. int currentFillingAmount = fdcTransaction.Amount;
  790. int nextRoundedAmount = currentFillingAmount;
  791. do
  792. {
  793. nextRoundedAmount++;
  794. }
  795. while (nextRoundedAmount % 100 != 0);
  796. InfoLog($"Current amount: {currentFillingAmount}, rounded amount: {nextRoundedAmount}");
  797. return nextRoundedAmount;
  798. }
  799. #endregion
  800. #region Log methods
  801. private void InfoLog(string log)
  802. {
  803. if (logger.IsInfoEnabled)
  804. logger.Info($"{PumpId} " + log);
  805. }
  806. private void DebugLog(string log)
  807. {
  808. if (logger.IsDebugEnabled)
  809. logger.Debug($"{PumpId} " + log);
  810. }
  811. private void ErrorLog(string log)
  812. {
  813. if (logger.IsErrorEnabled)
  814. logger.Error($"{PumpId} " + log);
  815. }
  816. #endregion
  817. }
  818. public enum DiscountType
  819. {
  820. FixedAmount = 1,
  821. Percentage = 2
  822. }
  823. public class FuelDiscount
  824. {
  825. public int FuelBarcode { get; set; }
  826. public DiscountType DiscountType { get; set; }
  827. public int Deduction { get; set; }
  828. }
  829. public class CampaignEngine
  830. {
  831. static NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("FuelingManager");
  832. public List<FuelDiscount> ActiveDiscounts { get; private set; }
  833. public CampaignEngine(List<FuelDiscount> discounts)
  834. {
  835. ActiveDiscounts = discounts;
  836. }
  837. public CampaignEngine(string discountConfig)
  838. {
  839. LoadDiscounts(discountConfig);
  840. }
  841. private void LoadDiscounts(string discountConfig)
  842. {
  843. ActiveDiscounts = new List<FuelDiscount>();
  844. string[] discounts = discountConfig.Split(';');
  845. foreach (var d in discounts)
  846. {
  847. string[] part = d.Split(',');
  848. ActiveDiscounts.Add(new FuelDiscount
  849. {
  850. FuelBarcode = Convert.ToInt32(part[0]),
  851. DiscountType = (DiscountType)Convert.ToInt32(part[1]),
  852. Deduction = Convert.ToInt32(part[2])
  853. });
  854. }
  855. InfoLog($"Active discounts count: {ActiveDiscounts.Count}");
  856. }
  857. #region Discount version 1.0
  858. //public void ApplyDiscount(int barcode, int volume, ref int unitPrice, ref int amount)
  859. //{
  860. // if (barcode < 0)
  861. // return;
  862. // int originalPrice = unitPrice;
  863. // unitPrice = GetDiscountedPrice(barcode, unitPrice);
  864. // if (originalPrice == unitPrice)
  865. // {
  866. // if (logger.IsErrorEnabled)
  867. // logger.Error("Price after discount equals original price");
  868. // return;
  869. // }
  870. // amount = unitPrice * volume / 100;
  871. //}
  872. //private int GetDiscountedPrice(int barcode, int unitPrice)
  873. //{
  874. // foreach (var fd in ActiveDiscounts)
  875. // {
  876. // DebugLog($"FD barcode: {fd.FuelBarcode}, barcode: {barcode}");
  877. // if (fd.FuelBarcode == barcode)
  878. // {
  879. // if (fd.DiscountType == DiscountType.FixedAmount)
  880. // return unitPrice - fd.Deduction;
  881. // else if (fd.DiscountType == DiscountType.Percentage)
  882. // return unitPrice * (100 - fd.Deduction) / 100;
  883. // }
  884. // }
  885. // if (logger.IsErrorEnabled)
  886. // logger.Error("No discount on price applied");
  887. // //No discount is applied
  888. // return unitPrice;
  889. //}
  890. #endregion
  891. public void ApplyDiscount(int barcode, int volume, ref int unitPrice, ref int amount)
  892. {
  893. if (barcode < 0)
  894. return;
  895. int originalPrice = unitPrice;
  896. decimal realUnitPrice = unitPrice;
  897. realUnitPrice = GetDiscountedPrice(barcode, realUnitPrice);
  898. if (originalPrice == realUnitPrice)
  899. {
  900. ErrorLog("Price after discount equals original price");
  901. return;
  902. }
  903. amount = Convert.ToInt32(Math.Round(realUnitPrice * volume / 100, 0));
  904. unitPrice = Convert.ToInt32(Math.Round(realUnitPrice, 0));
  905. }
  906. /// <summary>
  907. /// Get the discounted price of a particular fuel.
  908. /// </summary>
  909. /// <param name="barcode">the fuel product barcode.</param>
  910. /// <param name="unitPrice">the original unit price of that fuel.</param>
  911. /// <returns></returns>
  912. private decimal GetDiscountedPrice(int barcode, decimal unitPrice)
  913. {
  914. foreach (var fd in ActiveDiscounts)
  915. {
  916. DebugLog($"FD barcode: {fd.FuelBarcode}, barcode: {barcode}");
  917. if (fd.FuelBarcode == barcode)
  918. {
  919. if (fd.DiscountType == DiscountType.FixedAmount)
  920. return unitPrice - fd.Deduction;
  921. else if (fd.DiscountType == DiscountType.Percentage)
  922. return unitPrice * (100 - fd.Deduction) / 100;
  923. }
  924. }
  925. ErrorLog("No discount on price applied");
  926. //No discount is applied
  927. return unitPrice;
  928. }
  929. #region Log methods
  930. private void DebugLog(string log)
  931. {
  932. if (logger.IsDebugEnabled)
  933. logger.Debug(log);
  934. }
  935. private void InfoLog(string log)
  936. {
  937. if (logger.IsInfoEnabled)
  938. logger.Info(log);
  939. }
  940. private void ErrorLog(string log)
  941. {
  942. if (logger.IsErrorEnabled)
  943. logger.Error(log);
  944. }
  945. #endregion
  946. }
  947. }