TerminalManager.cs 93 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277
  1. using Dfs.WayneChina.HengshanPayTerminal;
  2. using Dfs.WayneChina.HengshanPayTerminal.MessageEntity;
  3. using Dfs.WayneChina.HengshanPayTerminal.MessageEntity.Incoming;
  4. using Dfs.WayneChina.HengshanPayTerminal.MessageEntity.Outgoing;
  5. using Dfs.WayneChina.SpsDbManager;
  6. using Dfs.WayneChina.SpsDbManager.ResultSet;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Text;
  10. using System.Linq;
  11. using Wayne.FDCPOSLibrary;
  12. using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;
  13. using Dfs.WayneChina.HengshanPayTerminal.Support;
  14. using System.Timers;
  15. using Dfs.WayneChina.IPosPlus.ServiceClient;
  16. using System.Threading.Tasks;
  17. using System.Collections.Concurrent;
  18. namespace Dfs.WayneChina.IPosPlus
  19. {
  20. /// <summary>
  21. /// An entity that manages the IC card terminal.
  22. /// </summary>
  23. public class TerminalManager
  24. {
  25. #region Fields
  26. private readonly byte STX = 0xFA;
  27. /// <summary>
  28. /// Sps_db database manager.
  29. /// </summary>
  30. private readonly SpsManager _dbManager;
  31. /// <summary>
  32. /// The terminal handler.
  33. /// </summary>
  34. private readonly HengshanPayTermHandler _terminal;
  35. /// <summary>
  36. /// IPosPlusApp instance.
  37. /// </summary>
  38. private readonly IPosPlusApp _posApp;
  39. /// <summary>
  40. /// Internally maintained pump state.
  41. /// </summary>
  42. private HengshanPumpState _pumpState;
  43. private HengshanPumpState _lastPumpState;
  44. /// <summary>
  45. /// All listed cards (Newly-added blacklist, base blacklist, newly-deleted blacklist, whitelist)
  46. /// </summary>
  47. public VersionedListedCard VersionedListedCard;
  48. /// <summary>
  49. /// Connection detection timer.
  50. /// </summary>
  51. private Timer timer;
  52. private int interval;
  53. private HengshanPayTerminal.Parser parser = new HengshanPayTerminal.Parser();
  54. private CardAppType _cardAppType = CardAppType.CpuCard;
  55. private WorkMode currentWorkMode = WorkMode.Disabled;
  56. private IList<DataVersion> currentDataVersions = null;
  57. #endregion
  58. #region Filling control state data
  59. private FillingInfo currentFilling;
  60. private FillingInfo lastFilling;
  61. #endregion
  62. #region Logger
  63. private static NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("IPosPlusApp");
  64. #endregion
  65. #region Queue
  66. private Queue<CardMessageBase> activeSendQueue = new Queue<CardMessageBase>();
  67. private object sendQueueSyncObj = new object();
  68. #endregion
  69. #region Payment queue
  70. private System.Threading.Thread thread;
  71. private ConcurrentQueue<PaymentRequest> paymentRequestQueue = new ConcurrentQueue<PaymentRequest>();
  72. private System.Threading.ManualResetEvent mre = new System.Threading.ManualResetEvent(false);
  73. #endregion
  74. #region Reserved balance
  75. private readonly int reservedBalance = 0;
  76. #endregion
  77. #region Constructor
  78. public TerminalManager(IPosPlusApp posApp, int pumpId, int subAddress, SpsManager spsManager, HengshanPayTermHandler terminal,
  79. int interval, int cardAppType)
  80. {
  81. _posApp = posApp;
  82. PumpId = pumpId;
  83. SubAddress = subAddress;
  84. _dbManager = spsManager;
  85. _terminal = terminal;
  86. _pumpState = HengshanPumpState.Idle;
  87. _lastPumpState = HengshanPumpState.Idle;
  88. this.interval = interval;
  89. if (cardAppType == 1)
  90. _cardAppType = CardAppType.RfCard;
  91. else if (cardAppType == 2)
  92. _cardAppType = CardAppType.CpuCard;
  93. reservedBalance = posApp.ReservedBalance;
  94. RegisterEncodingProvider();
  95. InitializeTimer();
  96. thread = new System.Threading.Thread(() => HandleCardPayment());
  97. thread.Start();
  98. }
  99. private void RegisterEncodingProvider()
  100. {
  101. EncodingProvider provider = CodePagesEncodingProvider.Instance;
  102. Encoding.RegisterProvider(provider);
  103. }
  104. #endregion
  105. #region Properties
  106. public string TerminalId { get; private set; }
  107. public string Identifier => _terminal.PumpIdList;
  108. public int PumpId { get; private set; }
  109. public int SubAddress { get; }
  110. public DiscountServiceClient DiscountClient => _posApp.DiscountServiceClient;
  111. #endregion
  112. #region Timer
  113. private void InitializeTimer()
  114. {
  115. timer = new Timer();
  116. timer.Interval = interval * 1000;
  117. timer.Elapsed += Timer_Elapsed;
  118. timer.Start();
  119. }
  120. private void Timer_Elapsed(object sender, ElapsedEventArgs e)
  121. {
  122. logger.Error($"*** Terminal {PumpId}, no incoming command from terminal/pump for {interval} seconds! Probably communication lost.");
  123. _pumpState = HengshanPumpState.Offline;
  124. if (_pumpState != _lastPumpState)
  125. {
  126. _terminal.UpdatePumpState(PumpId, 1, LogicalDeviceState.FDC_OFFLINE);
  127. _lastPumpState = _pumpState;
  128. }
  129. // When pump is offline, it's offline.
  130. if (_lastPumpState == HengshanPumpState.Offline)
  131. {
  132. timer.Stop();
  133. }
  134. }
  135. private void ResetTimer()
  136. {
  137. timer.Stop();
  138. logger.Debug($"terminal {PumpId.ToString()} timer stopped");
  139. timer.Start();
  140. logger.Debug($"terminal {PumpId.ToString()} timer started");
  141. }
  142. #endregion
  143. #region Methods
  144. #region Register 油机上电注册
  145. public void HandleRegiser(RegisterRequest request)
  146. {
  147. logger.Info($"Terminal registration from pump {PumpId}, Terminal Id: {request.TerminalId}");
  148. CheckTerminalId(request.TerminalId);
  149. var registerResults = _dbManager.RegisterTerminal(request.TerminalId);
  150. RegisterTerminalResult currentResult = null;
  151. if (registerResults.Count > 0)
  152. {
  153. currentResult = registerResults[0];
  154. }
  155. SendRegisterResult(request, currentResult);
  156. }
  157. private void SendRegisterResult(RegisterRequest request, RegisterTerminalResult registerResult)
  158. {
  159. if (registerResult == null)
  160. {
  161. }
  162. RegisterResult result = new RegisterResult
  163. {
  164. Prefix = STX,
  165. SourceAddress = request.SourceAddress,
  166. DestinationAddress = request.DestinationAddress,
  167. FrameSqNoByte = request.FrameSqNoByte,
  168. Handle = Convert.ToByte(Command.RegisterResult),
  169. Result = Convert.ToByte(registerResult.RegisterResultCode),
  170. TerminalId = request.TerminalId,
  171. SystemKey = registerResult.SystemKey
  172. };
  173. if (!string.IsNullOrEmpty(registerResult.StationName))
  174. {
  175. result.StationNameLength = Convert.ToInt32(registerResult.StationNameLength);
  176. result.StationName = Encoding.GetEncoding("GB2312").GetBytes(registerResult.StationName).ToList();
  177. }
  178. _terminal.Write(result);
  179. }
  180. #endregion
  181. #region REGURLAR CHECK 油机普通查询命令处理
  182. /// <summary>
  183. /// Handles the Check_cmd request.
  184. /// 处理油机普通查询命令。
  185. /// </summary>
  186. /// <param name="request">The Check_cmd request.</param>
  187. public void HandleCheckCmdRequest(CheckCmdRequest request)
  188. {
  189. CheckTerminalId(request.TerminalId);
  190. ResetTimer();
  191. if (request.WorkMode != (byte)currentWorkMode)
  192. {
  193. currentWorkMode = (WorkMode)_dbManager.GetPumpWorkMode(Convert.ToByte(PumpId));
  194. if (request.WorkMode != 0x05)
  195. {
  196. ChangeAuthMode(request, currentWorkMode);
  197. }
  198. if (IsMessagePendingForSend())
  199. {
  200. var message = PickAndSendCardMessage();
  201. logger.Info($"Active send queue, pending message exists, send it, MessageHandle={message.Handle}");
  202. message.FrameSqNoByte = request.FrameSqNoByte;
  203. _terminal.Write(message);
  204. return;
  205. }
  206. }
  207. //Reset the `Versioned listed cards`
  208. VersionedListedCard = null;
  209. logger.Debug($"Pump {PumpId}, Amount: {request.Amount}, Volume: {request.Volume}, Price: {request.Price}");
  210. _terminal.StoreLatestFrameSqNo(PumpId, request.FrameSqNoByte);
  211. if (_terminal.TrySendNextMessage())
  212. {
  213. //Really sent something, return!
  214. logger.Debug("Will not send the CheckCmdResponse");
  215. return;
  216. }
  217. var versions = _dbManager.GetDataVersions();
  218. if (currentDataVersions == null)
  219. {
  220. currentDataVersions = versions;
  221. }
  222. if (currentDataVersions != null && versions != null)
  223. {
  224. var storedGeneralInfo = versions.FirstOrDefault(v => v.VersionType == VersionType.GeneralInfoVersion);
  225. var currentGeneralInfo = currentDataVersions.FirstOrDefault(v => v.VersionType == VersionType.GeneralInfoVersion);
  226. if (storedGeneralInfo != null && currentGeneralInfo != null)
  227. {
  228. if (currentGeneralInfo.VersionNo != storedGeneralInfo.VersionNo)
  229. {
  230. byte currentMode = _dbManager.GetPumpWorkMode(Convert.ToByte(PumpId));
  231. if (currentMode != (byte)currentWorkMode)
  232. {
  233. ChangeAuthMode(request, (WorkMode)currentMode);
  234. logger.Warn($"Auth mode changed!, {currentWorkMode} -> {currentMode}");
  235. }
  236. if (IsMessagePendingForSend())
  237. {
  238. var message = PickAndSendCardMessage();
  239. logger.Info($"Active send queue, pending message exists, send it, MessageHandle={message.Handle}");
  240. message.FrameSqNoByte = request.FrameSqNoByte;
  241. _terminal.Write(message);
  242. return;
  243. }
  244. }
  245. }
  246. }
  247. SendCheckCmdResult(versions, request);
  248. var currentNozzleNo = request.FuelingPoint.NozzleNo;
  249. int currentLogicId;
  250. bool getLogicIdResult = _terminal.NozzleLogicIdDict.TryGetValue(currentNozzleNo, out currentLogicId);
  251. if (getLogicIdResult == false)
  252. currentLogicId = 1;
  253. //Get dispenser station from check cmd
  254. if (request.DispenserState == 0x08)
  255. {
  256. _pumpState = HengshanPumpState.Fueling;
  257. if (request.WorkMode == 0x05 && _lastPumpState != _pumpState)
  258. {
  259. EmulatePumpStateSequentialChange();
  260. }
  261. if (_lastPumpState != _pumpState)
  262. {
  263. logger.Info($"Pump {PumpId} aleady in fuelling, but last state was not, trigger a state change");
  264. _terminal.UpdatePumpState(PumpId, currentLogicId, LogicalDeviceState.FDC_FUELLING);
  265. }
  266. _lastPumpState = _pumpState;
  267. }
  268. else if (request.DispenserState == 0x03)
  269. {
  270. _pumpState = HengshanPumpState.Idle;
  271. if (_lastPumpState == HengshanPumpState.Fueling && _lastPumpState != _pumpState)
  272. {
  273. logger.Info($"Pump {PumpId}, Nozzle {currentNozzleNo}, fueling => ready");
  274. _terminal.UpdatePumpState(PumpId, currentLogicId, LogicalDeviceState.FDC_READY);
  275. }
  276. else if (_lastPumpState == HengshanPumpState.Offline && _pumpState == HengshanPumpState.Idle)
  277. {
  278. logger.Info($"Pump {PumpId}, Nozzle {currentNozzleNo}, offline => ready");
  279. _terminal.UpdatePumpState(PumpId, currentLogicId, LogicalDeviceState.FDC_READY);
  280. }
  281. }
  282. if (_pumpState == HengshanPumpState.Idle)
  283. {
  284. //If the pump is idle, it's idle.
  285. _lastPumpState = _pumpState;
  286. }
  287. else if (_pumpState == HengshanPumpState.Offline)
  288. {
  289. _pumpState = HengshanPumpState.Idle;
  290. _terminal.UpdatePumpState(PumpId, currentLogicId, LogicalDeviceState.FDC_READY);
  291. _lastPumpState = _pumpState;
  292. }
  293. else if (_pumpState == HengshanPumpState.Authorized)
  294. {
  295. if (request.Amount >= 0 || request.Volume >= 0)
  296. {
  297. logger.Info($"Pump {PumpId}, Nozzle {currentNozzleNo}, authorized => fuelling");
  298. _pumpState = HengshanPumpState.Fueling;
  299. _terminal.UpdatePumpState(currentNozzleNo, currentLogicId, LogicalDeviceState.FDC_FUELLING);
  300. _lastPumpState = _pumpState;
  301. }
  302. }
  303. else if (_pumpState == HengshanPumpState.Fueling)
  304. {
  305. logger.Info($"Pump {PumpId}, Nozzle {currentNozzleNo}, fueling in progress, Amount: {request.Amount}, Volume: {request.Volume}, Price: {request.Price}");
  306. _terminal.UpdateFuelingStatus(PumpId, new FdcTransaction
  307. {
  308. Nozzle = new LogicalNozzle(PumpId, 1, Convert.ToByte(currentLogicId), null),
  309. Amount = request.Amount,
  310. Volumn = request.Volume,
  311. Price = request.Price,
  312. Finished = false
  313. });
  314. _lastPumpState = _pumpState;
  315. }
  316. }
  317. private void EmulatePumpStateSequentialChange()
  318. {
  319. logger.Info("Emulating pump state sequential changes...");
  320. _terminal.UpdatePumpState(PumpId, 1, LogicalDeviceState.FDC_CALLING);
  321. System.Threading.Thread.Sleep(50);
  322. _terminal.UpdatePumpState(PumpId, 1, LogicalDeviceState.FDC_AUTHORISED);
  323. System.Threading.Thread.Sleep(50);
  324. _terminal.UpdatePumpState(PumpId, 1, LogicalDeviceState.FDC_STARTED);
  325. System.Threading.Thread.Sleep(50);
  326. _terminal.UpdatePumpState(PumpId, 1, LogicalDeviceState.FDC_FUELLING);
  327. System.Threading.Thread.Sleep(50);
  328. }
  329. /// <summary>
  330. /// Sends result of the Check_cmd.
  331. /// 返回油机普通查询命令结果。
  332. /// </summary>
  333. private void SendCheckCmdResult(IList<DataVersion> dataVersions, CheckCmdRequest request)
  334. {
  335. if(dataVersions == null)
  336. {
  337. logger.Error("Retrieved empty version information");
  338. return;
  339. }
  340. InquiryResult result = new InquiryResult
  341. {
  342. Prefix = STX,
  343. SourceAddress = request.SourceAddress,
  344. DestinationAddress = request.DestinationAddress,
  345. FrameSqNoByte = request.FrameSqNoByte,
  346. Handle = Convert.ToByte(Command.Ver_info),
  347. BaseBlacklistVersion = dataVersions.First(d => d.VersionType == VersionType.BaseBlacklistVersion).VersionNo,
  348. NewlyAddedBlacklistVersion = dataVersions.First(d => d.VersionType == VersionType.NewlyAddedBlacklistVersion).VersionNo,
  349. NewlyDeletedBlacklistVersion = dataVersions.First(d => d.VersionType == VersionType.NewlyDeletedBlacklistVersion).VersionNo,
  350. WhitelistVersion = dataVersions.First(d => d.VersionType == VersionType.WhitelistVersion).VersionNo,
  351. FuelPriceVersion = dataVersions.First(d => d.VersionType == VersionType.FuelPriceChangeVersion).VersionNo,
  352. StationGeneralInfoVersion = dataVersions.First(d => d.VersionType == VersionType.GeneralInfoVersion).VersionNo,
  353. PrinterReceiptInfoVersion = 0x00,
  354. SoftwareDownloadFlag = 0x00,
  355. UnspecifiedField1 = 0x00,
  356. };
  357. result.SetTime(DateTime.Now);
  358. _terminal.Write(result);
  359. }
  360. #endregion
  361. #region Validate card 验卡
  362. public void HandleCardValidation(ValidateCardRequest request)
  363. {
  364. ResetTimer();
  365. var results = _dbManager.GetCheckCardResult(GetCardNo(request.Asn),
  366. long.Parse(request.PhysicalCardNo, System.Globalization.NumberStyles.HexNumber), request.FuelingPoint.PumpNo);
  367. var blacklistedCard = _dbManager.IsCardBlackListed(GetCardNo(request.Asn));
  368. if (results!= null && results.Count == 1)
  369. {
  370. var currentResult = results[0];
  371. var cardInfo = _dbManager.GetCardInfoByCardNo(GetCardNo(request.Asn));
  372. if (cardInfo != null)
  373. {
  374. byte cardClass = cardInfo.CardClass.Value;
  375. byte cardType = cardInfo.CardType.Value;
  376. SendValidateCardResult(currentResult, request, blacklistedCard, cardClass, cardType, cardInfo.Holder, cardInfo.Carno);
  377. }
  378. else
  379. {
  380. SendValidateCardResult(currentResult, request, blacklistedCard, 0, 0, string.Empty, string.Empty);
  381. }
  382. }
  383. }
  384. private void SendValidateCardResult(CardResult checkResult, ValidateCardRequest request, bool blacklistedCard, byte cardClass,
  385. byte cardType, string holder, string carNo)
  386. {
  387. var cardState = checkResult.CardStatus;
  388. if (blacklistedCard)
  389. cardState = SpsDbManager.ResultSet.CardState.AccountFrosen;
  390. ValidateCardResult result = new ValidateCardResult();
  391. result.Prefix = STX;
  392. result.SourceAddress = request.SourceAddress;
  393. result.DestinationAddress = request.DestinationAddress;
  394. result.Handle = Convert.ToByte(Command.ValidateCardResult);
  395. result.FrameSqNoByte = request.FrameSqNoByte;
  396. result.Asn = request.Asn;
  397. result.DiscountNo = Convert.ToUInt16(checkResult.DiscountNo);
  398. result.CardState = (HengshanPayTerminal.Support.CardState)Convert.ToByte(cardState);
  399. result.AdditionalInfo = GenerateAdditionalInfo(cardState);
  400. result.AdditionalInfoLength = Convert.ToByte(result.AdditionalInfo.Count);
  401. if (result.AdditionalInfoLength == 0)
  402. {
  403. List<byte> addInfo = new List<byte>();
  404. if (cardClass == 1 || cardClass == 2)
  405. {
  406. addInfo.Add(1);
  407. if (!string.IsNullOrEmpty(carNo))
  408. addInfo.AddRange(Encoding.GetEncoding("GB2312").GetBytes(carNo));
  409. else
  410. addInfo.AddRange(Encoding.GetEncoding("GB2312").GetBytes(holder));
  411. }
  412. if (_cardAppType == CardAppType.CpuCard)
  413. {
  414. if (cardClass == 3 && cardType == 1)
  415. {
  416. addInfo.Add(1);
  417. addInfo.AddRange(Encoding.GetEncoding("GB2312").GetBytes(holder));
  418. }
  419. }
  420. else if (_cardAppType == CardAppType.RfCard)
  421. {
  422. if (cardClass == 3 && cardType == 3)
  423. {
  424. addInfo.Add(1);
  425. addInfo.AddRange(Encoding.GetEncoding("GB2312").GetBytes(holder));
  426. }
  427. }
  428. result.AdditionalInfo = addInfo;
  429. result.AdditionalInfoLength = Convert.ToByte(addInfo.Count);
  430. }
  431. int reserveAmount = reservedBalance * 100;
  432. result.CardBalance = request.Balance - reserveAmount;
  433. int maxAllowedAmount = Convert.ToInt32(checkResult.MaxAllowedAmount);
  434. result.MaxAllowedAmount = request.Balance - reserveAmount > maxAllowedAmount ? maxAllowedAmount : request.Balance - reserveAmount;
  435. result.PhysicalCardNo = long.Parse(
  436. ByteArrayToString(
  437. StringToByteArray(request.PhysicalCardNo).Reverse().ToArray()), System.Globalization.NumberStyles.HexNumber);
  438. _terminal.Write(result);
  439. }
  440. private List<byte> GenerateAdditionalInfo(SpsDbManager.ResultSet.CardState cardState)
  441. {
  442. if (cardState == SpsDbManager.ResultSet.CardState.NoInfo)
  443. {
  444. return Encoding.GetEncoding("GB2312").GetBytes("验卡失败,无此卡").ToList();
  445. }
  446. else if (cardState == SpsDbManager.ResultSet.CardState.Lost)
  447. {
  448. return Encoding.GetEncoding("GB2312").GetBytes("卡已锁定!").ToList();
  449. }
  450. return new List<byte>();
  451. }
  452. #endregion
  453. #region Authorization 授权处理
  454. public void HandlAuthorization(AuthRequest request)
  455. {
  456. ResetTimer();
  457. var currentNozzleNo = request.FuelingPoint.NozzleNo;
  458. int logicId;
  459. bool getResult = _terminal.NozzleLogicIdDict.TryGetValue(currentNozzleNo, out logicId);
  460. if (!getResult)
  461. logicId = 1;
  462. //var logicId = _terminal.NozzleLogicIdDict[currentNozzleNo];
  463. _pumpState = HengshanPumpState.Calling;
  464. _terminal.UpdatePumpState(PumpId, logicId, LogicalDeviceState.FDC_CALLING);
  465. _lastPumpState = _pumpState;
  466. SendAuthResult(request);
  467. _pumpState = HengshanPumpState.Authorized;
  468. _terminal.UpdatePumpState(PumpId, logicId, LogicalDeviceState.FDC_AUTHORISED);
  469. _lastPumpState = _pumpState;
  470. }
  471. private void SendAuthResult(AuthRequest request)
  472. {
  473. var result = new AuthResult
  474. {
  475. Prefix = STX,
  476. SourceAddress = request.SourceAddress,
  477. DestinationAddress = request.DestinationAddress,
  478. FrameSqNoByte = request.FrameSqNoByte,
  479. Handle = Convert.ToByte(Command.AuthResult),
  480. Asn = request.Asn,
  481. PosTtc = request.PosTtc,
  482. SeqNo = request.SeqNo,
  483. FuelCode = request.FPCode.ToUInt16(),
  484. ResultCode = HengshanPayTerminal.Support.AuthResultCode.Passed,
  485. AdditionalInfoLength = 0
  486. };
  487. _terminal.Write(result);
  488. }
  489. #endregion
  490. #region Fueling Data 加油数据
  491. public void HandleFuelingData(FuelingDataRequest request)
  492. {
  493. ResetTimer();
  494. var currentNozzleNo = request.FuelingPoint.NozzleNo;
  495. int logicId;
  496. bool getResult = _terminal.NozzleLogicIdDict.TryGetValue(currentNozzleNo, out logicId);
  497. if (!getResult)
  498. logicId = 1;
  499. //var logicId = _terminal.NozzleLogicIdDict[currentNozzleNo];
  500. var cardNo = GetCardNo(request.Asn);
  501. var cardAccount = _dbManager.GetCardAccountInfo(cardNo);
  502. var correctFuelPrice =
  503. GetCorrectFuelPrice(Convert.ToString(request.ProductCode), request.Price, request.Volume, request.Amount);
  504. if (request.CurrentCardInfo == null)
  505. logger.Error("Card info is null!!!");
  506. if (cardAccount == null)
  507. {
  508. logger.Error("card account is null");
  509. _terminal.UpdateFuelingStatus(PumpId, new FdcTransaction
  510. {
  511. Nozzle = new LogicalNozzle(PumpId, 1, Convert.ToByte(logicId), null),
  512. Amount = request.Amount,
  513. Volumn = request.Volume,
  514. Price = correctFuelPrice,//request.Price,
  515. VolumeTotalizer = request.VolumeTotal,
  516. SequenceNumberGeneratedOnPhysicalPump = request.PosTtc//, //I would like to use SeqNo, but TQC pumps always use 0 for it.
  517. //Finished = true //Set FDC transaction as FINISHED
  518. });
  519. }
  520. currentFilling = new FillingInfo
  521. {
  522. PumpId = this.PumpId,
  523. NozzleId = request.FuelingPoint.NozzleNo,//1,
  524. SequenceNo = request.PosTtc,
  525. CardNo = cardNo,
  526. CardType = request.CurrentCardInfo.CardType,
  527. CardBalance = cardAccount == null ? 0 : Convert.ToInt32(cardAccount.Money),
  528. FillingAmount = request.Amount,
  529. Volume = request.Volume,
  530. UnitPrice = correctFuelPrice,
  531. //UnitPrice = request.Price,
  532. FuelProductCode = request.ProductCode,
  533. StartTime =GetDateTime(request.DispenserTime),
  534. EndTime = GetDateTime(request.TransactionEndTime)
  535. };
  536. if (request.CurrentCardInfo.CardType != 0)
  537. {
  538. bool result = _dbManager.InsertAuthInfo(_posApp.PosId, 0, cardNo, request.Amount, request.Volume,
  539. request.DispenserTime, request.TransactionEndTime, request.PosTtc, request.SeqNo, 0x50,
  540. request.ProductCode, correctFuelPrice, request.CurrentCardInfo.CardCtc, 00, Convert.ToByte(PumpId),
  541. Convert.ToByte(PumpId), Convert.ToInt32(cardAccount.Money), request.CurrentCardInfo.CardType,
  542. cardAccount.DiscountNo, 1, request.VolumeTotal);
  543. logger.Debug($"Insert auth info, success? {result}");
  544. }
  545. SendFuelingDataAck(request);
  546. logger.Info($"Filling done, Amount: {request.Amount}, Volume: {request.Volume}, " +
  547. $"corrected price: {correctFuelPrice}, pump price: {request.Price}, ready for payment");
  548. }
  549. private void SendFuelingDataAck(FuelingDataRequest request)
  550. {
  551. var result = new FuelingDataProcessingResult
  552. {
  553. Prefix = STX,
  554. SourceAddress = request.SourceAddress,
  555. DestinationAddress = request.DestinationAddress,
  556. FrameSqNoByte = request.FrameSqNoByte,
  557. Handle = Convert.ToByte(Command.FuelingDataResult),
  558. PosTtc = request.PosTtc,
  559. SeqNo = request.SeqNo,
  560. FPCode = request.FPCode.ToUInt16()
  561. };
  562. _terminal.Write(result);
  563. }
  564. #endregion
  565. #region Payment 支付
  566. private void HandleCardPayment()
  567. {
  568. while (true)
  569. {
  570. logger.Info($"Terminal {PumpId}, in the loop");
  571. if (paymentRequestQueue.Count > 0)
  572. {
  573. logger.Info($"Terminal {PumpId}, Payment request exists, send it! Queue item count: {paymentRequestQueue.Count}");
  574. PaymentRequest request;
  575. if (paymentRequestQueue.TryDequeue(out request))
  576. {
  577. SendPaymentData(request);
  578. }
  579. logger.Info($"Terminal {PumpId}, Payment request queue, item count: {paymentRequestQueue.Count}");
  580. }
  581. else
  582. {
  583. logger.Info($"Terminal {PumpId}, nothing in payment queue, put thread on hold");
  584. mre.Reset();
  585. mre.WaitOne();
  586. }
  587. }
  588. }
  589. public void HandlePaymentRequest(PaymentRequest request)
  590. {
  591. ResetTimer();
  592. if (paymentRequestQueue.Count > 0)
  593. {
  594. if (paymentRequestQueue.Any(r => r.Asn == request.Asn && r.PosTtc == request.PosTtc))
  595. {
  596. logger.Info("Payment request already in the queue");
  597. return;
  598. }
  599. }
  600. else
  601. {
  602. paymentRequestQueue.Enqueue(request);
  603. }
  604. logger.Info($"Terminal {PumpId}, releasing payment request processing thread");
  605. mre.Set();
  606. //SendPaymentData(request);
  607. }
  608. private void SendPaymentData(PaymentRequest request)
  609. {
  610. int paymentAmount = 0;
  611. int fillingAmount = 0;
  612. int fillingPrice = 0;
  613. int productCode = 0;
  614. int volume = 0;
  615. if (DiscountClient != null && currentFilling != null &&
  616. (_posApp.CardAppType == 1 && currentFilling.CardType == 3 || _posApp.CardAppType == 2 && currentFilling.CardType == 1))
  617. {
  618. fillingAmount = currentFilling.FillingAmount;
  619. fillingPrice = currentFilling.UnitPrice;
  620. productCode = currentFilling.FuelProductCode;
  621. volume = currentFilling.Volume;
  622. logger.Info($"Applying fuel discount against customer card, amount: {fillingAmount}, price: {fillingPrice}, code: {productCode}");
  623. DiscountRequest discountRequest = new DiscountRequest();
  624. discountRequest.Barcode = _posApp.GetBarcode(currentFilling.FuelProductCode).ToString();
  625. discountRequest.FillingAmount = Convert.ToDecimal(currentFilling.FillingAmount) / 100;
  626. discountRequest.UnitPrice =
  627. currentFilling.UnitPrice == 151 ?
  628. Convert.ToDecimal( _posApp.CurrentFuelPrices[currentFilling.FuelProductCode.ToString()]) / 100
  629. : Convert.ToDecimal(currentFilling.UnitPrice) / 100;
  630. discountRequest.Volume = Convert.ToDecimal(currentFilling.Volume) / 100;
  631. discountRequest.TimeStamp = DateTime.Now;
  632. discountRequest.CardNo = GetCardNo(request.Asn);
  633. var discountResponse = DiscountClient.CalculatDiscount(discountRequest).Result;
  634. if (discountResponse != null)
  635. {
  636. paymentAmount = Convert.ToInt32(discountResponse.PayAmount * 100);
  637. }
  638. }
  639. else
  640. {
  641. logger.Info($"Possible crash 1");
  642. //var amountAfterDiscount = _dbManager.GetDiscountedAmount(request.Asn, fillingAmount, volume, fillingPrice, productCode);
  643. if (currentFilling != null)
  644. paymentAmount = currentFilling.FillingAmount;//amountAfterDiscount.Real_Mon;
  645. }
  646. if (currentFilling == null)
  647. {
  648. logger.Info($"Current Filling missing, could be reset");
  649. return;
  650. }
  651. if (paymentAmount > currentFilling.FillingAmount)
  652. {
  653. logger.Warn($"This is absurd, payment amount {paymentAmount.ToString()} larger than filling amount");
  654. paymentAmount = fillingAmount;
  655. }
  656. logger.Info($"payment amount is: {paymentAmount}");
  657. PaymentData data = new PaymentData
  658. {
  659. Prefix = STX,
  660. SourceAddress = request.SourceAddress,
  661. DestinationAddress = request.DestinationAddress,
  662. FrameSqNoByte = request.FrameSqNoByte,
  663. Handle = Convert.ToByte(Command.PaymentData),
  664. Asn = request.Asn,
  665. PosTtc = request.PosTtc,
  666. FillingAmount = fillingAmount,
  667. PayAmount = paymentAmount == 0? fillingAmount : paymentAmount,
  668. FillingVolume = volume,
  669. Price = Convert.ToUInt16(fillingPrice),
  670. ProductCode = Convert.ToUInt16(productCode),
  671. C_Name = "00000000000000000000000000000000",
  672. FPCode = request.FPCode.ToUInt16(),
  673. Result = HengshanPayTerminal.Support.PaymentProcessingResult.Ok,
  674. AdditionalInfoLength = 0
  675. };
  676. _terminal.Write(data);
  677. mre.WaitOne();
  678. }
  679. #endregion
  680. #region Transaction Data 交易数据
  681. public async Task HandleTransactionData(TransactionDataRequest request)
  682. {
  683. ResetTimer();
  684. var currentNozzleNo = request.FuelingPoint.NozzleNo;
  685. int logicId;
  686. bool getResult = _terminal.NozzleLogicIdDict.TryGetValue(currentNozzleNo, out logicId);
  687. if (!getResult)
  688. logicId = 1;
  689. //var logicId = _terminal.NozzleLogicIdDict[currentNozzleNo];
  690. logger.Info($"*** Handle TransactionData {request.Handle}, Pump {PumpId} ***");
  691. byte trxType = request.TrxType;
  692. var cardNo = GetCardNo(request.Asn);
  693. var cardAccount = _dbManager.GetCardAccountInfo(cardNo);
  694. if (cardAccount == null)
  695. {
  696. logger.Info($"Terminal {PumpId}, card account queried for card no: {cardNo}");
  697. }
  698. var creditResult = _dbManager.GetCredits(request.ProductCode, cardNo, request.FillingAmount, request.Volume);
  699. logger.Debug("Credit queried");
  700. bool trxExists = _dbManager.CheckIfTransactionExists(request.TrxTime, request.DitTTC, request.FuelingPoint.PumpNo, trxType);
  701. logger.Debug($"Transaction Existence checked, Exists? {trxExists}");
  702. var operatorCardNo = GetCardNo(request.PsamAsn);
  703. bool ackSent = false;
  704. var correctedFuelPrice =
  705. GetCorrectFuelPrice(Convert.ToString(request.ProductCode), request.Price, request.Volume, request.FillingAmount);
  706. //Make sure there is not an existing payment trx record.
  707. if (!trxExists)
  708. {
  709. logger.Info("No existing record");
  710. int payModeId = request.PaymentMethodLocation; //100;
  711. string payModeNo = string.Empty;
  712. int stationNo = 1;
  713. if (_posApp.StationInfo != null)
  714. stationNo = _posApp.StationInfo.StationNo;
  715. uint volumeTotal = request.VolumeTotal < 0 ? 0 : Convert.ToUInt32(request.VolumeTotal);
  716. var newGid = _dbManager.AddTrade(
  717. stationNo,
  718. cardNo,
  719. operatorCardNo,
  720. payModeId,
  721. payModeNo,
  722. trxType,
  723. Convert.ToString(request.ProductCode),
  724. correctedFuelPrice,//request.Price,
  725. request.Volume,
  726. request.FillingAmount,
  727. request.PaymentAmount,
  728. request.CardBalance,
  729. request.CurrentCardInfo.CardType,
  730. request.CurrentCardInfo.CardCtc,
  731. GetDateTime(request.TrxTime),
  732. GetDateTime(request.TrxEndTime),
  733. request.DitTTC,
  734. request.SeqNo,
  735. _posApp.GetNextBillNo(), //billNo
  736. request.FuelingPoint.NozzleNo,
  737. request.FuelingPoint.PumpNo,
  738. Convert.ToInt32(request.TerminalId.TrimStart('0')),
  739. volumeTotal,
  740. 0, //discountNo
  741. 1001,
  742. 1,
  743. request.PsamAsn, //PsamAsn
  744. uint.Parse(request.PsamTac, System.Globalization.NumberStyles.HexNumber),//PsamTac
  745. request.PsamTid,//psam tid
  746. uint.Parse(request.PsamTtc, System.Globalization.NumberStyles.HexNumber),//psam ttc
  747. uint.Parse(request.Tac, System.Globalization.NumberStyles.HexNumber),//tac
  748. uint.Parse(request.Gmac, System.Globalization.NumberStyles.HexNumber),//gmac
  749. uint.Parse(request.Tmac, System.Globalization.NumberStyles.HexNumber),//tmac
  750. 0, //Integral
  751. Convert.ToInt32(_posApp.CurrentShiftNo),
  752. "000000",
  753. "000000",
  754. "",
  755. 0);
  756. logger.Info($"Trdinfo added, new Gid: {newGid}, TrdType: {trxType}, CardNo: {request.Asn}, CTC: {request.CurrentCardInfo.CardCtc}");
  757. if (trxType == 0x01)
  758. {
  759. _dbManager.InsertGrayInfo(
  760. cardNo,
  761. payModeId,
  762. trxType,
  763. Convert.ToString(request.ProductCode),
  764. correctedFuelPrice,//request.Price
  765. request.Volume,
  766. request.FillingAmount,
  767. request.PaymentAmount,
  768. request.CardBalance,
  769. request.CurrentCardInfo.CardCtc,
  770. request.TrxTime,
  771. request.TrxEndTime,
  772. Convert.ToUInt32(request.DitTTC),
  773. request.SeqNo,
  774. request.FuelingPoint.NozzleNo,
  775. request.FuelingPoint.PumpNo,
  776. request.TerminalId,
  777. Convert.ToUInt64(request.VolumeTotal),
  778. 0,
  779. request.PsamAsn,
  780. uint.Parse(request.PsamTac, System.Globalization.NumberStyles.HexNumber),//PsamTac//request.PsamTac,
  781. request.PsamTid,
  782. uint.Parse(request.PsamTtc, System.Globalization.NumberStyles.HexNumber),//psam ttc
  783. uint.Parse(request.Tac, System.Globalization.NumberStyles.HexNumber),//tac
  784. uint.Parse(request.Gmac, System.Globalization.NumberStyles.HexNumber),//gmac
  785. uint.Parse(request.Tmac, System.Globalization.NumberStyles.HexNumber),//tmac
  786. 0);
  787. logger.Info("Gray info inserted");
  788. }
  789. else if (trxType == 0x02)
  790. {
  791. var releaseResult = _dbManager.ReleaseGrayCard(cardNo, request.CurrentCardInfo.CardCtc, request.TrxTime);
  792. logger.Info($"Gray info deleted, card released success? {releaseResult}");
  793. }
  794. _dbManager.UpdateCardInfo(cardNo, request.FillingAmount, request.CardBalance, request.CurrentCardInfo.CardType,
  795. creditResult.IntegralResult, 1);
  796. logger.Debug("Card Info updated");
  797. _dbManager.DeleteAuthInfo(request.FuelingPoint.PumpNo, request.DitTTC, request.TrxTime);
  798. logger.Debug("Auth Info deleted");
  799. if (trxType == 2)
  800. {
  801. logger.Info($"Trying to find the gray trade, ASN: {request.Asn}, CTC: {request.CurrentCardInfo.CardCtc}");
  802. var grayTrd = _dbManager.GetGrayTrdInfo(request.Asn, request.CurrentCardInfo.CardCtc, request.FuelingPoint.PumpNo);
  803. if (grayTrd != null)
  804. {
  805. logger.Info($"Found gray trade for card: {request.Asn}");
  806. currentFilling = new FillingInfo
  807. {
  808. PumpId = this.PumpId,
  809. NozzleId = request.FuelingPoint.NozzleNo,//1,
  810. SequenceNo = request.DitTTC,
  811. CardNo = cardNo,
  812. CardBalance = request.CardBalance,//Convert.ToInt32(cardAccount.Money),
  813. FillingAmount = grayTrd.Mon.HasValue ? grayTrd.Mon.Value : request.FillingAmount,
  814. PayAmount = request.PaymentAmount,
  815. Volume = request.Volume,
  816. UnitPrice = correctedFuelPrice,//request.Price,
  817. FuelProductCode = int.Parse(grayTrd.CommId), //request.ProductCode,
  818. StartTime = GetDateTime(request.TrxTime),
  819. EndTime = GetDateTime(request.TrxEndTime),
  820. VolumeTotal = Convert.ToInt32(grayTrd.EndPumpId)//request.VolumeTotal
  821. };
  822. }
  823. else
  824. {
  825. currentFilling = new FillingInfo
  826. {
  827. PumpId = this.PumpId,
  828. NozzleId = request.FuelingPoint.NozzleNo,//1,
  829. SequenceNo = request.DitTTC,
  830. CardNo = cardNo,
  831. CardBalance = request.CardBalance,//Convert.ToInt32(cardAccount.Money),
  832. FillingAmount = request.FillingAmount,
  833. PayAmount = request.PaymentAmount,
  834. Volume = request.Volume,
  835. UnitPrice = correctedFuelPrice,//request.Price,
  836. FuelProductCode = request.ProductCode,
  837. StartTime = GetDateTime(request.TrxTime),
  838. EndTime = GetDateTime(request.TrxEndTime),
  839. VolumeTotal = request.VolumeTotal
  840. };
  841. }
  842. }
  843. else
  844. {
  845. currentFilling = new FillingInfo
  846. {
  847. PumpId = this.PumpId,
  848. NozzleId = request.FuelingPoint.NozzleNo,//1,
  849. SequenceNo = request.DitTTC,
  850. CardNo = cardNo,
  851. CardBalance = request.CardBalance,//Convert.ToInt32(cardAccount.Money),
  852. FillingAmount = request.FillingAmount,
  853. PayAmount = request.PaymentAmount,
  854. Volume = request.Volume,
  855. UnitPrice = correctedFuelPrice,//request.Price,
  856. FuelProductCode = request.ProductCode,
  857. StartTime = GetDateTime(request.TrxTime),
  858. EndTime = GetDateTime(request.TrxEndTime),
  859. VolumeTotal = request.VolumeTotal
  860. };
  861. }
  862. //Customer card, submit it.
  863. if ((_posApp.CardAppType == 1 && request.CurrentCardInfo.CardType == 3
  864. || _posApp.CardAppType == 2 && request.CurrentCardInfo.CardType == 1) && request.TrxType != 2)
  865. {
  866. logger.Info($"Pump {PumpId}, there is a customer card transaction pending for upload, ttc: {currentFilling.SequenceNo}");
  867. if (request.FillingAmount != 0)
  868. _posApp.AddCustomerCardFilling(currentFilling);
  869. }
  870. if (request.FillingAmount != 0 && request.TrxType != 2)
  871. {
  872. if (_posApp != null)
  873. {
  874. //2023-08-22
  875. SendTrasactionDataAck(request);
  876. ackSent = true;
  877. logger.Info("TransactionData ack sent");
  878. //Should first upload the record to cloud...
  879. logger.Info("Start force upload");
  880. //_ = _posApp?.SpsDataCourier.TriggerTransacationLookup(newGid);
  881. await _posApp?.SpsDataCourier.ForceTransactionUploadAsync(newGid).ContinueWith(t =>
  882. {
  883. logger.Info("Push transaction to FDC Server");
  884. _terminal.UpdateFuelingStatus(PumpId, new FdcTransaction
  885. {
  886. Nozzle = new LogicalNozzle(PumpId, 1, Convert.ToByte(logicId), null),
  887. Amount = request.FillingAmount,
  888. Volumn = request.Volume,
  889. Price = correctedFuelPrice,//request.Price,
  890. VolumeTotalizer = request.VolumeTotal,
  891. SequenceNumberGeneratedOnPhysicalPump = request.DitTTC, //I would like to use SeqNo, but TQC pumps always use 0 for it.
  892. Finished = true //Set FDC transaction as FINISHED
  893. });
  894. _pumpState = HengshanPumpState.Idle;
  895. _terminal.UpdatePumpState(PumpId, logicId, LogicalDeviceState.FDC_READY);
  896. _lastPumpState = _pumpState;
  897. });
  898. }
  899. //logger.Info("Push transaction to FDC Server");
  900. //_terminal.UpdateFuelingStatus(PumpId, new FdcTransaction
  901. //{
  902. // Nozzle = new LogicalNozzle(PumpId, 1, 1, null),
  903. // Amount = request.FillingAmount,
  904. // Volumn = request.Volume,
  905. // Price = request.Price,
  906. // VolumeTotalizer = request.VolumeTotal,
  907. // SequenceNumberGeneratedOnPhysicalPump = request.DitTTC, //I would like to use SeqNo, but TQC pumps always use 0 for it.
  908. // Finished = true //Set FDC transaction as FINISHED
  909. //});
  910. }
  911. //_pumpState = HengshanPumpState.Idle;
  912. //_terminal.UpdatePumpState(PumpId, LogicalDeviceState.FDC_READY);
  913. //_lastPumpState = _pumpState;
  914. }
  915. if (!ackSent)
  916. {
  917. logger.Info($"Pump {PumpId}, TransactionData ack not sent, send it now");
  918. SendTrasactionDataAck(request);
  919. }
  920. if (currentFilling != null)
  921. {
  922. lastFilling = new FillingInfo
  923. {
  924. FillingAmount = currentFilling.FillingAmount,
  925. Volume = currentFilling.Volume,
  926. UnitPrice = currentFilling.UnitPrice,
  927. FuelProductCode = currentFilling.FuelProductCode
  928. };
  929. }
  930. else
  931. {
  932. logger.Info($"Pump {PumpId}, currentFilling is null");
  933. }
  934. if (currentFilling != null)
  935. {
  936. //重置当前的Filling
  937. if (currentFilling.SequenceNo == request.DitTTC)
  938. {
  939. logger.Info($"Pump {PumpId}, reset current filling, ttc: {currentFilling.SequenceNo}");
  940. currentFilling = null;
  941. }
  942. }
  943. }
  944. private void CheckTransactionType(byte trxType)
  945. {
  946. if (trxType == 0x00)
  947. {
  948. logger.Info("Normal card transaction");
  949. }
  950. else if (trxType == 0x01)
  951. {
  952. logger.Info("Gray card transaction");
  953. }
  954. else if (trxType == 0x02)
  955. {
  956. logger.Info("Ungray card transaction");
  957. }
  958. else if (trxType == 0x03)
  959. {
  960. logger.Info("Indoor card payment transaction");
  961. }
  962. else if (trxType == 0x04)
  963. {
  964. logger.Info("POS auth transaction (cash)");
  965. }
  966. else if (trxType == 0x05)
  967. {
  968. logger.Info("Cancel auth card transaction");
  969. }
  970. else if (trxType == 0x06)
  971. {
  972. logger.Info("Non card transaction");
  973. }
  974. else if (trxType == 0x07)
  975. {
  976. logger.Info("Cancel auth non card transaction");
  977. }
  978. else if (trxType == 0x08)
  979. {
  980. logger.Info("Fuel price download transaction");
  981. }
  982. else if (trxType == 0x09)
  983. {
  984. logger.Info("Other card release transaction");
  985. }
  986. else if (trxType == 0x0A)
  987. {
  988. logger.Info("Change fuel type transaction");
  989. }
  990. else if (trxType == 0x0B)
  991. {
  992. logger.Info("POS bank card transaction");
  993. }
  994. else
  995. {
  996. logger.Info("Unknown transaction type");
  997. }
  998. }
  999. private void SendTrasactionDataAck(TransactionDataRequest request)
  1000. {
  1001. TransactionDataAck ack = new TransactionDataAck
  1002. {
  1003. Prefix = STX,
  1004. SourceAddress = request.SourceAddress,
  1005. DestinationAddress = request.DestinationAddress,
  1006. FrameSqNoByte = request.FrameSqNoByte,
  1007. Handle = Convert.ToByte(Command.TransactionDataAck),
  1008. TerminalId = request.TerminalId,
  1009. Result = 0,
  1010. UnspecifiedField1 = 0
  1011. };
  1012. _terminal.Write(ack);
  1013. }
  1014. private ushort GetCorrectFuelPrice(string fuelNo, int price, int volume, int amount)
  1015. {
  1016. try
  1017. {
  1018. var recordedPrice = Convert.ToInt32(_posApp.CurrentFuelPrices[fuelNo]);
  1019. if (recordedPrice != price)
  1020. {
  1021. logger.Info($"Price: {price} on pump: {PumpId}, is different from system recorded price: {recordedPrice}");
  1022. int calculatedAmount = price * volume;
  1023. int candidateAmount = recordedPrice * volume;
  1024. double calculatedDiff = Math.Abs(calculatedAmount - amount * 100);
  1025. double candidateDiff = Math.Abs(candidateAmount - amount * 100);
  1026. if (candidateDiff < calculatedDiff)
  1027. {
  1028. logger.Info($"Returning system recorded price: {recordedPrice}");
  1029. return Convert.ToUInt16(recordedPrice);
  1030. }
  1031. return Convert.ToUInt16(price);
  1032. }
  1033. }
  1034. catch (Exception ex)
  1035. {
  1036. logger.Error("Error in getting correct fuel price ", ex.ToString());
  1037. }
  1038. return Convert.ToUInt16(price);
  1039. }
  1040. #endregion
  1041. #region Lock/Unlock Pump 锁定/解锁油机
  1042. public void LockUnlockPump(LockUnlockOperation operation)
  1043. {
  1044. var request = new LockOrUnlockPumpRequest
  1045. {
  1046. Prefix = STX,
  1047. Handle = Convert.ToByte(Command.LockOrUnlockPump),
  1048. FPCode = EncodeFPCodeString(PumpId, PumpId),
  1049. OperationType = operation
  1050. };
  1051. PrepareSendMessage(request);
  1052. }
  1053. public void HandleLockUnlockPumpResult(LockOrUnlockPumpAck lockUnlockPumpResult)
  1054. {
  1055. if (lockUnlockPumpResult.DispenserState == DispenserState.Closed)
  1056. {
  1057. logger.Info($"Current pump {PumpId} is locked!");
  1058. //... should notify POS then.
  1059. }
  1060. else if (lockUnlockPumpResult.DispenserState == DispenserState.Idle)
  1061. {
  1062. logger.Info($"Current pump {PumpId} is idle (unlocked)!");
  1063. //... should notify POS then.
  1064. }
  1065. }
  1066. #endregion
  1067. #region Request Data 加油机请求下载数据
  1068. public void HandleDataDownloadRequest(DataDownloadRequest request)
  1069. {
  1070. ResetTimer();
  1071. //Reset
  1072. VersionedListedCard = null;
  1073. if (request.DataContentType == DataContentType.FuelPriceList)
  1074. {
  1075. logger.Info($"Terminal {PumpId} initiates fuel price list download");
  1076. }
  1077. else if (request.DataContentType == DataContentType.StationGeneralInfo)
  1078. {
  1079. logger.Info($"Terminal {PumpId} initiates station general info download");
  1080. }
  1081. else if (request.DataContentType == DataContentType.Whitelist)
  1082. {
  1083. logger.Info($"Terminal {PumpId} initiates whitelist download");
  1084. }
  1085. else if (request.DataContentType == DataContentType.NewlyAddedBlacklist)
  1086. {
  1087. logger.Info($"Terminal {PumpId} initiates newly added blacklist download");
  1088. }
  1089. else if (request.DataContentType == DataContentType.NewlyDeletedBlacklist)
  1090. {
  1091. logger.Info($"Terminal {PumpId} initiates newly deleted blacklist download");
  1092. }
  1093. else if (request.DataContentType == DataContentType.BaseBlacklist)
  1094. {
  1095. logger.Info($"Terminal {PumpId} initiates base blacklist download");
  1096. }
  1097. else if (request.DataContentType == DataContentType.StationGeneralInfo)
  1098. {
  1099. logger.Info($"Terminal {PumpId} initiates station general info download");
  1100. }
  1101. SendDataLength(request);
  1102. }
  1103. public void SendDataLength(DataDownloadRequest request)
  1104. {
  1105. DataBytesLength response = new DataBytesLength();
  1106. response.Prefix = STX;
  1107. response.SourceAddress = request.SourceAddress;
  1108. response.DestinationAddress = request.DestinationAddress;
  1109. response.Handle = (byte)Command.DataBytesLength;
  1110. response.FrameSqNoByte = request.FrameSqNoByte;
  1111. if (request.DataContentType == DataContentType.FuelPriceList)
  1112. {
  1113. response.DataContentType = request.DataContentType;
  1114. response.DataLength = 0x32;
  1115. logger.Info($"Fuel Price Update, Data Length: {response.DataLength}");
  1116. }
  1117. else if (request.DataContentType == DataContentType.Whitelist)
  1118. {
  1119. response.DataContentType = DataContentType.Whitelist;
  1120. var result = _dbManager.GetWhitelistedCards(_cardAppType);
  1121. if (result != null)
  1122. {
  1123. response.DataLength = 16 + 10 * result.Count;
  1124. logger.Info($"WhiteList, Data Length: {response.DataLength}");
  1125. }
  1126. }
  1127. else if (request.DataContentType == DataContentType.NewlyAddedBlacklist)
  1128. {
  1129. response.DataContentType = DataContentType.NewlyAddedBlacklist;
  1130. var result = _dbManager.GetNewlyAddedBlacklistedCards(_cardAppType);
  1131. if (result != null)
  1132. {
  1133. response.DataLength = 16 + 10 * result.Count;
  1134. logger.Info($"Incremental Black List, Data Length: {response.DataLength}");
  1135. }
  1136. }
  1137. else if (request.DataContentType == DataContentType.NewlyDeletedBlacklist)
  1138. {
  1139. response.DataContentType = DataContentType.NewlyDeletedBlacklist;
  1140. var result = _dbManager.GetNewlyDeletedBlacklistedCards(_cardAppType);
  1141. if (result != null)
  1142. {
  1143. response.DataLength = 16 + 10 * result.Count;
  1144. logger.Info($"Decremental Black List, Data Length: {response.DataLength}");
  1145. }
  1146. }
  1147. else if (request.DataContentType == DataContentType.BaseBlacklist)
  1148. {
  1149. response.DataContentType = DataContentType.BaseBlacklist;
  1150. var result = _dbManager.GetBaseBlacklistedCards(_cardAppType);
  1151. if (result != null)
  1152. {
  1153. response.DataLength = 16 + 10 * result.Count;
  1154. logger.Info($"Base Black List, Data Length: {response.DataLength}");
  1155. }
  1156. }
  1157. else if (request.DataContentType == DataContentType.StationGeneralInfo)
  1158. {
  1159. response.DataContentType = DataContentType.StationGeneralInfo;
  1160. var result = _dbManager.GetPumpInfo(Convert.ToByte(PumpId));
  1161. if (result != null && result.Count > 0)
  1162. {
  1163. response.DataLength = 6;
  1164. }
  1165. logger.Info($"Station General Info, Data Length: {response.DataLength}");
  1166. }
  1167. _terminal.Write(response);
  1168. }
  1169. #endregion
  1170. #region Request Data Content 加油机申请下载数据的内容
  1171. public void HandleDataContentRequest(DataContentRequest request)
  1172. {
  1173. ResetTimer();
  1174. if (request.DataContentType == DataContentType.FuelPriceList)
  1175. {
  1176. logger.Info($"Terminal {PumpId} downloads fuel price list content");
  1177. }
  1178. SendDataContent(request);
  1179. }
  1180. public void SendDataContent(DataContentRequest request)
  1181. {
  1182. DataContent response = new DataContent();
  1183. response.Prefix = STX;
  1184. response.SourceAddress = request.SourceAddress;
  1185. response.DestinationAddress = request.DestinationAddress;
  1186. response.FrameSqNoByte = request.FrameSqNoByte;
  1187. response.Handle = (byte)Command.DataContent;
  1188. //Fuel price, 油品油价列表
  1189. if (request.DataContentType == DataContentType.FuelPriceList)
  1190. {
  1191. logger.Info($"Terminal downloads fuel price, Source: {request.SourceAddress}");
  1192. response.DataContentType = DataContentType.FuelPriceList;
  1193. response.SegmentOffset = request.SegmentOffset;
  1194. var result = _dbManager.GetFuelPriceChangeConfig(PumpId);
  1195. if (result.Count > 0)
  1196. {
  1197. var priceChangeRecordCount = result.Count;
  1198. if (priceChangeRecordCount <= 1)
  1199. {
  1200. var priceChangeRecord = result.First();
  1201. FuelPriceRecord record = new FuelPriceRecord();
  1202. record.Version = Convert.ToByte(priceChangeRecord.Ver);
  1203. record.EffectiveTime = priceChangeRecord.EffeTime;
  1204. record.RecordCount = Convert.ToByte(priceChangeRecordCount);
  1205. record.NozzleNo = Convert.ToByte(priceChangeRecord.LogicId);
  1206. record.FuelProductCode = Convert.ToUInt16(priceChangeRecord.OilId);
  1207. record.FuelProductName =
  1208. ByteArrayToString(Encoding.GetEncoding("GB2312").GetBytes(priceChangeRecord.OilName)).PadRight(64, '0');
  1209. record.Density = priceChangeRecord.Density == "" ? 0 : Convert.ToInt32(priceChangeRecord.Density);
  1210. record.ValidPriceCount = 1;
  1211. record.Price1 = priceChangeRecord.Price > UInt16.MaxValue ? UInt16.MaxValue : Convert.ToUInt16(priceChangeRecord.Price);
  1212. _terminal.SetRealPrice(PumpId, Convert.ToInt32(priceChangeRecord.Price));
  1213. response.Content = parser.SerializeInnerElement(record).ToList();
  1214. if (response.Content.Count % 16 == 0)
  1215. {
  1216. response.SegmentCount = Convert.ToByte(response.Content.Count / 16);
  1217. }
  1218. else
  1219. {
  1220. response.SegmentCount = Convert.ToByte(response.Content.Count / 16 + 1);
  1221. }
  1222. }
  1223. else
  1224. {
  1225. logger.Info("There are multiple price change records, seriously? ...");
  1226. var first = result.First();
  1227. MultiFuelPriceRecord record = new MultiFuelPriceRecord();
  1228. record.Version = Convert.ToByte(first.Ver);
  1229. record.EffectiveTime = first.EffeTime;
  1230. record.RecordCount = Convert.ToByte(result.Count);
  1231. record.FirstNozzleNo = Convert.ToByte(result[0].LogicId);
  1232. record.FirstFuelProductCode = Convert.ToUInt16(result[0].OilId);
  1233. record.FirstFuelProductName =
  1234. ByteArrayToString(Encoding.GetEncoding("GB2312").GetBytes(result[0].OilName)).PadRight(64, '0');
  1235. record.FirstDensity = result[0].Density == "" ? 0 : Convert.ToInt32(result[0].Density);
  1236. record.FirstValidPriceCount = 1;
  1237. record.FirstPrice1 = Convert.ToUInt16(result[0].Price);
  1238. record.SecondNozzleNo = Convert.ToByte(result[1].LogicId);
  1239. record.SecondFuelProductCode = Convert.ToUInt16(result[1].OilId);
  1240. record.SecondFuelProductName =
  1241. ByteArrayToString(Encoding.GetEncoding("GB2312").GetBytes(result[1].OilName)).PadRight(64, '0');
  1242. record.SecondDensity = result[1].Density == "" ? 0 : Convert.ToInt32(result[1].Density);
  1243. record.SecondValidPriceCount = 1;
  1244. record.SecondPrice1 = Convert.ToUInt16(result[1].Price);
  1245. response.Content = new List<byte>();
  1246. response.Content.AddRange(parser.SerializeInnerElement(record).ToList());
  1247. if (response.Content.Count % 16 == 0)
  1248. {
  1249. response.SegmentCount = Convert.ToByte(response.Content.Count / 16);
  1250. }
  1251. else
  1252. {
  1253. response.SegmentCount = Convert.ToByte(response.Content.Count / 16 + 1);
  1254. }
  1255. }
  1256. }
  1257. }
  1258. //Incremental blacklist, 新增(增量)黑名单
  1259. else if (request.DataContentType == DataContentType.NewlyAddedBlacklist)
  1260. {
  1261. logger.Info($"Terminal downloading newly added (incremental) blacklist, Source: {request.SourceAddress}, " +
  1262. $"Segment offset: {request.SegmentOffset}, Segment count: {request.SegmentCount}");
  1263. response.DataContentType = DataContentType.NewlyAddedBlacklist;
  1264. response.SegmentOffset = request.SegmentOffset;
  1265. response.SegmentCount = request.SegmentCount;
  1266. var cards = _dbManager.GetNewlyAddedBlacklistedCards(_cardAppType);
  1267. var versions = _dbManager.GetDataVersions();
  1268. if (versions.Count > 0 && cards.Count > 0)
  1269. {
  1270. var versionInfo = versions.First(v => v.VersionType == VersionType.NewlyAddedBlacklistVersion);
  1271. ListedCardRecord listedCardRecord = null;
  1272. if (versionInfo != null)
  1273. {
  1274. listedCardRecord = new ListedCardRecord();
  1275. listedCardRecord.Version = versionInfo.VersionNo;
  1276. listedCardRecord.ValidStartDate = versionInfo.Effectivetime.ToString("yyyyMMdd");
  1277. listedCardRecord.ExpiryDate = versionInfo.ExpiredTime.ToString("yyyyMMdd");
  1278. listedCardRecord.Region = Convert.ToString(ushort.MaxValue);
  1279. listedCardRecord.CardCount = cards.Count;
  1280. }
  1281. if (VersionedListedCard == null)
  1282. {
  1283. logger.Info("Formatting card list (newly added)");
  1284. VersionedListedCard = new VersionedListedCard(versionInfo.VersionNo, versionInfo.Effectivetime.ToString("yyyyMMdd"),
  1285. versionInfo.ExpiredTime.ToString("yyyyMMdd"), cards.Count, cards.ToList());
  1286. }
  1287. if (request.SegmentOffset == 0)
  1288. {
  1289. if (request.SegmentCount == 1)
  1290. {
  1291. logger.Info("Terminal requesting just one segment of newly added blacklist data");
  1292. response.Content = parser.SerializeInnerElement(listedCardRecord).ToList();
  1293. }
  1294. else
  1295. {
  1296. logger.Info($"Terminal requests Newly-added blacklist, Segment offset: 0000, Segment count: {request.SegmentCount}");
  1297. var versionInfoBytes = parser.SerializeInnerElement(listedCardRecord);
  1298. var cardBytes = StringToByteArray(VersionedListedCard.AllCards);
  1299. if (versionInfoBytes.Length + cardBytes.Length >= 160)
  1300. {
  1301. var bytes = new byte[160];
  1302. Buffer.BlockCopy(versionInfoBytes, 0, bytes, 0, versionInfoBytes.Length);
  1303. Buffer.BlockCopy(cardBytes, 0, bytes, versionInfoBytes.Length, 160 - versionInfoBytes.Length);
  1304. response.Content = bytes.ToList();
  1305. }
  1306. else
  1307. {
  1308. var bytes = new byte[160];
  1309. Buffer.BlockCopy(versionInfoBytes, 0, bytes, 0, versionInfoBytes.Length);
  1310. Buffer.BlockCopy(cardBytes, 0, bytes, versionInfoBytes.Length, cardBytes.Length);
  1311. response.Content = bytes.ToList();
  1312. }
  1313. }
  1314. }
  1315. else
  1316. {
  1317. var bytes = new byte[160];
  1318. int cardsToBeSkipped = request.SegmentOffset > 10 ? request.SegmentOffset / 10 : 0;
  1319. logger.Info($"Newly-added blacklist, Segment Offset: {request.SegmentOffset}, Cards to be skipped: {cardsToBeSkipped * 16}");
  1320. if (VersionedListedCard.CardCount <= 16)
  1321. {
  1322. var cardBytes = StringToByteArray(VersionedListedCard.AllCards);
  1323. Buffer.BlockCopy(cardBytes, 0, bytes, 0, cardBytes.Length);
  1324. response.Content = bytes.ToList();
  1325. }
  1326. else
  1327. {
  1328. IEnumerable<ListedCard> currentCards = VersionedListedCard.ListedCards.Skip(cardsToBeSkipped * 16).Take(16);
  1329. StringBuilder sb = new StringBuilder();
  1330. foreach (var card in currentCards)
  1331. {
  1332. sb.Append(card.CardNo.PadLeft(20, '0'));
  1333. logger.Info(card.CardNo);
  1334. }
  1335. var cardBytes = StringToByteArray(sb.ToString());
  1336. Buffer.BlockCopy(cardBytes, 0, bytes, 0, cardBytes.Length);
  1337. response.Content = bytes.ToList();
  1338. }
  1339. }
  1340. }
  1341. }
  1342. //Decremental blacklist, 新删(减量)黑名单
  1343. else if (request.DataContentType == DataContentType.NewlyDeletedBlacklist)
  1344. {
  1345. logger.Info($"Terminal downloading newly deleted blacklist, Source: {request.SourceAddress}");
  1346. response.DataContentType = DataContentType.NewlyDeletedBlacklist;
  1347. response.SegmentOffset = request.SegmentOffset;
  1348. response.SegmentCount = request.SegmentCount;
  1349. var cards = _dbManager.GetNewlyDeletedBlacklistedCards(_cardAppType);
  1350. var versions = _dbManager.GetDataVersions();
  1351. if (versions.Count > 0 && cards.Count > 0)
  1352. {
  1353. var versionInfo = versions.First(v => v.VersionType == VersionType.NewlyDeletedBlacklistVersion);
  1354. ListedCardRecord record = null;
  1355. if (versionInfo != null)
  1356. {
  1357. record = new ListedCardRecord();
  1358. record.Version = versionInfo.VersionNo;
  1359. record.ValidStartDate = versionInfo.Effectivetime.ToString("yyyyMMdd");
  1360. record.ExpiryDate = versionInfo.ExpiredTime.ToString("yyyyMMdd");
  1361. record.Region = Convert.ToString(ushort.MaxValue);
  1362. record.CardCount = cards.Count;
  1363. }
  1364. if (VersionedListedCard == null)
  1365. {
  1366. logger.Info("Formatting card list (newly deleted)");
  1367. VersionedListedCard = new VersionedListedCard(versionInfo.VersionNo, versionInfo.Effectivetime.ToString("yyyyMMdd"),
  1368. versionInfo.ExpiredTime.ToString("yyyyMMdd"), cards.Count, cards.ToList());
  1369. }
  1370. if (request.SegmentOffset == 0)
  1371. {
  1372. if (request.SegmentCount == 1)
  1373. {
  1374. response.Content = parser.SerializeInnerElement(record).ToList();
  1375. }
  1376. else
  1377. {
  1378. var versionInfoBytes = parser.SerializeInnerElement(record);
  1379. var cardBytes = StringToByteArray(VersionedListedCard.AllCards);
  1380. if (versionInfoBytes.Length + cardBytes.Length >= 160)
  1381. {
  1382. var bytes = new byte[160];
  1383. Buffer.BlockCopy(versionInfoBytes, 0, bytes, 0, versionInfoBytes.Length);
  1384. Buffer.BlockCopy(cardBytes, 0, bytes, versionInfoBytes.Length, 160 - versionInfoBytes.Length);
  1385. response.Content = bytes.ToList();
  1386. }
  1387. else
  1388. {
  1389. var bytes = new byte[160];
  1390. Buffer.BlockCopy(versionInfoBytes, 0, bytes, 0, versionInfoBytes.Length);
  1391. Buffer.BlockCopy(cardBytes, 0, bytes, versionInfoBytes.Length, cardBytes.Length);
  1392. response.Content = bytes.ToList();
  1393. }
  1394. }
  1395. }
  1396. else
  1397. {
  1398. var bytes = new byte[160];
  1399. int segmentsToBeSkipped = request.SegmentOffset > 10 ? request.SegmentOffset / 10 : 0;
  1400. logger.Info($"Newly-deleted blacklist, Segment Offset: {request.SegmentOffset.ToString("X")}, " +
  1401. $"Cards to be skipped: {segmentsToBeSkipped * 16}");
  1402. if (VersionedListedCard.CardCount <= 16)
  1403. {
  1404. var cardBytes = StringToByteArray(VersionedListedCard.AllCards);
  1405. Buffer.BlockCopy(cardBytes, 0, bytes, 0, cardBytes.Length);
  1406. response.Content = bytes.ToList();
  1407. }
  1408. else
  1409. {
  1410. var currentCards = VersionedListedCard.ListedCards.Skip(segmentsToBeSkipped * 16).Take(16);
  1411. StringBuilder sb = new StringBuilder();
  1412. foreach (var card in currentCards)
  1413. {
  1414. sb.Append(card.CardNo.PadLeft(20, '0'));
  1415. logger.Info(card.CardNo);
  1416. }
  1417. var cardBytes = StringToByteArray(sb.ToString());
  1418. Buffer.BlockCopy(cardBytes, 0, bytes, 0, cardBytes.Length);
  1419. response.Content = bytes.ToList();
  1420. }
  1421. }
  1422. }
  1423. }
  1424. //White list, 白名单数据
  1425. else if (request.DataContentType == DataContentType.Whitelist)
  1426. {
  1427. logger.Info($"Terminal downloads whitelist, Source: {request.SourceAddress}");
  1428. response.DataContentType = DataContentType.Whitelist;
  1429. response.SegmentOffset = request.SegmentOffset;
  1430. response.SegmentCount = request.SegmentCount;
  1431. var cards = _dbManager.GetWhitelistedCards(_cardAppType);
  1432. var versions = _dbManager.GetDataVersions();
  1433. if (versions.Count > 0 && cards.Count > 0)
  1434. {
  1435. logger.Info($"Whitelist, card count: {cards.Count}");
  1436. var versionInfo = versions.First(v => v.VersionType == VersionType.WhitelistVersion);
  1437. ListedCardRecord record = null;
  1438. if (versionInfo != null)
  1439. {
  1440. logger.Info($"Whitelist, version: {versionInfo.VersionNo}");
  1441. record = new ListedCardRecord();
  1442. record.Version = versionInfo.VersionNo;
  1443. record.ValidStartDate = versionInfo.Effectivetime.ToString("yyyyMMdd");
  1444. record.ExpiryDate = versionInfo.ExpiredTime.ToString("yyyyMMdd");
  1445. record.Region = Convert.ToString(ushort.MaxValue);
  1446. record.CardCount = cards.Count;
  1447. }
  1448. if (VersionedListedCard == null)
  1449. {
  1450. logger.Info("Formatting card list (whitelist)");
  1451. VersionedListedCard = new VersionedListedCard(versionInfo.VersionNo, versionInfo.Effectivetime.ToString("yyyyMMdd"),
  1452. versionInfo.ExpiredTime.ToString("yyyyMMdd"), cards.Count, cards.ToList());
  1453. }
  1454. if (request.SegmentOffset == 0)
  1455. {
  1456. logger.Info("Segment offset is: 0");
  1457. if (request.SegmentCount == 1)
  1458. {
  1459. logger.Info("Segment count is: 1");
  1460. response.Content = parser.SerializeInnerElement(record).ToList();
  1461. }
  1462. else
  1463. {
  1464. logger.Info($"Segment count is: {request.SegmentCount}");
  1465. var versionInfoBytes = parser.SerializeInnerElement(record);
  1466. var cardBytes = StringToByteArray(VersionedListedCard.AllCards);
  1467. if (versionInfoBytes.Length + cardBytes.Length >= 160)
  1468. {
  1469. logger.Info($"Version Info serialized: {ByteArrayToString(versionInfoBytes)}, Length: {versionInfoBytes.Length}, ");
  1470. logger.Info($"More than 160 bytes, Cards bytes Length: {cardBytes.Length}");
  1471. var bytes = new byte[160];
  1472. try
  1473. {
  1474. Buffer.BlockCopy(versionInfoBytes, 0, bytes, 0, versionInfoBytes.Length);
  1475. Buffer.BlockCopy(cardBytes, 0, bytes, versionInfoBytes.Length, 160 - versionInfoBytes.Length);
  1476. }
  1477. catch (Exception ex)
  1478. {
  1479. logger.Info($"{ex.ToString()}");
  1480. }
  1481. response.Content = bytes.ToList();
  1482. }
  1483. else
  1484. {
  1485. logger.Info("Less than 160 bytes");
  1486. var bytes = new byte[160];
  1487. Buffer.BlockCopy(versionInfoBytes, 0, bytes, 0, versionInfoBytes.Length);
  1488. Buffer.BlockCopy(cardBytes, 0, bytes, versionInfoBytes.Length - 1, cardBytes.Length);
  1489. response.Content = bytes.ToList();
  1490. }
  1491. }
  1492. }
  1493. else
  1494. {
  1495. logger.Info($"Segment offset is: {request.SegmentOffset}");
  1496. var bytes = new byte[160];
  1497. int segmentsToBeSkipped = request.SegmentOffset > 10 ? request.SegmentOffset / 10 : 0;
  1498. logger.Info($"Whitelist, Segment Offset: {request.SegmentOffset.ToString("X")}, " +
  1499. $"Cards to be skipped: {segmentsToBeSkipped * 16}");
  1500. if (VersionedListedCard.CardCount <= 16)
  1501. {
  1502. var currentCards = VersionedListedCard.ListedCards.Skip((request.SegmentOffset - 1) * 15).Take(15);
  1503. StringBuilder sb = new StringBuilder();
  1504. foreach (var card in currentCards)
  1505. {
  1506. sb.Append(card.CardNo.PadLeft(20, '0'));
  1507. logger.Info(card.CardNo);
  1508. }
  1509. var cardBytes = StringToByteArray(sb.ToString());
  1510. Buffer.BlockCopy(cardBytes, 0, bytes, 0, cardBytes.Length);
  1511. response.Content = bytes.ToList();
  1512. }
  1513. else
  1514. {
  1515. var currentCards = VersionedListedCard.ListedCards.Skip(segmentsToBeSkipped * 16).Take(16);
  1516. StringBuilder sb = new StringBuilder();
  1517. foreach (var card in currentCards)
  1518. {
  1519. sb.Append(card.CardNo.PadLeft(20, '0'));
  1520. logger.Info(card.CardNo);
  1521. }
  1522. var cardBytes = StringToByteArray(sb.ToString());
  1523. Buffer.BlockCopy(cardBytes, 0, bytes, 0, cardBytes.Length);
  1524. response.Content = bytes.ToList();
  1525. //var cardBytes = StringToByteArray(VersionedListedCard.AllCards);
  1526. //Buffer.BlockCopy(cardBytes, 0, bytes, 0, cardBytes.Length);
  1527. //response.Content = bytes.ToList();
  1528. }
  1529. }
  1530. }
  1531. else if (cards.Count == 0)
  1532. {
  1533. logger.Info("No cards for whitelist");
  1534. var versionInfo = versions.First(v => v.VersionType == VersionType.WhitelistVersion);
  1535. ListedCardRecord record = null;
  1536. if (versionInfo != null)
  1537. {
  1538. record = new ListedCardRecord();
  1539. record.Version = versionInfo.VersionNo;
  1540. record.ValidStartDate = versionInfo.Effectivetime.ToString("yyyyMMdd");
  1541. record.ExpiryDate = versionInfo.ExpiredTime.ToString("yyyyMMdd");
  1542. record.Region = Convert.ToString(ushort.MaxValue);
  1543. record.CardCount = 0;
  1544. }
  1545. if (VersionedListedCard == null)
  1546. {
  1547. logger.Info("Formatting card list (whitelist)");
  1548. VersionedListedCard = new VersionedListedCard(versionInfo.VersionNo, versionInfo.Effectivetime.ToString("yyyyMMdd"),
  1549. versionInfo.ExpiredTime.ToString("yyyyMMdd"), 0, null);
  1550. }
  1551. response.Content = parser.SerializeInnerElement(record).ToList();
  1552. }
  1553. }
  1554. //Base blacklist, 基础黑名单
  1555. else if (request.DataContentType == DataContentType.BaseBlacklist)
  1556. {
  1557. logger.Info($"Terminal downloads base blacklist, Source: {request.SourceAddress}");
  1558. response.DataContentType = DataContentType.BaseBlacklist;
  1559. response.SegmentOffset = request.SegmentOffset;
  1560. response.SegmentCount = request.SegmentCount;
  1561. var cards = _dbManager.GetBaseBlacklistedCards(_cardAppType);
  1562. var versions = _dbManager.GetDataVersions();
  1563. if (versions.Count > 0 && cards.Count > 0)
  1564. {
  1565. var versionInfo = versions.First(v => v.VersionType == VersionType.BaseBlacklistVersion);
  1566. ListedCardRecord record = null;
  1567. if (versionInfo != null)
  1568. {
  1569. record = new ListedCardRecord();
  1570. record.Version = versionInfo.VersionNo;
  1571. record.ValidStartDate = versionInfo.Effectivetime.ToString("yyyyMMdd");
  1572. record.ExpiryDate = versionInfo.ExpiredTime.ToString("yyyyMMdd");
  1573. record.Region = Convert.ToString(ushort.MaxValue);
  1574. record.CardCount = cards.Count;
  1575. }
  1576. if (VersionedListedCard == null)
  1577. {
  1578. logger.Info("Formatting card list (base blacklist)");
  1579. VersionedListedCard = new VersionedListedCard(versionInfo.VersionNo, versionInfo.Effectivetime.ToString("yyyyMMdd"),
  1580. versionInfo.ExpiredTime.ToString("yyyyMMdd"), cards.Count, cards.ToList());
  1581. }
  1582. if (request.SegmentOffset == 0)
  1583. {
  1584. if (request.SegmentCount == 1)
  1585. {
  1586. response.Content = parser.SerializeInnerElement(record).ToList();
  1587. }
  1588. else
  1589. {
  1590. var versionInfoBytes = parser.SerializeInnerElement(record);
  1591. var cardBytes = StringToByteArray(VersionedListedCard.AllCards);
  1592. if (versionInfoBytes.Length + cardBytes.Length >= 160)
  1593. {
  1594. var bytes = new byte[160];
  1595. Buffer.BlockCopy(versionInfoBytes, 0, bytes, 0, versionInfoBytes.Length);
  1596. Buffer.BlockCopy(cardBytes, 0, bytes, versionInfoBytes.Length, 160 - versionInfoBytes.Length);
  1597. response.Content = bytes.ToList();
  1598. }
  1599. else
  1600. {
  1601. var bytes = new byte[160];
  1602. Buffer.BlockCopy(versionInfoBytes, 0, bytes, 0, versionInfoBytes.Length);
  1603. Buffer.BlockCopy(cardBytes, 0, bytes, versionInfoBytes.Length - 1, cardBytes.Length);
  1604. response.Content = bytes.ToList();
  1605. }
  1606. }
  1607. }
  1608. else
  1609. {
  1610. var bytes = new byte[160];
  1611. int cardsToBeSkipped = request.SegmentOffset > 10 ? request.SegmentOffset / 10 : 0;
  1612. logger.Info($"Base blacklist, Segment Offset: {request.SegmentOffset}, Cards to be skipped: {cardsToBeSkipped * 16}");
  1613. if (VersionedListedCard.CardCount <= 16)
  1614. {
  1615. var cardBytes = StringToByteArray(VersionedListedCard.AllCards);
  1616. Buffer.BlockCopy(cardBytes, 0, bytes, 0, cardBytes.Length);
  1617. response.Content = bytes.ToList();
  1618. }
  1619. else
  1620. {
  1621. IEnumerable<ListedCard> currentCards = VersionedListedCard.ListedCards.Skip(cardsToBeSkipped * 16).Take(16);
  1622. StringBuilder sb = new StringBuilder();
  1623. foreach (var card in currentCards)
  1624. {
  1625. sb.Append(card.CardNo.PadLeft(20, '0'));
  1626. logger.Info(card.CardNo);
  1627. }
  1628. var cardBytes = StringToByteArray(sb.ToString());
  1629. Buffer.BlockCopy(cardBytes, 0, bytes, 0, cardBytes.Length);
  1630. response.Content = bytes.ToList();
  1631. }
  1632. }
  1633. }
  1634. }
  1635. //Station general info, 油站通用信息
  1636. else if (request.DataContentType == DataContentType.StationGeneralInfo)
  1637. {
  1638. logger.Info($"Terminal downloads Station general info (油站通用信息内容), Source: {request.SourceAddress}");
  1639. response.DataContentType = DataContentType.StationGeneralInfo;
  1640. response.SegmentOffset = request.SegmentOffset;
  1641. var result = _dbManager.GetPumpInfo(Convert.ToByte(PumpId));
  1642. List<byte> data = new List<byte>();
  1643. var pumpInfo = result.First();
  1644. data.Add(Convert.ToByte(pumpInfo.VersionId));
  1645. data.Add(Convert.ToByte(pumpInfo.PosId));
  1646. data.Add(Convert.ToByte(PumpId));
  1647. if (result != null && result.Count > 0)
  1648. {
  1649. data.Add(Convert.ToByte(result.Count()));
  1650. foreach (var item in result)
  1651. data.Add(Convert.ToByte(item.LogicId));
  1652. data.AddRange(new byte[10]);
  1653. }
  1654. response.SegmentCount = 1;
  1655. response.Content = data;
  1656. }
  1657. if (response.Content != null && response.Content.Count > 0)
  1658. logger.Info($"ContentType: {response.DataContentType}, Content: {ByteArrayToString(response.Content.ToArray())}");
  1659. else
  1660. logger.Info($"ContentType: {response.DataContentType}, Content emtpy");
  1661. _terminal.Write(response);
  1662. }
  1663. #endregion
  1664. #region Change Auth Mode 修改授权模式
  1665. private void ChangeAuthMode(CheckCmdRequest checkCmd, WorkMode workMode)
  1666. {
  1667. var request = new ChangeAuthMode
  1668. {
  1669. Prefix = STX,
  1670. SourceAddress = checkCmd.SourceAddress,
  1671. DestinationAddress = checkCmd.DestinationAddress,
  1672. FrameSqNoByte = checkCmd.FrameSqNoByte,
  1673. Handle = Convert.ToByte(Command.ChangeAuthMode),
  1674. FPCode = EncodeFPCodeString(PumpId, PumpId),
  1675. WorkMode = workMode
  1676. };
  1677. PrepareSendMessage(request);
  1678. }
  1679. public void HandleModeChangeResult(ChangeAuthModeAck response)
  1680. {
  1681. logger.Info($"Mode change result: {response.ModeChangeResult}, current mode: {response.WorkMode}");
  1682. }
  1683. #endregion
  1684. #region Cancel Auth request 撤销授权
  1685. public void HandleCancelAuth(CancelAuthRequest cancelAuthRequest)
  1686. {
  1687. logger.Info("Handling CancelAuth (0x19)");
  1688. //There is no special handling for cancel auth.
  1689. SendCancelAuthResult(cancelAuthRequest);
  1690. }
  1691. private void SendCancelAuthResult(CancelAuthRequest request)
  1692. {
  1693. var result = new CancelAuthResult();
  1694. result.Prefix = STX;
  1695. result.DestinationAddress = request.DestinationAddress;
  1696. result.SourceAddress = request.SourceAddress;
  1697. result.FrameSqNoByte = request.FrameSqNoByte;
  1698. result.Handle = Convert.ToByte(Command.CancelAuthResult);
  1699. result.Asn = request.Asn;
  1700. result.PosTtc = request.PosTtc;
  1701. result.SeqNo = request.SeqNo;
  1702. result.FPCode = request.FPCode.ToUInt16();
  1703. result.Result = 0;
  1704. result.AdditionalInfoLength = 0;
  1705. _terminal.Write(result);
  1706. }
  1707. #endregion
  1708. #region Query gray record 查询灰记录
  1709. public void HandleQueryGrayRecord(QueryGrayRecordRequest request)
  1710. {
  1711. logger.Info($"Query gray record, info, ASN: {request.Asn}, CTC: {request.Ctc}, Time: {request.Time}");
  1712. SendGrayRecord(request);
  1713. }
  1714. private void SendGrayRecord(QueryGrayRecordRequest request)
  1715. {
  1716. var record = _dbManager.SelectGrayInfo(request.Asn, request.Ctc, request.Time);
  1717. QueryGrayRecordResult result = new QueryGrayRecordResult();
  1718. result.Prefix = STX;
  1719. result.SourceAddress = request.SourceAddress;
  1720. result.DestinationAddress = request.DestinationAddress;
  1721. result.FrameSqNoByte = request.FrameSqNoByte;
  1722. result.Handle = Convert.ToByte(Command.GrayRecord);
  1723. if (record != null)
  1724. {
  1725. result.Match = 0;
  1726. result.Asn = request.Asn;
  1727. result.Balance = Convert.ToInt32(record.CardBal);
  1728. //result.Amount = Convert.ToInt32(record.Mon);
  1729. result.Amount1 = Convert.ToInt32(record.RealMon);
  1730. result.Ctc = request.Ctc;
  1731. result.Time = record.Ttctime;
  1732. result.Gmac = record.Gmac.ToString("X");
  1733. result.PsamTid = record.Psamtid;
  1734. result.PsamTtc = Convert.ToInt32(record.Psamttc);
  1735. result.Volume = Convert.ToInt32(record.Vol);
  1736. }
  1737. else
  1738. {
  1739. result.Match = 1;
  1740. }
  1741. _terminal.Write(result);
  1742. }
  1743. #endregion
  1744. public void HandleFakeNullMessage()
  1745. {
  1746. logger.Info($"Terminal {PumpId} Handling Fake null message");
  1747. ResetTimer();
  1748. }
  1749. #region Message sent to terminal from System
  1750. private bool IsMessagePendingForSend()
  1751. {
  1752. lock (sendQueueSyncObj)
  1753. {
  1754. logger.Debug($"Current send queue count: {activeSendQueue.Count}");
  1755. return activeSendQueue.Count > 0;
  1756. }
  1757. }
  1758. private void PrepareSendMessage(CardMessageBase cardMessage)
  1759. {
  1760. lock (sendQueueSyncObj)
  1761. {
  1762. activeSendQueue.Enqueue(cardMessage);
  1763. }
  1764. }
  1765. private CardMessageBase PickAndSendCardMessage()
  1766. {
  1767. lock (sendQueueSyncObj)
  1768. {
  1769. if (activeSendQueue.Count > 0)
  1770. {
  1771. logger.Info("Dequeued message from SendQueue");
  1772. return activeSendQueue.Dequeue();
  1773. }
  1774. }
  1775. return null;
  1776. }
  1777. #endregion
  1778. #region Check terminal id
  1779. private void CheckTerminalId(string terminalId)
  1780. {
  1781. int tid;
  1782. if (int.TryParse(terminalId, out tid))
  1783. {
  1784. if (tid <= 0 || tid >= 99)
  1785. {
  1786. logger.Error($"Invalid terminal id: {tid}");
  1787. }
  1788. }
  1789. else
  1790. {
  1791. logger.Error("Invalid terminal id, unrecognized");
  1792. }
  1793. }
  1794. #endregion
  1795. #endregion
  1796. #region Helper methods
  1797. public byte[] StringToByteArray(string hex)
  1798. {
  1799. int numberChars = hex.Length;
  1800. byte[] bytes = new byte[numberChars / 2];
  1801. for (int i = 0; i < numberChars; i += 2)
  1802. bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
  1803. return bytes;
  1804. }
  1805. public static string ByteArrayToString(byte[] ba)
  1806. {
  1807. return BitConverter.ToString(ba).Replace("-", "");
  1808. }
  1809. private DateTime GetDateTime(string time)
  1810. {
  1811. if (time == "00000000000000")
  1812. return new DateTime(0001, 01, 01, 00, 00, 00);
  1813. var timeFormat = "yyyyMMddHHmmss";
  1814. return DateTime.ParseExact(time, timeFormat, null);
  1815. }
  1816. private string EncodeFPCodeString(int nozzleId, int pumpId)
  1817. {
  1818. return nozzleId.ToString("X").PadLeft(2, '0') + pumpId.ToString("X").PadLeft(2, '0');
  1819. }
  1820. #endregion
  1821. #region Save non card trade
  1822. private long AddTradeFromFuelingData(FuelingDataRequest request)
  1823. {
  1824. int payModeId = 102;
  1825. string payModeNo = string.Empty;
  1826. byte trxType = 6;
  1827. string asn = string.Empty;
  1828. if (request.Asn.Any(c => c > 48))
  1829. asn = request.Asn;
  1830. int stationNo = 1;
  1831. if (_posApp.StationInfo != null)
  1832. stationNo = _posApp.StationInfo.StationNo;
  1833. var newGid = _dbManager.AddTrade(
  1834. stationNo,
  1835. asn,
  1836. asn,
  1837. payModeId,
  1838. payModeNo,
  1839. trxType,
  1840. Convert.ToString(request.ProductCode),
  1841. request.Price,
  1842. request.Volume,
  1843. request.Amount,
  1844. request.Amount,
  1845. 0, //Card Balance,
  1846. 0, //card type, use 0 for non-card transaction
  1847. 0, //card ctc use 0 for non-card transaction
  1848. GetDateTime(request.DispenserTime),
  1849. GetDateTime(request.TransactionEndTime),
  1850. request.PosTtc,
  1851. request.SeqNo,
  1852. _posApp.GetNextBillNo(), //billNo
  1853. request.FuelingPoint.NozzleNo,
  1854. request.FuelingPoint.PumpNo,
  1855. Convert.ToInt32(request.TerminalId.TrimStart('0')),
  1856. request.VolumeTotal,
  1857. 0, //discountNo
  1858. 1001,
  1859. 1,
  1860. string.Empty,//request.PsamAsn, //PsamAsn
  1861. 0,//uint.Parse(request.PsamTac, System.Globalization.NumberStyles.HexNumber),//PsamTac
  1862. string.Empty,//request.PsamTid,//psam tid
  1863. 0,//uint.Parse(request.PsamTtc, System.Globalization.NumberStyles.HexNumber),//psam ttc
  1864. 0,//uint.Parse(request.Tac, System.Globalization.NumberStyles.HexNumber),//tac
  1865. 0,//uint.Parse(request.Gmac, System.Globalization.NumberStyles.HexNumber),//gmac
  1866. 0,//uint.Parse(request.Tmac, System.Globalization.NumberStyles.HexNumber),//tmac
  1867. 0, //Integral
  1868. Convert.ToInt32(_posApp.CurrentShiftNo),
  1869. string.Empty,
  1870. string.Empty,
  1871. string.Empty,
  1872. 0);
  1873. return newGid;
  1874. }
  1875. #endregion
  1876. private string GetCardNo(string asn)
  1877. {
  1878. if (_cardAppType == CardAppType.CpuCard)
  1879. {
  1880. string leftOver = asn.TrimStart('0');
  1881. return leftOver.PadLeft(20, '0');
  1882. }
  1883. else if (_cardAppType == CardAppType.RfCard)
  1884. {
  1885. string leftOver = asn.TrimStart('0');
  1886. return leftOver.PadLeft(8, '0');
  1887. }
  1888. return asn;
  1889. }
  1890. }
  1891. }