12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259 |
- using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;
- using Edge.Core.Parser.BinaryParser.Util;
- using SinochemCarplateService.Models;
- using SinochemCloudClient.Models;
- using SinochemPosClient.Models;
- using System;
- using System.Collections.Generic;
- using System.Configuration;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using Wayne.Lib.StateEngine;
- using WayneChina_IcCardReader_SinoChem.MessageEntity;
- using WayneChina_IcCardReader_SinoChem.MessageEntity.Incoming;
- using WayneChina_IcCardReader_SinoChem.MessageEntity.Outgoing;
- namespace SinochemInternetPlusApp
- {
- public class ReceiptLine
- {
- public ReceiptLine(byte[] bytes, int lineWidth)
- {
- Line = new byte[lineWidth];
- bytes.CopyTo(Line, 0);
- }
- public byte[] Line { get; set; }
- }
- public class FuelingPoint : StateManager
- {
- #region Fields
- //Immutable after construction
- public int FuelingPointId { get; }
- private byte initialSqNo = 0;
- private byte sqNo;
- private object syncObj = new object();
- private Queue<IcCardReaderMessageBase> outboundMsgQueue = new Queue<IcCardReaderMessageBase>();
- private object queueSyncObj = new object();
- private object evSyncObj = new object();
- private object operantionSyncObj = new object();
- private List<int> requestIdList = new List<int>();
- private object reqIdSyncObj = new object();
- private bool printerIdle = true;
- private object printerSyncObj = new object();
- private byte currentSqNo { get; set; }
- private object sqNoSyncObj = new object();
- private IFdcPumpController callingPump;
- #endregion
- #region Timeout values
- //in seconds
- public int CardReaderDisplayTimeout { get { return 10; } }
- //in milliseconds
- public int CardReaderBackToIdleTimeout { get { return 2000; } }
- #endregion
- #region Properties
- /// <summary>
- /// Current physical nozzle id, reflecting the running nozzle of this FP.
- /// </summary>
- public int CurrentNozzleId { get; set; }
- /// <summary>
- /// The physical nozzles associated with this fueling point
- /// </summary>
- public IEnumerable<int> AssociatedNozzles { get; set; }
- public Eps Eps { get; set; }
- public EpsTransaction NewEpsTrx { get; set; }
- public EpsTransaction CurrentEpsTrx { get; set; }
- public EpsTransactionMode CurrentTrxMode { get; set; }
- public long? AuthorizationId { get; set; }
- public byte ResetSqNo;
- public override string EntityType => "FuelingPoint";
- public override string EntitySubType => "";
- public IEnumerable<EpsTransactionModel> ActiveTrx { get; set; }
- public CardOnlineVerificationRequest OnlineVerificationRequest { get; set; }
- public WayneChina_IcCardReader_SinoChem.Handler CurrentCardReader => Eps.GetHandler(FuelingPointId);
- public SignDataResponse SignedData { get; internal set; }
- public List<ReceiptLine> CurrentReceipt { get; set; }
- public bool CardReaderDisabled { get; set; } = false;
- public byte IdleStateCardReaderSqNo { get; set; }
- public int GetPrinterCount { get; set; }
- private Dictionary<int, string> NozzeIdGradeNameDictOnThisSide = new Dictionary<int, string>();
- /// <summary>
- /// Site id aka Station number
- /// </summary>
- public string StationNo => GenericSinochemEpsApp.AppSettings["SinochemSiteId"];
- /// <summary>
- /// Site name aka Station name
- /// </summary>
- public string StationName => GenericSinochemEpsApp.AppSettings["SinochemSiteName"];
- private bool printReceiptEnabled => Convert.ToBoolean(GenericSinochemEpsApp.AppSettings["PrintReceiptEnabled"]);
- private bool printQRCodeEnabled => Convert.ToBoolean(GenericSinochemEpsApp.AppSettings["PrintQrCodeOnReceiptEnabled"]);
- private int? liftedNozzleId = null;
- #endregion
- #region Request Id operations
- public void AddRequestId(int reqIid)
- {
- lock (reqIdSyncObj)
- {
- requestIdList.Add(reqIid);
- }
- }
- public void RemoveRequestId(int reqIid)
- {
- lock (reqIdSyncObj)
- {
- requestIdList.Remove(reqIid);
- }
- }
- public bool ContainsRequestId(int reqIid)
- {
- lock (reqIdSyncObj)
- {
- return requestIdList.Contains(reqIid);
- }
- }
- public void ClearAllRequestIds()
- {
- lock (reqIdSyncObj)
- {
- foreach (var id in requestIdList)
- {
- debugLogger.Add($"Clearing RequestId: {id}");
- }
- requestIdList.Clear();
- }
- }
- #endregion
- #region Constructor
- public FuelingPoint(int fuelingPointId, IEnumerable<int> associatedNozzles, Eps eps) : base(fuelingPointId, "FuelingPoint")
- {
- FuelingPointId = fuelingPointId;
- AssociatedNozzles = associatedNozzles;
- Eps = eps;
- }
- #endregion
- public override void Start()
- {
- base.Start();
- Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
- }
- #region DB & Trx handling
- /// <summary>
- /// Create an eps trx given the mop type.
- /// </summary>
- /// <param name="mop"></param>
- internal void CreateEpsTransaction(EpsTransactionMode mop)
- {
- CurrentEpsTrx = EpsTransaction.CreateEpsTrx(CurrentNozzleId, mop, debugLogger);
- }
- internal void LockTrxInXiaofei2AsEpsTrx()
- {
- if (CurrentEpsTrx.MoveFromXiaofei2(debugLogger))
- {
- }
- else
- {
- debugLogger.Add("trx in xiaofei2 not found!!!!");
- }
- }
- #endregion
- #region Cloud & POS interactions
- internal void NotifyPosAync()
- {
- ThreadPool.QueueUserWorkItem(_ =>
- {
- TrxNotificationResponse response = Eps.NotifySuccessfulTrxToPos(CurrentEpsTrx.Model, debugLogger);
- if (response != null && response.IsSuccessful())
- {
- stateMachine.IncomingEvent(new StateEngineEvent(EventType.PosNotifyOk));
- }
- else
- {
- stateMachine.IncomingEvent(new StateEngineEvent(EventType.PosNotifyFailed));
- }
- });
- }
- internal void SendPaymentToCloudAsync()
- {
- ThreadPool.QueueUserWorkItem(_ =>
- {
- if (CurrentEpsTrx != null)
- {
- PaymentResponse response = Eps.SendPaymentToCloud(CurrentEpsTrx.Model, debugLogger);
- if (response != null)
- {
- switch (response.code)
- {
- case PaymentResponse.SuccessResponse:
- {
- stateMachine.IncomingEvent(GenericEvent.Create(EventType.CloudPaymentOk, this, response));
- break;
- }
- //CarPlate User, need print the pay_url
- case PaymentResponse.QRCodeResponse:
- {
- if (response.result != null && !string.IsNullOrEmpty(response.result.pay_url))
- stateMachine.IncomingEvent(GenericEvent.Create(EventType.QRCodePayment, this, response));
- else
- stateMachine.IncomingEvent(GenericEvent.Create(EventType.CloudPaymentFailed, this, response));
- break;
- }
- //普通车主
- case PaymentResponse.PlainUserResponse:
- {
- stateMachine.IncomingEvent(GenericEvent.Create(EventType.PlainUser_Unpaid, this, response));
- break;
- }
- default:
- {
- if (response.result != null && !string.IsNullOrEmpty(response.result.pay_url))
- stateMachine.IncomingEvent(GenericEvent.Create(EventType.CloudPaymentFailed, this, response));
- else
- stateMachine.IncomingEvent(GenericEvent.Create(EventType.PlainUser_Unpaid, this, response));
- break;
- }
- }
- }
- }
- else
- {
- stateMachine.IncomingEvent(new StateEngineEvent(EventType.CloudPaymentFailed));
- }
- });
- }
- internal void NotifyPaymentFailedAsync()
- {
- ThreadPool.QueueUserWorkItem(_ =>
- {
- stateMachine.IncomingEvent(new StateEngineEvent(EventType.CloudPaymentFailed));
- });
- }
- internal void SendBalanceQueryToCloudAsync(string cardNo, string encryptedPin, string tid, int nozzleId)
- {
- ThreadPool.QueueUserWorkItem(_ =>
- {
- BalanceInquiryResponse response = Eps.SendBalanceInquiryToCloud(cardNo, encryptedPin, tid, nozzleId, debugLogger);
- if (response != null && response.IsSuccessful())
- {
- stateMachine.IncomingEvent(GenericEvent.Create(EventType.CloudBalanceOk, this, response));
- }
- else
- {
- stateMachine.IncomingEvent(new StateEngineEvent(EventType.CloudBalanceFailed));
- }
- });
- }
- #endregion
- #region Pump interactions
- internal void AuthorizePumpAsync(decimal authAmount)
- {
- Eps.AuthorizePumpAsync(callingPump, CurrentNozzleId, authAmount);
- }
- internal List<int> GetAllNozzlesOnThisSide()
- {
- List<int> nozzles = new List<int>();
- if (CurrentCardReader.NumberOfNozzlesPerSide != 1)
- {
- foreach (var fp in CurrentCardReader.SupportedNozzles)
- {
- var fuelingPoint = this.Eps.GetFuelingPoint(fp);
- if (fuelingPoint != null)
- nozzles.AddRange(fuelingPoint.AssociatedNozzles);
- }
- }
- else
- {
- nozzles.AddRange(AssociatedNozzles);
- }
- return nozzles;
- }
- internal bool HasBigSreen()
- {
- return CurrentCardReader.HasBigScreen;
- }
- #endregion
- #region Eps state config and signal
- protected override void ConfigStates()
- {
- States.CONFIGURATION.Config(stateMachine.StateTransitionLookup);
- }
- public void SignalShutdown()
- {
- Console.Out.WriteLine("SignalShutdown");
- stateMachine.IncomingEvent(new StateEngineEvent(EventType.ShutdownRequest));
- }
- public void FinalStateReached(string name)
- {
- Console.Out.WriteLine(name + " FinalStateReached");
- }
- #endregion
- #region Card Plate
- public void SignalNewCarplate(CarPlateTrxRequest request)
- {
- lock (evSyncObj)
- {
- if (request != null && AssociatedNozzles.Contains(Convert.ToInt32(request.gun)))
- {
- debugLogger.AddIfActive
- ($"New car plate info arrived: Gun: {request.gun}, License plate No: {request.car_Number}, Balance: {request.amount}");
- stateMachine.IncomingEvent(GenericEvent.Create(EventType.CarPlateScanned, this, request));
- }
- }
- }
- #endregion
- #region Display on dispenser interactions
- public void SignalDisplayResponseReceived(DisplayResponse response)
- {
- debugLogger.AddIfActive("DisplayResponse from big screen");
- stateMachine.IncomingEvent(new GenericEvent<DisplayResponseEventArgs>(
- EventType.DisplayResponseReceived, this, new DisplayResponseEventArgs(response)));
- }
- public bool SignalTrxOpRequest(TransactionOperation request)
- {
- debugLogger.AddIfActive($"Transaction operation request from big screen, Trx id: {request?.TrxId}, Op:{request?.OpType}");
- bool result = false;
- if (request != null)
- {
- int trxId = request.TrxId;
- var validTrx = EpsTransactionQuery.GetValidCarPlateEpsTrxModels(
- GetAllNozzlesOnThisSide(),
- ConfigurationValues.AlreadyDoneEpsTrxCountPerDisplay,
- DebugLogger);
- if (request.OpType == OpType.NozzleSelect)
- {
- int selectedNozzleNo = int.Parse(request.NozzleNo);
- string gradeName = Eps.NozzleMappingGradeName()?.Where(i => i.Key == selectedNozzleNo).Select(n => n.Value).FirstOrDefault();
- foreach (var trx in validTrx)
- {
- if (trx.id == trxId)
- {
- lock (operantionSyncObj)
- {
- debugLogger.Add($"Current selected nozzle, number = {selectedNozzleNo}");
- //ICCard sale, need to remove the trx created when carplate scanned
- if (CurrentEpsTrx != null && CurrentEpsTrx.Model.mop == EpsTransactionMode.ICCardMode)
- {
- debugLogger.Add("nozzleSelected CurrentEpsTrx is ICCardMode, ICCardEpsTrxId:" + CurrentEpsTrx.Model.id);
- CurrentEpsTrx.Model.mop = EpsTransactionMode.CarPlateMode;
- CurrentEpsTrx.Model.car_number = trx.car_number;
- CurrentEpsTrx.Model.jihao = selectedNozzleNo;
- CurrentEpsTrx.Model.youpin = gradeName;
- CurrentEpsTrx.Model.nozzleSelected = 1;
- CurrentEpsTrx.SaveToDb();
- //Remove the epsTrx for carplate scanned
- EpsTransaction.RestroeEpsTrxFrom(trx).UpdateTrxStatusToDb(trx, EpsTrxStatus.Removed);
- }
- else if (CurrentEpsTrx != null && CurrentEpsTrx.Model.id == trx.id)
- {
- trx.jihao = selectedNozzleNo;
- CurrentEpsTrx.Model.jihao = selectedNozzleNo;
- trx.youpin = gradeName;
- CurrentEpsTrx.Model.youpin = gradeName;
- trx.nozzleSelected = 1;
- CurrentEpsTrx.Model.nozzleSelected = 1;
- var fp = Eps.GetFp(selectedNozzleNo);
- PumpState fpStatus = Eps.GetFPState(fp.Id);
-
- debugLogger.Add($"CurrentEpsTrx not null 1, currentEpsTrx Id = {CurrentEpsTrx.Model.id}, " +
- $"request trx id: {trx.id}, fpStattus = {fpStatus}, jihao = {trx.jihao}");
-
- if (fpStatus == PumpState.Fuelling)
- {
- debugLogger.Add("setting pump state to fuelling");
- trx.trx_status = EpsTrxStatus.Fueling;
- CurrentEpsTrx.Model.trx_status = EpsTrxStatus.Fueling;
- }
- else if (fpStatus == PumpState.Idle)
- {
- debugLogger.Add("setting pump state to BeforeFueling");
- trx.trx_status = EpsTrxStatus.BeforeFueling;
- CurrentEpsTrx.Model.trx_status = EpsTrxStatus.BeforeFueling;
- }
- var restoredTrx = EpsTransaction.RestroeEpsTrxFrom(trx);
- if (restoredTrx != null)
- {
- debugLogger.Add($"restored trx, jihao = {restoredTrx.Model.jihao}");
- restoredTrx.SaveToDb();
- debugLogger.Add("Restored Eps transaction and saved to database");
- }
- CurrentEpsTrx.SaveToDb();
- }
- else
- {
- if (CurrentEpsTrx != null)
- {
- var RestoredCurrentEpsTrxModel = EpsTransactionQuery.RefreshEpsTrx(CurrentEpsTrx.Model.id);
- //1 nozzle, Only 1 trx that the status is Fueling
- if (RestoredCurrentEpsTrxModel != null
- && RestoredCurrentEpsTrxModel.trx_status == EpsTrxStatus.Fueling
- && RestoredCurrentEpsTrxModel.jihao == selectedNozzleNo
- && RestoredCurrentEpsTrxModel.id != trx.id)
- {
- CurrentEpsTrx.UpdateTrxStatusToDb(CurrentEpsTrx.Model, EpsTrxStatus.BeforeFueling);
- }
- }
- CurrentEpsTrx = EpsTransaction.RestroeEpsTrxFrom(trx);
- CurrentEpsTrx.Model.jihao = selectedNozzleNo;
- CurrentEpsTrx.Model.youpin = gradeName;
- CurrentEpsTrx.Model.nozzleSelected = 1;
- var fp = Eps.GetFp(selectedNozzleNo);
- PumpState fpStatus = Eps.GetFPState(fp.Id);
- debugLogger.Add($"CurrentEpsTrx not null 3, currentEpsTrx Id = {CurrentEpsTrx.Model.id}, request trx id: {trx.id}, fpStattus = {fpStatus}");
-
- if (fpStatus == PumpState.Fuelling)
- {
- CurrentEpsTrx.Model.trx_status = EpsTrxStatus.Fueling;
- }
- else if (fpStatus == PumpState.Idle)
- {
- CurrentEpsTrx.Model.trx_status = EpsTrxStatus.BeforeFueling;
- }
- CurrentEpsTrx.SaveToDb();
- }
- BroadcastTrxListChanged(true);
- result = true;
- DebugLogger.Add(string.Format("Nozzle {0} Selected, update db", selectedNozzleNo));
- break;
- }
- }
- }
- }
- else if (request.OpType == OpType.Delete)
- {
- debugLogger.AddIfActive($"Delete Transaction operation request: {trxId}");
- foreach (var trx in validTrx)
- {
- if (trx.id == trxId)
- {
- lock (operantionSyncObj)
- {
- //Remove the epsTrx for carplate scanned
- EpsTransaction.RestroeEpsTrxFrom(trx).UpdateTrxStatusToDb(trx, EpsTrxStatus.Removed);
- if (CurrentEpsTrx != null && CurrentEpsTrx.Model.id == trx.id)
- {
- debugLogger.AddIfActive($"Delete Transaction operation request3: {CurrentEpsTrx.Model.id}");
- CurrentEpsTrx = null;
- }
- result = true;
- BroadcastTrxListChanged();
- break;
- }
- }
- }
- }
- }
- return result;
- //stateMachine.IncomingEvent(new GenericEvent<TransactionOperationEventArgs>(EventType.TrxOpRequest, this, new TransactionOperationEventArgs(request)));
- }
- private void BroadcastTrxListChanged(bool nozzleSelected = false)
- {
- var validTrx = EpsTransactionQuery.GetValidCarPlateEpsTrxModels(
- GetAllNozzlesOnThisSide(),
- ConfigurationValues.AlreadyDoneEpsTrxCountPerDisplay,
- DebugLogger);
- var availableNozzleInfo = GetAvailableNozzleInfo();
- if (availableNozzleInfo != null)
- {
- foreach (var trx in validTrx)
- {
- if (trx.trx_status == EpsTrxStatus.BeforeFueling || trx.trx_status == EpsTrxStatus.Fueling)
- {
- trx.AvailableNozzleGrade = availableNozzleInfo;
- }
- }
- }
- int trxOpRequestId;
- //Refresh 15" screen
- var text = Eps.CreateDisplayTrxCommand(validTrx, out trxOpRequestId, 0);
- if (Eps.CarPlateHandler != null)
- {
- DebugLogger.AddIfActive("NozzleSelected broadcast text:" + text);
- Eps.CarPlateHandler.BroadcastMessageViaFdc(text);
- }
- }
- #endregion
- #region Card reader signals
- public void SingalAckReceived(ACK cardReaderMessage)
- {
- lock (sqNoSyncObj)
- {
- if (cardReaderMessage.MessageSeqNumber == currentSqNo)
- {
- debugLogger.AddIfActive($"Received card reader message with MsgSqNo = {cardReaderMessage.MessageSeqNumber}");
- outboundMsgQueue.Dequeue();
- debugLogger.AddIfActive($"Removed the message from the queue");
- }
- }
- stateMachine.IncomingEvent(new GenericEvent<CardReaderAckEventArgs>(EventType.CardReaderAck, this, new CardReaderAckEventArgs(cardReaderMessage)));
- }
- public void SignalCardReaderDisconnected()
- {
- stateMachine.IncomingEvent(new StateEngineEvent(EventType.CardReaderDisconnected));
- }
- public void SignalCardReaderDisabled()
- {
- stateMachine.IncomingEvent(new StateEngineEvent(EventType.ICCardReaderDisabled));
- }
- public void SignalCardReaderHeartbeat(HeartBeat heartBeat)
- {
- SendNextCardReaderMessage(heartBeat);
- }
- public void SignalRepeatedMessageRecieved(IcCardReaderMessageBase message)
- {
- SendAckToCardReader(message.SourceAddress, message.MessageSeqNumber);
- }
- public void SignalSignedDataArrived(SignDataResponse cardReaderMessage)
- {
- debugLogger.AddIfActive("sending ack for SignDataResponse");
- SendAckToCardReader(cardReaderMessage.SourceAddress, cardReaderMessage.MessageSeqNumber);
- stateMachine.IncomingEvent(new GenericEvent<SignedDataEventArgs>(EventType.DataSigned, this, new SignedDataEventArgs(cardReaderMessage)));
- }
- public void SingalCardReaderStateEvent(CardReaderStateEvent cardReaderStateEvent)
- {
- debugLogger.AddIfActive($"CardReaderStateEvent \n source address: {cardReaderStateEvent.SourceAddress} \n SqNo: {cardReaderStateEvent.MessageSeqNumber} \n Reader state: {cardReaderStateEvent.State.ToString()}");
- SendAckToCardReader(cardReaderStateEvent.SourceAddress, cardReaderStateEvent.MessageSeqNumber);
- stateMachine.IncomingEvent(new GenericEvent<CardReaderStateEventArgs>(EventType.ReaderStateChanged, this, new CardReaderStateEventArgs(cardReaderStateEvent)));
- }
- private void SendAckToCardReader(byte srcAddress, byte sqNo)
- {
- ACK ack = new ACK { SourceAddress = srcAddress, MessageSeqNumber = sqNo };
- if (CurrentCardReader != null)
- CurrentCardReader.Write(ack);
- }
- public void SignalCardExternalCheckFailure(CardExternalCheckErrorRequest cardReaderMessage)
- {
- debugLogger.AddIfActive($"IC Card check failure, Error reason: {cardReaderMessage.ErrorType.ToString()}");
- SendAckToCardReader(cardReaderMessage.SourceAddress, cardReaderMessage.MessageSeqNumber);
- stateMachine.IncomingEvent(new GenericEvent<ExternalCheckFailedEventArgs>(EventType.ExternalCheckFailure, this, new ExternalCheckFailedEventArgs(cardReaderMessage)));
- }
- public void SignalCardOnlineVerification(CardOnlineVerificationRequest cardReaderMessage)
- {
- debugLogger.AddIfActive($"IC Card online verification request, Card No: {cardReaderMessage.CardNo}, Encrypted PIN: {cardReaderMessage.EncryptedPIN}, TID: {cardReaderMessage.TID}");
- SendAckToCardReader(cardReaderMessage.SourceAddress, cardReaderMessage.MessageSeqNumber);
- stateMachine.IncomingEvent(new GenericEvent<CardOnlineVerificationEventArgs>(EventType.OnlineVerification, this, new CardOnlineVerificationEventArgs(cardReaderMessage)));
- }
- #endregion
- #region Pump signals
- // phase out, 2020/02/17
- //internal void SignalNozzleLifted(IPump callingPump, int sitewiseNozzleId)
- //{
- // debugLogger.Add("SignalNozzleLifted, nozzleid -- " + sitewiseNozzleId);
- // this.callingPump = callingPump;
- // stateMachine.IncomingEvent(
- // new GenericEvent<NozzleLiftedEventArgs>(
- // EventType.NozzleLifted,
- // this,
- // new NozzleLiftedEventArgs() { NozzleId = sitewiseNozzleId }));
- //}
- internal void SignalNozzleLifted(IFdcPumpController callingPump, int sitewiseNozzleId)
- {
- debugLogger.Add("SignalNozzleLifted, nozzleid -- " + sitewiseNozzleId);
- this.callingPump = callingPump;
- this.liftedNozzleId = sitewiseNozzleId;
- stateMachine.IncomingEvent(
- new GenericEvent<NozzleLiftedEventArgs>(
- EventType.NozzleLifted,
- this,
- new NozzleLiftedEventArgs() { NozzleId = sitewiseNozzleId }));
- }
- internal void SignalNozzleReplaced(int sitewiseNozzleId, int pumpId)
- {
- if (sitewiseNozzleId != 0)
- {
- debugLogger.Add("SignalNozzleReplaced, nozzleid -- " + sitewiseNozzleId);
-
- stateMachine.IncomingEvent(new GenericEvent<NozzleReplacedEventArgs>
- (EventType.NozzleReplaced, this, new NozzleReplacedEventArgs() { NozzleId = sitewiseNozzleId }));
- }
- else
- {
- debugLogger.Add($"SignalNozzleReplaced, nozzleid -- {sitewiseNozzleId}, use last lifted nozzle id: {liftedNozzleId}");
- if (liftedNozzleId.HasValue)
- {
- stateMachine.IncomingEvent(new GenericEvent<NozzleReplacedEventArgs>
- (EventType.NozzleReplaced, this, new NozzleReplacedEventArgs() { NozzleId = liftedNozzleId.Value }));
- }
- }
- }
- internal void SignalAuthOk(long? authId)
- {
- debugLogger.Add("SignalAuthOk -- " + authId);
- AuthorizationId = authId;
- var arg = new GenericEventArg<string>(DateTime.Now.ToString("HH:mm:ss"));
- stateMachine.IncomingEvent(GenericEvent.Create(EventType.PumpAuthOk, this, arg));
- }
- internal void SignalAuthFailed()
- {
- debugLogger.Add("SignalAuthFailed");
- stateMachine.IncomingEvent(new StateEngineEvent(EventType.PumpAuthFailed));
- }
- internal void SignalFuelingDone(int nozzleId, int seqNum, decimal fuelAmount, decimal fuelingQty, long authId)
- {
- debugLogger.Add("SignalFuelingDone -- " + authId);
- stateMachine.IncomingEvent(new GenericEvent<FuelingDoneEventArgs>(EventType.FuelingDone, this,
- new FuelingDoneEventArgs
- {
- NozzleId = nozzleId,
- FuelingSqNo = seqNum,
- Amount = fuelAmount,
- Quantity = fuelingQty,
- AuthId = authId
- }));
- }
- #endregion
- #region Card reader support
- /// <summary>
- /// Sender side's sequence number, 01H-0FH, when program restarted, use 00H.
- /// </summary>
- /// <returns>The sequence number byte.</returns>
- public byte GetSenderSideSqNo()
- {
- lock (syncObj)
- {
- if (initialSqNo == 0)
- {
- sqNo = initialSqNo;
- initialSqNo = 1;
- return sqNo;
- }
- byte nextSqNo = (byte)(sqNo++ % 16);
- if (nextSqNo == 0)
- {
- sqNo++;
- return initialSqNo;
- }
- return nextSqNo;
- }
- }
- public byte CardReaderSrcAddr
- {
- get
- {
- return (byte)(CurrentCardReader?.GetAddressForNozzleId(FuelingPointId));
- }
- }
- public void SendCommand(IcCardReaderMessageBase command, out byte sqNo)
- {
- command.MessageSeqNumber = GetSenderSideSqNo();
- command.SourceAddress = CardReaderSrcAddr;
- sqNo = command.MessageSeqNumber;
- PendMessage(command);
- }
- public void PendMessage(IcCardReaderMessageBase msg)
- {
- lock (queueSyncObj)
- {
- outboundMsgQueue.Enqueue(msg);
- }
- }
- public void RemovePendingBalanceResult()
- {
- lock (queueSyncObj)
- {
- var result = outboundMsgQueue.FirstOrDefault(m => m is CardOnlineVerificationResultRequest);
- if (result != null)
- {
- debugLogger.AddIfActive($"Removing item: {result.ToLogString()}");
- outboundMsgQueue = new Queue<IcCardReaderMessageBase>(outboundMsgQueue.Where(m => m != result));
- }
- }
- }
- public void SendNextCardReaderMessage(HeartBeat heartBeat)
- {
- lock (queueSyncObj)
- {
- if (outboundMsgQueue.Count > 0)
- {
- var msgToSend = outboundMsgQueue.Peek();
- lock (sqNoSyncObj)
- {
- currentSqNo = msgToSend.MessageSeqNumber;
- }
- debugLogger.AddIfActive($"Peek and send Card reader message {msgToSend.GetType().ToString()} with MsgSqNo = {msgToSend.MessageSeqNumber}");
- CurrentCardReader?.Write(msgToSend);
- }
- else
- {
- var eot = new EOT
- {
- SourceAddress = CardReaderSrcAddr,
- MessageSeqNumber = 0
- };
-
- CurrentCardReader?.Write(eot);
- }
- }
- }
- #endregion
- #region Receipt
- public void BuildReceipt()
- {
- if (CurrentReceipt == null)
- CurrentReceipt = new List<ReceiptLine>();
- else
- CurrentReceipt.Clear();
- List<string> bodyLines = new List<string>();
- List<string> header = new List<string>();
- List<string> product = new List<string>();
- List<string> payment = new List<string>();
- ReceiptModel receiptModel = ReceiptModelBuilder.BuildReceiptModel(CurrentEpsTrx, CurrentTrxMode);
- string lineSeparator = "------------------------------";
- string emptyLine = " ";
- //string receiptHeaderReceiptType = string.Format($" {receiptModel.TrxModeName}小票 ");
- string stationName = "油站名称:" + StationName;
- string trxTimeStamp = "时间:" + Utilities.ConvertDateTimeToReadble(DateTime.Now);
- string runningNumberLine = "流水号:" + receiptModel.RunningNumber;
- string cardNoLine = "卡号:" + receiptModel.CardNo;
- string gradeNameLine = "油品:" + receiptModel.GradeName;
- string nozzleIdLine = "枪号:" + receiptModel.NozzleId;
- string ppuLine = "单价:" + receiptModel.PPU + "元/升";
- string qtyLine = "数量:" + receiptModel.Qty + "升";
- string amountLine = "金额:" + receiptModel.Amount + "元";
- string dueAmountLine = string.Format($"应付: {receiptModel.DueAmount}元");
- string discountLine = string.Format($"优惠: {receiptModel.DiscountAmount}元");
- string payAmountLine = string.Format($"实付: {receiptModel.PayAmount}元");
- //header.Add(receiptHeaderReceiptType);
- header.Add(emptyLine);
- header.Add(stationName);
- header.Add(trxTimeStamp);
- header.Add(runningNumberLine);
- header.Add(cardNoLine);
- header.Add(lineSeparator);
- //Add header
- bodyLines.AddRange(header);
- product.Add(gradeNameLine);
- product.Add(nozzleIdLine);
- product.Add(ppuLine);
- product.Add(qtyLine);
- product.Add(amountLine);
- product.Add(lineSeparator);
- //Add product details
- bodyLines.AddRange(product);
- payment.Add(dueAmountLine);
- payment.Add(discountLine);
- payment.Add(payAmountLine);
- payment.Add(lineSeparator);
-
- //Add payment details
- bodyLines.AddRange(payment);
- //QR code
- if (printQRCodeEnabled && !string.IsNullOrEmpty(receiptModel.PayUrl))
- {
- string invoicePromptLine = string.Format(" 微信支付二维码 ");
- bodyLines.Add(invoicePromptLine);
- bodyLines.Add(emptyLine);
- }
- else if (printQRCodeEnabled && !string.IsNullOrEmpty(receiptModel.InvoiceUrl))
- {
- string invoicePromptLine = string.Format(" 电子发票二维码 ");
- bodyLines.Add(invoicePromptLine);
- bodyLines.Add(emptyLine);
- }
- //Thanks line
- string thanksLine = " 谢谢惠顾,欢迎再次光临 ";
- CreateReceiptLine(bodyLines);
- if (printQRCodeEnabled)
- {
- debugLogger.Add("EInvocie URL: " + receiptModel.PayUrl);
- if (!string.IsNullOrEmpty(receiptModel.PayUrl))
- CreateQRCode(receiptModel.PayUrl);
- else if (!string.IsNullOrEmpty(receiptModel.InvoiceUrl))
- CreateQRCode(receiptModel.InvoiceUrl);
- }
- CreateReceiptEnd(thanksLine);
- }
- private void CreateReceiptLine(List<string> lines)
- {
- List<byte> targetBytes = new List<byte>();
- foreach (var line in lines)
- {
- var bytes = Encoding.GetEncoding("GB2312").GetBytes(line);
- targetBytes.Add(0x01);
- targetBytes.AddRange(EnsureLength(bytes));
- CurrentReceipt.Add(new ReceiptLine(targetBytes.ToArray(), 31));
- targetBytes.Clear();
- }
- }
- private void CreateQRCode(string url)
- {
- int frameLen = 80;
- byte[] source = Encoding.ASCII.GetBytes(url);
- List<byte> target = new List<byte>();
- if (source.Length <= frameLen)
- {
- byte[] buffer = new byte[source.Length];
- Buffer.BlockCopy(source, 0, buffer, 0, source.Length);
- target.Add(0x04);
- target.AddRange(buffer);
- CurrentReceipt.Add(new ReceiptLine(target.ToArray(), source.Length + 1));
- target.Clear();
- }
- else
- {
- for (int i = 0; i < source.Length; i += frameLen)
- {
- if (source.Length - i < frameLen)
- {
- byte[] buffer = new byte[source.Length - i];
- Buffer.BlockCopy(source, i, buffer, 0, source.Length - i);
- target.Add(0x04);
- target.AddRange(buffer);
- CurrentReceipt.Add(new ReceiptLine(target.ToArray(), source.Length - i + 1));
- }
- else
- {
- byte[] buffer = new byte[frameLen];
- Buffer.BlockCopy(source, i, buffer, 0, frameLen);
- target.Add(0x03);
- target.AddRange(buffer);
- CurrentReceipt.Add(new ReceiptLine(target.ToArray(), frameLen + 1));
- }
- target.Clear();
- }
- }
- }
- private void CreateReceiptEnd(string endLine)
- {
- List<byte> targetBytes = new List<byte>();
- var bytes = Encoding.GetEncoding("GB2312").GetBytes(endLine);
- targetBytes.Add(0x02);
- targetBytes.AddRange(bytes);
- CurrentReceipt.Add(new ReceiptLine(targetBytes.ToArray(), 31));
- targetBytes.Clear();
- }
- private byte[] EnsureLength(byte[] bytes)
- {
- if (bytes.Length < 30)
- {
- List<byte> targetBytes = new List<byte>();
- targetBytes.AddRange(bytes);
- for (int i = 0; i < 30 - bytes.Length; i++)
- {
- targetBytes.Add(0x20);
- }
- return targetBytes.ToArray();
- }
- else
- {
- return bytes.Take(30).ToArray();
- }
- }
- public byte[] GetNextReceiptLine(int i)
- {
- debugLogger.Add("nfe current i = " + i);
- debugLogger.Add("nfe total count = " + CurrentReceipt.Count);
- if (i <= CurrentReceipt.Count - 1)
- return CurrentReceipt.ElementAt(i).Line;
- return null;
- }
- #endregion
- #region Card reader trx handling
- public void SetICCardReaderToCarPlateIdle(out byte sqNo)
- {
- StartFuelPresetProcessRequest setCardReaderToIdle = new StartFuelPresetProcessRequest(StartFuelPresetProcessReason.CarPlatePaymentIsInProcessing)
- {
- MessageSeqNumber = GetSenderSideSqNo(),
- SourceAddress = CardReaderSrcAddr
- };
- sqNo = setCardReaderToIdle.MessageSeqNumber;
- PendMessage(setCardReaderToIdle);
- }
- public void NotifyCardReaderTrxDone(out byte sqNo)
- {
- NotifyTransactionIsDoneRequest trxDoneRequest = new NotifyTransactionIsDoneRequest()
- {
- MessageSeqNumber = GetSenderSideSqNo(),
- SourceAddress = CardReaderSrcAddr
- };
- sqNo = trxDoneRequest.MessageSeqNumber;
- PendMessage(trxDoneRequest);
- }
- public void CloseICCardReader(out byte sqNo)
- {
- CloseCardReaderRequest closeCardReader = new CloseCardReaderRequest
- {
- MessageSeqNumber = GetSenderSideSqNo(),
- SourceAddress = CardReaderSrcAddr
- };
- sqNo = closeCardReader.MessageSeqNumber;
- PendMessage(closeCardReader);
- }
- public void OpenCardReader(out byte? sqNo)
- {
- OpenCardReaderRequest openCardReader = new OpenCardReaderRequest
- {
- MessageSeqNumber = GetSenderSideSqNo(),
- SourceAddress = CardReaderSrcAddr
- };
- sqNo = openCardReader.MessageSeqNumber;
- PendMessage(openCardReader);
- }
- public void SendICCardDisplay(string carPlate, out byte sqNo)
- {
- string licenseNo = carPlate;
- byte[] newbytes = new byte[64];
- for (int i = 0; i < 64; i++)
- {
- newbytes[i] = 0x20;
- }
- byte[] bytes = Encoding.GetEncoding("gb2312").GetBytes(licenseNo);
- if (bytes.Count() < 64)
- bytes.CopyTo(newbytes, 0);
- DisplayRequest displayRequest = new DisplayRequest(newbytes, CardReaderDisplayTimeout)
- {
- MessageSeqNumber = GetSenderSideSqNo(),
- SourceAddress = CardReaderSrcAddr
- };
- sqNo = displayRequest.MessageSeqNumber;
- PendMessage(displayRequest);
- }
- public byte[] GetMAC(EpsTransactionModel epsTrxModel)
- {
- debugLogger.AddIfActive($"fuel amount: {epsTrxModel.amount}");
- debugLogger.AddIfActive($"fuel quantity: {epsTrxModel.qty}");
- string cardNo = epsTrxModel.card_no;// "8888118001000078417";
- string carPlate = "晋A12345";
- //string carPlate = string.IsNullOrEmpty(epsTrxModel.car_number) ? "晋A12345" : epsTrxModel.car_number;
- byte[] chsByte = Encoding.Unicode.GetBytes(carPlate[0].ToString());
- byte[] b1 = { chsByte[1], chsByte[0] };
- string asciiBytes = carPlate.Substring(1, 6).PadRight(8, ' ');
- byte[] b2 = Encoding.ASCII.GetBytes(asciiBytes);
- byte[] newBytes = new byte[b1.Length + b2.Length];
- b1.CopyTo(newBytes, 0);
- b2.CopyTo(newBytes, 2);
- //if (string.IsNullOrEmpty(epsTrxModel.car_number))
- newBytes = new byte[10] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
- string ts = epsTrxModel.created_time.ToString("yyyyMMddHHmmss");//"20180425110509";
- double fuelAmount = epsTrxModel.amount;//10.00;
- double fuelVolume = epsTrxModel.qty;//1.00;
- int nozzleNumber = epsTrxModel.jihao;
- string tid = epsTrxModel.tid;//"111001000633";
- var targetSignBytes = cardNo.PadLeft(20, '0').ToBCD()
- .Concat(newBytes)
- .Concat(ts.ToBCD()
- .Concat(((int)((decimal)fuelAmount * 100)).GetBinBytes(4))
- .Concat(((int)((decimal)fuelVolume * 100)).GetBinBytes(4))
- .Concat(nozzleNumber.GetBCDBytes(2)));
- return targetSignBytes.ToArray();
- //string cardNumber = "08888118001000078417";//carPlateTrxRequest.card_No;
- ////string carPlate = Encoding.GetEncoding("gbk").GetString(Encoding.GetEncoding("gbk").GetBytes("晋A12345"));//carPlateTrxRequest.car_Number));
- //string carPlate = "晋A12345";
- //byte[] b = Encoding.Unicode.GetBytes(carPlate[0].ToString());
- //byte[] b1 = { b[1], b[0] };
- //string sss = carPlate.Substring(0, 6).PadRight(8, ' ');
- //byte[] b2 = Encoding.ASCII.GetBytes(sss);
- //byte[] newBytes = new byte[b1.Length + b2.Length];
- //b1.CopyTo(newBytes, 0);
- //b2.CopyTo(newBytes, 2);
- //string ts = "20180425110509";
- //DateTime fuelingTime = DateTime.Now;
- //int fuelAmount = 10;
- //int fuelVolume = 1;
- //int nozzleNumber = 2;
- //var targetSignBytes = cardNumber.PadRight(20, ' ').ToBCD()
- // .Concat(newBytes)
- // //.Concat(Encoding.GetEncoding("gbk").GetBytes(carPlate))
- // .Concat(ts.ToBCD()// fuelingTime.ToString("yyyyMMddHHmmss"))
- // .Concat(fuelAmount.GetBinBytes(4))
- // .Concat(fuelVolume.GetBinBytes(4))
- // .Concat(nozzleNumber.GetBCDBytes(2)));
- //return targetSignBytes.ToArray();
- //string cardNumber = carPlateTrxRequest.card_No;
- //string carPlate = Encoding.GetEncoding("gbk").GetString(Encoding.GetEncoding("gbk").GetBytes(carPlateTrxRequest.car_Number));
- //DateTime fuelingTime = DateTime.Now;
- //int fuelAmount = 99;
- //int fuelVolume = 14;
- //int nozzleNumber = 1;
- //var targetSignBytes = cardNumber.PadRight(20, ' ').ToBCD()
- // .Concat(Encoding.GetEncoding("gbk").GetBytes(carPlate))
- // .Concat(ASCIIEncoding.ASCII.GetBytes(fuelingTime.ToString("yyyyMMddHHmmss"))
- // .Concat(fuelAmount.GetBinBytes(4))
- // .Concat(fuelVolume.GetBinBytes(4))
- // .Concat(nozzleNumber.GetBCDBytes(2)));
- //var expected = new byte[] { 0xFA, 0xD2, }.Concat((targetSignBytes.Count() + 1).GetBCDBytes(2))
- // .Concat(new byte[] { 0x05 })
- // .Concat(targetSignBytes)
- // .Concat(new byte[] { 0x84, 0xb0 });
- var signDataRequest = new SignDataRequest(targetSignBytes.ToArray())
- {
- MessageSeqNumber = GetSenderSideSqNo(),
- SourceAddress = CardReaderSrcAddr
- };
- sqNo = signDataRequest.MessageSeqNumber;
- PendMessage(signDataRequest);
- }
- #endregion
- /// <summary>
- /// Get all the Idle nozzle at this side
- /// </summary>
- /// <returns></returns>
- public Dictionary<int, string> GetAvailableNozzleInfo()
- {
- if (NozzeIdGradeNameDictOnThisSide != null && NozzeIdGradeNameDictOnThisSide.Count > 0)
- {
- ;
- }
- else
- {
- Dictionary<int, string> nozzleIdGradeNameDict = Eps.NozzleMappingGradeName(); //Mapping grade name & nozzleId
- foreach (var fpId in CurrentCardReader.SupportedNozzles)
- {
- var fp = Eps.GetFuelingPoint(fpId);
- foreach (var nozzleId in fp.AssociatedNozzles)
- {
- NozzeIdGradeNameDictOnThisSide.Add(nozzleId, nozzleIdGradeNameDict[nozzleId]);
- }
- }
- }
- foreach (var item in NozzeIdGradeNameDictOnThisSide)
- {
- debugLogger.Add($"NozzleIdGradeNameDictOnThisSide: Key - {item.Key}, Value - {item.Value}");
- }
- return NozzeIdGradeNameDictOnThisSide;
- }
- #region Printer
- public bool GetPrinter()
- {
- lock (printerSyncObj)
- {
- if (printerIdle == true)
- {
- printerIdle = false;
- return true;
- }
- else
- {
- GetPrinterCount++;
- return false;
- }
- }
- }
- public void ReleasePrinter()
- {
- lock (printerSyncObj)
- {
- printerIdle = true;
- }
- }
- #endregion
- #region Cleanup
- public void Cleanup()
- {
- CurrentEpsTrx = null;
- NewEpsTrx = null;
- CurrentTrxMode = EpsTransactionMode.Unknown;
- AuthorizationId = null;
- CurrentNozzleId = Eps.InvalidNozzleId;
- GetPrinterCount = 0;
- CurrentReceipt = null;
- }
- #endregion
- }
- }
|