FuelingPoint.cs 33 KB

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