FuelingPoint.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;
  2. using Edge.Core.Parser.BinaryParser.Util;
  3. using SinochemCarplateService.Models;
  4. using SinochemCloudClient.Models;
  5. using SinochemPosClient.Models;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Configuration;
  9. using System.Linq;
  10. using System.Text;
  11. using System.Threading;
  12. using System.Threading.Tasks;
  13. using Wayne.Lib.StateEngine;
  14. using WayneChina_IcCardReader_SinoChem.MessageEntity;
  15. using WayneChina_IcCardReader_SinoChem.MessageEntity.Incoming;
  16. using WayneChina_IcCardReader_SinoChem.MessageEntity.Outgoing;
  17. using Edge.Core.IndustryStandardInterface.Pump;
  18. namespace SinochemInternetPlusApp
  19. {
  20. public class ReceiptLine
  21. {
  22. public ReceiptLine(byte[] bytes, int lineWidth)
  23. {
  24. Line = new byte[lineWidth];
  25. bytes.CopyTo(Line, 0);
  26. }
  27. public byte[] Line { get; set; }
  28. }
  29. public class FuelingPoint : StateManager
  30. {
  31. #region Fields
  32. //Immutable after construction
  33. public int FuelingPointId { get; }
  34. private byte initialSqNo = 0;
  35. private byte sqNo;
  36. private object syncObj = new object();
  37. private Queue<IcCardReaderMessageBase> outboundMsgQueue = new Queue<IcCardReaderMessageBase>();
  38. private object queueSyncObj = new object();
  39. private object evSyncObj = new object();
  40. private List<int> requestIdList = new List<int>();
  41. private object reqIdSyncObj = new object();
  42. private bool printerIdle = true;
  43. private object printerSyncObj = new object();
  44. private byte currentSqNo { get; set; }
  45. private object sqNoSyncObj = new object();
  46. private IFdcPumpController callingPump;
  47. #endregion
  48. #region Timeout values
  49. //in seconds
  50. public int CardReaderDisplayTimeout { get { return 10; } }
  51. //in milliseconds
  52. public int CardReaderBackToIdleTimeout { get { return 2000; } }
  53. #endregion
  54. #region Properties
  55. /// <summary>
  56. /// Current physical nozzle id, reflecting the running nozzle of this FP.
  57. /// </summary>
  58. public int CurrentNozzleId { get; set; }
  59. /// <summary>
  60. /// The physical nozzles associated with this fueling point
  61. /// </summary>
  62. public IEnumerable<int> AssociatedNozzles { get; set; }
  63. public Eps Eps { get; set; }
  64. public EpsTransaction CurrentEpsTrx { get; set; }
  65. public TransactionMode CurrentTrxMode { get; set; }
  66. public long? AuthorizationId { get; set; }
  67. public byte ResetSqNo;
  68. public override string EntityType => "FuelingPoint";
  69. public override string EntitySubType => "";
  70. public IEnumerable<EpsTransactionModel> ActiveTrx { get; set; }
  71. public CardOnlineVerificationRequest OnlineVerificationRequest { get; set; }
  72. public WayneChina_IcCardReader_SinoChem.Handler CurrentCardReader => Eps.GetHandler(FuelingPointId);
  73. public SignDataResponse SignedData { get; internal set; }
  74. public List<ReceiptLine> CurrentReceipt { get; set; }
  75. public bool CardReaderDisabled { get; set; } = false;
  76. public byte IdleStateCardReaderSqNo { get; set; }
  77. public int GetPrinterCount { get; set; }
  78. /// <summary>
  79. /// Site id aka Station number
  80. /// </summary>
  81. public string StationNo => App.AppSettings["SinochemSiteId"];
  82. /// <summary>
  83. /// Site name aka Station name
  84. /// </summary>
  85. public string StationName => App.AppSettings["SinochemSiteName"];
  86. private bool printReceiptEnabled => Convert.ToBoolean(App.AppSettings["PrintReceiptEnabled"]);
  87. private bool printQRCodeEnabled => Convert.ToBoolean(App.AppSettings["PrintQrCodeOnReceiptEnabled"]);
  88. #endregion
  89. #region Request Id operations
  90. public void AddRequestId(int reqIid)
  91. {
  92. lock (reqIdSyncObj)
  93. {
  94. requestIdList.Add(reqIid);
  95. }
  96. }
  97. public void RemoveRequestId(int reqIid)
  98. {
  99. lock (reqIdSyncObj)
  100. {
  101. requestIdList.Remove(reqIid);
  102. }
  103. }
  104. public bool ContainsRequestId(int reqIid)
  105. {
  106. lock (reqIdSyncObj)
  107. {
  108. return requestIdList.Contains(reqIid);
  109. }
  110. }
  111. public void ClearAllRequestIds()
  112. {
  113. lock (reqIdSyncObj)
  114. {
  115. foreach (var id in requestIdList)
  116. {
  117. debugLogger.Add($"Clearing RequestId: {id}");
  118. }
  119. requestIdList.Clear();
  120. }
  121. }
  122. #endregion
  123. #region Constructor
  124. public FuelingPoint(int nozzleId, IEnumerable<int> associatedNozzles, Eps eps) : base(nozzleId, "FuelingPoint")
  125. {
  126. FuelingPointId = nozzleId;
  127. AssociatedNozzles = associatedNozzles;
  128. Eps = eps;
  129. }
  130. #endregion
  131. #region DB & Trx handling
  132. /// <summary>
  133. /// Create an eps trx given the mop type.
  134. /// </summary>
  135. /// <param name="mop"></param>
  136. internal void CreateEpsTransaction(TransactionMode mop)
  137. {
  138. CurrentEpsTrx = EpsTransaction.CreateEpsTrx(CurrentNozzleId, mop, debugLogger);
  139. }
  140. internal void LockTrxInXiaofei2AsEpsTrx()
  141. {
  142. if (CurrentEpsTrx.MoveFromXiaofei2())
  143. {
  144. }
  145. else
  146. {
  147. debugLogger.Add("trx in xiaofei2 not found!!!!");
  148. }
  149. }
  150. #endregion
  151. #region Cloud & POS interactions
  152. internal void NotifyPosAync()
  153. {
  154. ThreadPool.QueueUserWorkItem(_ =>
  155. {
  156. TrxNotificationResponse response = Eps.NotifySuccessfulTrxToPos(CurrentEpsTrx.Model, debugLogger);
  157. if (response != null && response.IsSuccessful())
  158. {
  159. stateMachine.IncomingEvent(new StateEngineEvent(EventType.PosNotifyOk));
  160. }
  161. else
  162. {
  163. stateMachine.IncomingEvent(new StateEngineEvent(EventType.PosNotifyFailed));
  164. }
  165. });
  166. }
  167. internal void SendPaymentToCloudAsync()
  168. {
  169. ThreadPool.QueueUserWorkItem(_ =>
  170. {
  171. PaymentResponse response = Eps.SendPaymentToCloud(CurrentEpsTrx.Model, debugLogger);
  172. if (response != null && response.IsSuccessful())
  173. {
  174. stateMachine.IncomingEvent(GenericEvent.Create(EventType.CloudPaymentOk, this, response));
  175. }
  176. else
  177. {
  178. stateMachine.IncomingEvent(new StateEngineEvent(EventType.CloudPaymentFailed));
  179. }
  180. });
  181. }
  182. internal void SendBalanceQueryToCloudAsync(string cardNo, string encryptedPin, string tid, int nozzleId)
  183. {
  184. ThreadPool.QueueUserWorkItem(_ =>
  185. {
  186. BalanceInquiryResponse response = Eps.SendBalanceInquiryToCloud(cardNo, encryptedPin, tid, nozzleId, debugLogger);
  187. if (response != null && response.IsSuccessful())
  188. {
  189. stateMachine.IncomingEvent(GenericEvent.Create(EventType.CloudBalanceOk, this, response));
  190. }
  191. else
  192. {
  193. stateMachine.IncomingEvent(new StateEngineEvent(EventType.CloudBalanceFailed));
  194. }
  195. });
  196. }
  197. #endregion
  198. #region Pump interactions
  199. internal void AuthorizePumpAsync(decimal authAmount)
  200. {
  201. Eps.AuthorizePumpAsync(callingPump, CurrentNozzleId, authAmount);
  202. }
  203. internal List<int> GetAllNozzlesOnThisSide()
  204. {
  205. List<int> nozzles = new List<int>();
  206. foreach (var fp in CurrentCardReader.SupportedNozzles)
  207. {
  208. var fuelingPoint = this.Eps.GetFuelingPoint(fp);
  209. if (fuelingPoint != null)
  210. nozzles.AddRange(fuelingPoint.AssociatedNozzles);
  211. }
  212. return nozzles;
  213. }
  214. #endregion
  215. #region Eps state config and signal
  216. protected override void ConfigStates()
  217. {
  218. States.CONFIGURATION.Config(stateMachine.StateTransitionLookup);
  219. }
  220. public void SignalShutdown()
  221. {
  222. Console.Out.WriteLine("SignalShutdown");
  223. stateMachine.IncomingEvent(new StateEngineEvent(EventType.ShutdownRequest));
  224. }
  225. public void FinalStateReached(string name)
  226. {
  227. Console.Out.WriteLine(name + " FinalStateReached");
  228. }
  229. #endregion
  230. #region Card Plate
  231. public void SignalNewCarplate(CarPlateTrxRequest request)
  232. {
  233. lock (evSyncObj)
  234. {
  235. if (request != null && AssociatedNozzles.Contains(Convert.ToInt32(request.gun)))
  236. {
  237. debugLogger.AddIfActive
  238. ($"New car plate info arrived: Gun: {request.gun}, License plate No: {request.car_Number}, Balance: {request.amount}");
  239. stateMachine.IncomingEvent(GenericEvent.Create(EventType.CarPlateScanned, this, request));
  240. }
  241. }
  242. }
  243. #endregion
  244. #region Display on dispenser interactions
  245. public void SignalDisplayResponseReceived(DisplayResponse response)
  246. {
  247. debugLogger.AddIfActive("DisplayResponse from big screen");
  248. stateMachine.IncomingEvent(new GenericEvent<DisplayResponseEventArgs>(
  249. EventType.DisplayResponseReceived, this, new DisplayResponseEventArgs(response)));
  250. }
  251. public void SignalTrxOpRequest(TransactionOperation request)
  252. {
  253. debugLogger.AddIfActive($"Transaction operation request from big screen, Trx id: {request.TrxId}");
  254. stateMachine.IncomingEvent(new GenericEvent<TransactionOperationEventArgs>(EventType.TrxOpRequest, this, new TransactionOperationEventArgs(request)));
  255. }
  256. #endregion
  257. #region Card reader signals
  258. public void SingalAckReceived(ACK cardReaderMessage)
  259. {
  260. lock (sqNoSyncObj)
  261. {
  262. if (cardReaderMessage.MessageSeqNumber == currentSqNo)
  263. {
  264. debugLogger.AddIfActive($"Received card reader message with MsgSqNo = {cardReaderMessage.MessageSeqNumber}");
  265. outboundMsgQueue.Dequeue();
  266. debugLogger.AddIfActive($"Removed the message from the queue");
  267. }
  268. }
  269. stateMachine.IncomingEvent(new GenericEvent<CardReaderAckEventArgs>(EventType.CardReaderAck, this, new CardReaderAckEventArgs(cardReaderMessage)));
  270. }
  271. public void SignalCardReaderDisconnected()
  272. {
  273. stateMachine.IncomingEvent(new StateEngineEvent(EventType.CardReaderDisconnected));
  274. }
  275. public void SignalCardReaderDisabled()
  276. {
  277. stateMachine.IncomingEvent(new StateEngineEvent(EventType.ICCardReaderDisabled));
  278. }
  279. public void SignalCardReaderHeartbeat(HeartBeat heartBeat)
  280. {
  281. SendNextCardReaderMessage();
  282. }
  283. public void SignalSignedDataArrived(SignDataResponse cardReaderMessage)
  284. {
  285. debugLogger.AddIfActive("sending ack for SignDataResponse");
  286. SendAckToCardReader(cardReaderMessage.SourceAddress, cardReaderMessage.MessageSeqNumber);
  287. stateMachine.IncomingEvent(new GenericEvent<SignedDataEventArgs>(EventType.DataSigned, this, new SignedDataEventArgs(cardReaderMessage)));
  288. }
  289. public void SingalCardReaderStateEvent(CardReaderStateEvent cardReaderStateEvent)
  290. {
  291. debugLogger.AddIfActive($"CardReaderStateEvent \n source address: {cardReaderStateEvent.SourceAddress} \n SqNo: {cardReaderStateEvent.MessageSeqNumber} \n Reader state: {cardReaderStateEvent.State.ToString()}");
  292. SendAckToCardReader(cardReaderStateEvent.SourceAddress, cardReaderStateEvent.MessageSeqNumber);
  293. stateMachine.IncomingEvent(new GenericEvent<CardReaderStateEventArgs>(EventType.ReaderStateChanged, this, new CardReaderStateEventArgs(cardReaderStateEvent)));
  294. }
  295. private void SendAckToCardReader(byte srcAddress, byte sqNo)
  296. {
  297. ACK ack = new ACK { SourceAddress = srcAddress, MessageSeqNumber = sqNo };
  298. if (CurrentCardReader != null)
  299. CurrentCardReader.Write(ack);
  300. }
  301. public void SignalCardExternalCheckFailure(CardExternalCheckErrorRequest cardReaderMessage)
  302. {
  303. debugLogger.AddIfActive($"IC Card check failure, Error reason: {cardReaderMessage.ErrorType.ToString()}");
  304. SendAckToCardReader(cardReaderMessage.SourceAddress, cardReaderMessage.MessageSeqNumber);
  305. stateMachine.IncomingEvent(new GenericEvent<ExternalCheckFailedEventArgs>(EventType.ExternalCheckFailure, this, new ExternalCheckFailedEventArgs(cardReaderMessage)));
  306. }
  307. public void SignalCardOnlineVerification(CardOnlineVerificationRequest cardReaderMessage)
  308. {
  309. debugLogger.AddIfActive($"IC Card online verification request, Card No: {cardReaderMessage.CardNo}, Encrypted PIN: {cardReaderMessage.EncryptedPIN}, TID: {cardReaderMessage.TID}");
  310. SendAckToCardReader(cardReaderMessage.SourceAddress, cardReaderMessage.MessageSeqNumber);
  311. stateMachine.IncomingEvent(new GenericEvent<CardOnlineVerificationEventArgs>(EventType.OnlineVerification, this, new CardOnlineVerificationEventArgs(cardReaderMessage)));
  312. }
  313. #endregion
  314. #region Pump signals
  315. internal void SignalNozzleLifted(IFdcPumpController callingPump, int sitewiseNozzleId)
  316. {
  317. debugLogger.Add("SignalNozzleLifted, nozzleid -- " + sitewiseNozzleId);
  318. this.callingPump = callingPump;
  319. stateMachine.IncomingEvent(
  320. new GenericEvent<NozzleLiftedEventArgs>(
  321. EventType.NozzleLifted,
  322. this,
  323. new NozzleLiftedEventArgs() { NozzleId = sitewiseNozzleId }));
  324. }
  325. internal void SignalNozzleReplaced(int sitewiseNozzleId)
  326. {
  327. debugLogger.Add("SignalNozzleReplaced, nozzleid -- " + sitewiseNozzleId);
  328. stateMachine.IncomingEvent(
  329. new GenericEvent<NozzleReplacedEventArgs>(
  330. EventType.NozzleReplaced,
  331. this,
  332. new NozzleReplacedEventArgs() { NozzleId = sitewiseNozzleId }));
  333. }
  334. internal void SignalAuthOk(long? authId)
  335. {
  336. debugLogger.Add("SignalAuthOk -- " + authId);
  337. AuthorizationId = authId;
  338. var arg = new GenericEventArg<string>(DateTime.Now.ToString("HH:mm:ss"));
  339. stateMachine.IncomingEvent(GenericEvent.Create(EventType.PumpAuthOk, this, arg));
  340. }
  341. internal void SignalAuthFailed()
  342. {
  343. debugLogger.Add("SignalAuthFailed");
  344. stateMachine.IncomingEvent(new StateEngineEvent(EventType.PumpAuthFailed));
  345. }
  346. internal void SignalFuelingDone(int nozzleId, int seqNum, decimal fuelAmount, decimal fuelingQty, long authId)
  347. {
  348. debugLogger.Add("SignalFuelingDone -- " + authId);
  349. stateMachine.IncomingEvent(new GenericEvent<FuelingDoneEventArgs>(EventType.FuelingDone, this,
  350. new FuelingDoneEventArgs
  351. {
  352. NozzleId = nozzleId,
  353. FuelingSqNo = seqNum,
  354. Amount = fuelAmount,
  355. Quantity = fuelingQty,
  356. AuthId = authId
  357. }));
  358. }
  359. #endregion
  360. #region Card reader support
  361. /// <summary>
  362. /// Sender side's sequence number, 01H-0FH, when program restarted, use 00H.
  363. /// </summary>
  364. /// <returns>The sequence number byte.</returns>
  365. public byte GetSenderSideSqNo()
  366. {
  367. lock (syncObj)
  368. {
  369. if (initialSqNo == 0)
  370. {
  371. sqNo = initialSqNo;
  372. initialSqNo = 1;
  373. return sqNo;
  374. }
  375. byte nextSqNo = (byte)(sqNo++ % 16);
  376. if (nextSqNo == 0)
  377. {
  378. sqNo++;
  379. return initialSqNo;
  380. }
  381. return nextSqNo;
  382. }
  383. }
  384. public byte CardReaderSrcAddr
  385. {
  386. get
  387. {
  388. return (byte)(CurrentCardReader?.GetAddressForNozzleId(FuelingPointId));
  389. }
  390. }
  391. public void SendCommand(IcCardReaderMessageBase command, out byte sqNo)
  392. {
  393. command.MessageSeqNumber = GetSenderSideSqNo();
  394. command.SourceAddress = CardReaderSrcAddr;
  395. sqNo = command.MessageSeqNumber;
  396. PendMessage(command);
  397. }
  398. public void PendMessage(IcCardReaderMessageBase msg)
  399. {
  400. lock (queueSyncObj)
  401. {
  402. outboundMsgQueue.Enqueue(msg);
  403. }
  404. }
  405. public void SendNextCardReaderMessage()
  406. {
  407. lock (queueSyncObj)
  408. {
  409. if (outboundMsgQueue.Count > 0)
  410. {
  411. var msgToSend = outboundMsgQueue.Peek();
  412. lock (sqNoSyncObj)
  413. {
  414. currentSqNo = msgToSend.MessageSeqNumber;
  415. }
  416. debugLogger.AddIfActive($"Peek and send Card reader message {msgToSend.GetType().ToString()} with MsgSqNo = {msgToSend.MessageSeqNumber}");
  417. CurrentCardReader?.Write(msgToSend);
  418. }
  419. else
  420. {
  421. var eot = new EOT
  422. {
  423. SourceAddress = CardReaderSrcAddr,
  424. MessageSeqNumber = 0
  425. };
  426. CurrentCardReader?.Write(eot);
  427. }
  428. }
  429. }
  430. #endregion
  431. #region Receipt
  432. public void BuildReceipt()
  433. {
  434. if (CurrentReceipt == null)
  435. CurrentReceipt = new List<ReceiptLine>();
  436. else
  437. CurrentReceipt.Clear();
  438. List<string> bodyLines = new List<string>();
  439. List<string> header = new List<string>();
  440. List<string> product = new List<string>();
  441. List<string> payment = new List<string>();
  442. ReceiptModel receiptModel = ReceiptModelBuilder.BuildReceiptModel(CurrentEpsTrx, CurrentTrxMode);
  443. string lineSeparator = "------------------------------";
  444. string emptyLine = " ";
  445. string receiptHeaderReceiptType = string.Format($" {receiptModel.TrxModeName}小票 ");
  446. string stationName = "油站名称:" + StationName;
  447. string trxTimeStamp = "时间:" + Utilities.ConvertDateTimeToReadble(DateTime.Now);
  448. string runningNumberLine = "流水号:" + receiptModel.RunningNumber;
  449. string cardNoLine = "卡号:" + receiptModel.CardNo;
  450. string gradeNameLine = "油品:" + receiptModel.GradeName;
  451. string nozzleIdLine = "枪号:" + receiptModel.NozzleId;
  452. string ppuLine = "单价:" + receiptModel.PPU + "元/升";
  453. string qtyLine = "数量:" + receiptModel.Qty + "升";
  454. string amountLine = "金额:" + receiptModel.Amount + "元";
  455. string dueAmountLine = string.Format($"应付: {receiptModel.DueAmount}元");
  456. string discountLine = string.Format($"优惠: {receiptModel.DiscountAmount}元");
  457. string payAmountLine = string.Format($"实付: {receiptModel.PayAmount}元");
  458. header.Add(receiptHeaderReceiptType);
  459. header.Add(emptyLine);
  460. header.Add(stationName);
  461. header.Add(trxTimeStamp);
  462. header.Add(runningNumberLine);
  463. header.Add(cardNoLine);
  464. header.Add(lineSeparator);
  465. //Add header
  466. bodyLines.AddRange(header);
  467. product.Add(gradeNameLine);
  468. product.Add(nozzleIdLine);
  469. product.Add(ppuLine);
  470. product.Add(qtyLine);
  471. product.Add(amountLine);
  472. product.Add(lineSeparator);
  473. //Add product details
  474. bodyLines.AddRange(product);
  475. payment.Add(dueAmountLine);
  476. payment.Add(discountLine);
  477. payment.Add(payAmountLine);
  478. payment.Add(lineSeparator);
  479. //Add payment details
  480. bodyLines.AddRange(payment);
  481. //QR code
  482. if (printQRCodeEnabled)
  483. {
  484. string invoicePromptLine = string.Format(" 如需发票,请扫描二维码 ");
  485. bodyLines.Add(invoicePromptLine);
  486. bodyLines.Add(emptyLine);
  487. }
  488. //Thanks line
  489. string thanksLine = " 谢谢惠顾,欢迎再次光临 ";
  490. CreateReceiptLine(bodyLines);
  491. if (printQRCodeEnabled)
  492. {
  493. debugLogger.Add("EInvocie URL: " + receiptModel.InvoiceUrl);
  494. CreateQRCode(receiptModel.InvoiceUrl);
  495. //CreateQRCode("http://invoicetest-invoicetest-invoicetest-invoicetest-invoicetest-invoicetestinvoicetest.com/orderno=12345678901234567890123456789012345678901234567890");
  496. }
  497. CreateReceiptEnd(thanksLine);
  498. }
  499. private void CreateReceiptLine(List<string> lines)
  500. {
  501. List<byte> targetBytes = new List<byte>();
  502. foreach (var line in lines)
  503. {
  504. var bytes = Encoding.GetEncoding("GB2312").GetBytes(line);
  505. targetBytes.Add(0x01);
  506. targetBytes.AddRange(EnsureLength(bytes));
  507. CurrentReceipt.Add(new ReceiptLine(targetBytes.ToArray(), 31));
  508. targetBytes.Clear();
  509. }
  510. }
  511. private void CreateQRCode(string url)
  512. {
  513. int frameLen = 80;
  514. byte[] source = Encoding.ASCII.GetBytes(url);
  515. List<byte> target = new List<byte>();
  516. if (source.Length <= frameLen)
  517. {
  518. byte[] buffer = new byte[source.Length];
  519. Buffer.BlockCopy(source, 0, buffer, 0, source.Length);
  520. target.Add(0x04);
  521. target.AddRange(buffer);
  522. CurrentReceipt.Add(new ReceiptLine(target.ToArray(), source.Length + 1));
  523. target.Clear();
  524. }
  525. else
  526. {
  527. for (int i = 0; i < source.Length; i += frameLen)
  528. {
  529. if (source.Length - i < frameLen)
  530. {
  531. byte[] buffer = new byte[source.Length - i];
  532. Buffer.BlockCopy(source, i, buffer, 0, source.Length - i);
  533. target.Add(0x04);
  534. target.AddRange(buffer);
  535. CurrentReceipt.Add(new ReceiptLine(target.ToArray(), source.Length - i + 1));
  536. }
  537. else
  538. {
  539. byte[] buffer = new byte[frameLen];
  540. Buffer.BlockCopy(source, i, buffer, 0, frameLen);
  541. target.Add(0x03);
  542. target.AddRange(buffer);
  543. CurrentReceipt.Add(new ReceiptLine(target.ToArray(), frameLen + 1));
  544. }
  545. target.Clear();
  546. }
  547. }
  548. }
  549. private void CreateReceiptEnd(string endLine)
  550. {
  551. List<byte> targetBytes = new List<byte>();
  552. var bytes = Encoding.GetEncoding("GB2312").GetBytes(endLine);
  553. targetBytes.Add(0x02);
  554. targetBytes.AddRange(bytes);
  555. CurrentReceipt.Add(new ReceiptLine(targetBytes.ToArray(), 31));
  556. targetBytes.Clear();
  557. }
  558. private byte[] EnsureLength(byte[] bytes)
  559. {
  560. if (bytes.Length < 30)
  561. {
  562. List<byte> targetBytes = new List<byte>();
  563. targetBytes.AddRange(bytes);
  564. for (int i = 0; i < 30 - bytes.Length; i++)
  565. {
  566. targetBytes.Add(0x20);
  567. }
  568. return targetBytes.ToArray();
  569. }
  570. else
  571. {
  572. return bytes.Take(30).ToArray();
  573. }
  574. }
  575. public byte[] GetNextReceiptLine(int i)
  576. {
  577. debugLogger.Add("nfe current i = " + i);
  578. debugLogger.Add("nfe total count = " + CurrentReceipt.Count);
  579. if (i <= CurrentReceipt.Count - 1)
  580. return CurrentReceipt.ElementAt(i).Line;
  581. return null;
  582. }
  583. #endregion
  584. #region Card reader trx handling
  585. public void SetICCardReaderToCarPlateIdle(out byte sqNo)
  586. {
  587. StartFuelPresetProcessRequest setCardReaderToIdle = new StartFuelPresetProcessRequest(StartFuelPresetProcessReason.CarPlatePaymentIsInProcessing)
  588. {
  589. MessageSeqNumber = GetSenderSideSqNo(),
  590. SourceAddress = CardReaderSrcAddr
  591. };
  592. sqNo = setCardReaderToIdle.MessageSeqNumber;
  593. PendMessage(setCardReaderToIdle);
  594. }
  595. public void NotifyCardReaderTrxDone(out byte sqNo)
  596. {
  597. NotifyTransactionIsDoneRequest trxDoneRequest = new NotifyTransactionIsDoneRequest()
  598. {
  599. MessageSeqNumber = GetSenderSideSqNo(),
  600. SourceAddress = CardReaderSrcAddr
  601. };
  602. sqNo = trxDoneRequest.MessageSeqNumber;
  603. PendMessage(trxDoneRequest);
  604. }
  605. public void CloseICCardReader(out byte sqNo)
  606. {
  607. CloseCardReaderRequest closeCardReader = new CloseCardReaderRequest
  608. {
  609. MessageSeqNumber = GetSenderSideSqNo(),
  610. SourceAddress = CardReaderSrcAddr
  611. };
  612. sqNo = closeCardReader.MessageSeqNumber;
  613. PendMessage(closeCardReader);
  614. }
  615. public void OpenCardReader(out byte? sqNo)
  616. {
  617. OpenCardReaderRequest openCardReader = new OpenCardReaderRequest
  618. {
  619. MessageSeqNumber = GetSenderSideSqNo(),
  620. SourceAddress = CardReaderSrcAddr
  621. };
  622. sqNo = openCardReader.MessageSeqNumber;
  623. PendMessage(openCardReader);
  624. }
  625. public void SendICCardDisplay(string carPlate, out byte sqNo)
  626. {
  627. string licenseNo = carPlate;
  628. byte[] newbytes = new byte[64];
  629. for (int i = 0; i < 64; i++)
  630. {
  631. newbytes[i] = 0x20;
  632. }
  633. byte[] bytes = Encoding.GetEncoding("gb2312").GetBytes(licenseNo);
  634. if (bytes.Count() < 64)
  635. bytes.CopyTo(newbytes, 0);
  636. DisplayRequest displayRequest = new DisplayRequest(newbytes, CardReaderDisplayTimeout)
  637. {
  638. MessageSeqNumber = GetSenderSideSqNo(),
  639. SourceAddress = CardReaderSrcAddr
  640. };
  641. sqNo = displayRequest.MessageSeqNumber;
  642. PendMessage(displayRequest);
  643. }
  644. public byte[] GetMAC(EpsTransactionModel epsTrxModel)
  645. {
  646. debugLogger.AddIfActive($"fuel amount: {epsTrxModel.amount}");
  647. debugLogger.AddIfActive($"fuel quantity: {epsTrxModel.qty}");
  648. string cardNo = epsTrxModel.card_no;// "8888118001000078417";
  649. string carPlate = string.IsNullOrEmpty(epsTrxModel.car_number) ? "晋A12345" : epsTrxModel.car_number;
  650. byte[] chsByte = Encoding.Unicode.GetBytes(carPlate[0].ToString());
  651. byte[] b1 = { chsByte[1], chsByte[0] };
  652. string asciiBytes = carPlate.Substring(1, 6).PadRight(8, ' ');
  653. byte[] b2 = Encoding.ASCII.GetBytes(asciiBytes);
  654. byte[] newBytes = new byte[b1.Length + b2.Length];
  655. b1.CopyTo(newBytes, 0);
  656. b2.CopyTo(newBytes, 2);
  657. if (string.IsNullOrEmpty(epsTrxModel.car_number))
  658. newBytes = new byte[10] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
  659. string ts = epsTrxModel.created_time.ToString("yyyyMMddHHmmss");//"20180425110509";
  660. double fuelAmount = epsTrxModel.amount;//10.00;
  661. double fuelVolume = epsTrxModel.qty;//1.00;
  662. int nozzleNumber = epsTrxModel.jihao;
  663. string tid = epsTrxModel.tid;//"111001000633";
  664. var targetSignBytes = cardNo.PadLeft(20, '0').ToBCD()
  665. .Concat(newBytes)
  666. .Concat(ts.ToBCD()
  667. .Concat(((int)((decimal)fuelAmount * 100)).GetBinBytes(4))
  668. .Concat(((int)((decimal)fuelVolume * 100)).GetBinBytes(4))
  669. .Concat(nozzleNumber.GetBCDBytes(2)));
  670. return targetSignBytes.ToArray();
  671. //string cardNumber = "08888118001000078417";//carPlateTrxRequest.card_No;
  672. ////string carPlate = Encoding.GetEncoding("gbk").GetString(Encoding.GetEncoding("gbk").GetBytes("晋A12345"));//carPlateTrxRequest.car_Number));
  673. //string carPlate = "晋A12345";
  674. //byte[] b = Encoding.Unicode.GetBytes(carPlate[0].ToString());
  675. //byte[] b1 = { b[1], b[0] };
  676. //string sss = carPlate.Substring(0, 6).PadRight(8, ' ');
  677. //byte[] b2 = Encoding.ASCII.GetBytes(sss);
  678. //byte[] newBytes = new byte[b1.Length + b2.Length];
  679. //b1.CopyTo(newBytes, 0);
  680. //b2.CopyTo(newBytes, 2);
  681. //string ts = "20180425110509";
  682. //DateTime fuelingTime = DateTime.Now;
  683. //int fuelAmount = 10;
  684. //int fuelVolume = 1;
  685. //int nozzleNumber = 2;
  686. //var targetSignBytes = cardNumber.PadRight(20, ' ').ToBCD()
  687. // .Concat(newBytes)
  688. // //.Concat(Encoding.GetEncoding("gbk").GetBytes(carPlate))
  689. // .Concat(ts.ToBCD()// fuelingTime.ToString("yyyyMMddHHmmss"))
  690. // .Concat(fuelAmount.GetBinBytes(4))
  691. // .Concat(fuelVolume.GetBinBytes(4))
  692. // .Concat(nozzleNumber.GetBCDBytes(2)));
  693. //return targetSignBytes.ToArray();
  694. //string cardNumber = carPlateTrxRequest.card_No;
  695. //string carPlate = Encoding.GetEncoding("gbk").GetString(Encoding.GetEncoding("gbk").GetBytes(carPlateTrxRequest.car_Number));
  696. //DateTime fuelingTime = DateTime.Now;
  697. //int fuelAmount = 99;
  698. //int fuelVolume = 14;
  699. //int nozzleNumber = 1;
  700. //var targetSignBytes = cardNumber.PadRight(20, ' ').ToBCD()
  701. // .Concat(Encoding.GetEncoding("gbk").GetBytes(carPlate))
  702. // .Concat(ASCIIEncoding.ASCII.GetBytes(fuelingTime.ToString("yyyyMMddHHmmss"))
  703. // .Concat(fuelAmount.GetBinBytes(4))
  704. // .Concat(fuelVolume.GetBinBytes(4))
  705. // .Concat(nozzleNumber.GetBCDBytes(2)));
  706. //var expected = new byte[] { 0xFA, 0xD2, }.Concat((targetSignBytes.Count() + 1).GetBCDBytes(2))
  707. // .Concat(new byte[] { 0x05 })
  708. // .Concat(targetSignBytes)
  709. // .Concat(new byte[] { 0x84, 0xb0 });
  710. var signDataRequest = new SignDataRequest(targetSignBytes.ToArray())
  711. {
  712. MessageSeqNumber = GetSenderSideSqNo(),
  713. SourceAddress = CardReaderSrcAddr
  714. };
  715. sqNo = signDataRequest.MessageSeqNumber;
  716. PendMessage(signDataRequest);
  717. }
  718. #endregion
  719. #region Printer
  720. public bool GetPrinter()
  721. {
  722. lock (printerSyncObj)
  723. {
  724. if (printerIdle == true)
  725. {
  726. printerIdle = false;
  727. return true;
  728. }
  729. else
  730. {
  731. GetPrinterCount++;
  732. return false;
  733. }
  734. }
  735. }
  736. public void ReleasePrinter()
  737. {
  738. lock (printerSyncObj)
  739. {
  740. printerIdle = true;
  741. }
  742. }
  743. #endregion
  744. #region Cleanup
  745. public void Cleanup()
  746. {
  747. CurrentEpsTrx = null;
  748. CurrentTrxMode = TransactionMode.Unknown;
  749. AuthorizationId = null;
  750. CurrentNozzleId = Eps.InvalidNozzleId;
  751. GetPrinterCount = 0;
  752. }
  753. #endregion
  754. }
  755. }