| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261 | using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;using NozzleLockConfiguration;using SinochemCarplateService.Models;using SinochemCloudClient.Models;using SinochemInternetPlusApp.EpsTrxCleanup;using SinochemPosClient.Models;using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Threading;using System.Xml.Serialization;using Wayne.FDCPOSLibrary;using Wayne.Lib.Log;using WayneChina_IcCardReader_SinoChem.MessageEntity;using WayneChina_IcCardReader_SinoChem.MessageEntity.Incoming;using Global_Pump_Fdc;using System.Threading.Tasks;using Wayne.Lib;namespace SinochemInternetPlusApp{    public class Eps : IDisposable    {        private List<FuelingPoint> fuelingPoints;        private Dictionary<int, IEnumerable<int>> fuelingPointNozzlesDict = new Dictionary<int, IEnumerable<int>>();        private Dictionary<int, Dictionary<int, string>> openTrxNozzleDict = new Dictionary<int, Dictionary<int, string>>();        private EpsTrxCleanupManager epsTrxCleanupManager;        protected DebugLogger debugLogger;        private int requestId;        private object requestSyncObj = new object();        #region devices and services        private Sinochem_CarPlateRecognizeCamera_HuLianWangJia.Handler carPlateServer;        private IEnumerable<WayneChina_IcCardReader_SinoChem.Handler> cardReaderHandlers;        private PumpGroupHandler globalPumpGroup;                private CloudManager cloudManager;        private PosManager posManger;        private object openTrxSyncObj = new object();        private Dictionary<int, byte> fpSqNoDict = new Dictionary<int, byte>();        private IEnumerable<IProcessor> processors;        private IEnumerable<IFdcPumpController> pumpControllers;        #endregion        static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("SinochemEpsApp");        #region constants        public int InvalidNozzleId { get { return 9999; } }        #endregion        ForecourtTrxManager forecourtTrxManager;        private Dictionary<int, string> nozzleMappingGradeName { get; set; } = new Dictionary<int, string>();        #region Constructor        public Eps(IEnumerable<int> fpIds, Dictionary<int, IEnumerable<int>> fpNozzlesDict, IEnumerable<IProcessor> processors)        {            var identifiableEntity = new IdentifiableEntity(0, "EpsMain", "", null);            debugLogger = new DebugLogger(identifiableEntity);            fuelingPoints = new List<FuelingPoint>(fpIds.Count());            foreach (int fpId in fpIds)            {                fuelingPoints.Add(new FuelingPoint(fpId, fpNozzlesDict[fpId], this));            }            fuelingPointNozzlesDict = fpNozzlesDict;            epsTrxCleanupManager = new EpsTrxCleanupManager(this);            cloudManager = new CloudManager();            posManger = new PosManager();            NozzleLockAccessor.FillinNozzles(fpNozzlesDict);            this.processors = processors;            var pumpControllerList = new List<IFdcPumpController>();            foreach (dynamic processor in processors)            {                if (processor is IAppProcessor)                    continue;                var handler = processor.Context.Handler;                if (handler is IEnumerable<IFdcPumpController>)                {                    pumpControllerList.AddRange(handler);                }            }            pumpControllers = pumpControllerList;            debugLogger.Add("Eps has been constructed");                        logger.Info(string.Format($"{this.processors.Count()} processors were loaded."));            foreach (var proc in this.processors)            {                logger.Info(proc.GetType().ToString());            }            forecourtTrxManager = new ForecourtTrxManager(pumpControllers, GenericSinochemEpsApp.PumpSideMapping,                 GenericSinochemEpsApp.ForceMappingFusionHoseToHuiTianHoseStr,GenericSinochemEpsApp.PosDatabaseConnString,                 GenericSinochemEpsApp.RawProductNameToPosProductNameStr);            forecourtTrxManager.Init();            nozzleMappingGradeName = forecourtTrxManager.GetNozzleFuelMapping();        }        internal TrxNotificationResponse NotifySuccessfulTrxToPos(EpsTransactionModel model, DebugLogger debugLogger)        {            return posManger.NotifyPosSuccessfulTrx(model, debugLogger);        }        internal PaymentResponse SendPaymentToCloud(EpsTransactionModel currentEpsTrxModel, DebugLogger debugLogger)        {            return cloudManager.Payment(currentEpsTrxModel, debugLogger);        }        internal BalanceInquiryResponse SendBalanceInquiryToCloud(string cardNo, string encryptedPin, string tid, int nozzleId, DebugLogger debugLogger)        {            return cloudManager.BalanceInquiry(cardNo, encryptedPin, tid, nozzleId, debugLogger);        }        #endregion        public void Run()        {             fpSqNoDict.Clear();            SetupICCardReaderHandler();            if (fuelingPoints != null)            {                foreach (var fp in fuelingPoints)                {                    if (!fpSqNoDict.ContainsKey(fp.FuelingPointId))                    {                        fpSqNoDict.Add(fp.FuelingPointId, 0);                    }                    fp.Start();                }            }            epsTrxCleanupManager.Start();            SetupFccClient();            SetupCarplateServer();        }        private void SetupFccClient()        {            foreach (var pumpController in pumpControllers)            {                pumpController.OnStateChange += (s, a) =>                {                    var pump = s as IFdcPumpController;                    if (a.NewPumpState == LogicalDeviceState.FDC_CALLING)                    {                        logger.Info($"Pump {pump.PumpId} at state CALLING");                        int sitewiseNozzleId =                            SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(pump.PumpId, a.StateChangedNozzles.FirstOrDefault()?.LogicalId ?? 0);                        FccClient_NozzleLifted(sitewiseNozzleId, pump);                    }                    else if (a.NewPumpState == LogicalDeviceState.FDC_READY)                    {                        int sitewiseNozzleId = 0;                        if (a.StateChangedNozzles != null)                        {                            SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(pump.PumpId, a.StateChangedNozzles.FirstOrDefault()?.LogicalId ?? 0);                        }                        else                        {                            logger.Info("StateChangeNozzles null, use sitewiseNozzleId 0");                        }                        logger.Info($"Pump {pump.PumpId} at state: FDC_READY, siteWiseNozzleId: {sitewiseNozzleId}");                        FccClient_NozzleReplaced(sitewiseNozzleId, pump.PumpId);                    }                    else if (a.NewPumpState == LogicalDeviceState.FDC_AUTHORISED)                    {                        int sitewiseNozzleId =                            SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(pump.PumpId,                                a.StateChangedNozzles.FirstOrDefault()?.LogicalId ?? 0);                        logger.Info($"Pump {pump.PumpId} at state FDC_AUTHORISED, siteWiseNozzleId: {sitewiseNozzleId}");                        FccClient_AuthOk(sitewiseNozzleId, 1999);                    }                    else if (a.NewPumpState == LogicalDeviceState.FDC_FUELLING)                    {                        logger.Info($"Pump {pump.PumpId} is fueling");                    }                };                pumpController.OnCurrentFuellingStatusChange += (s, a) =>                {                    var pump = s as IFdcPumpController;                    if (a.Transaction.Finished)                    {                        int sitewiseNozzleId =                               SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(pump.PumpId, a.Transaction.Nozzle.LogicalId);                        logger.Info($"Pump {pump.PumpId} fueling finished, siteWiseNozzleId: {sitewiseNozzleId}, amount: {a.Transaction.Amount}");                        FccClient_NozzleReplaced(sitewiseNozzleId, pump.PumpId);                        FccClient_FuelingDone(sitewiseNozzleId, a.Transaction.SequenceNumberGeneratedOnPhysicalPump,                             Convert.ToDecimal(a.Transaction.Amount / Math.Pow(10, pump.VolumeDecimalDigits)),                            Convert.ToDecimal(a.Transaction.Volumn / Math.Pow(10, pump.VolumeDecimalDigits)),                            1999);                    }                };                Console.WriteLine($"setup fcc client pump done for {pumpController.PumpId}");            }        }        private void Pump_OnStateChange(object sender, FdcPumpControllerOnStateChangeEventArg e)        {            throw new NotImplementedException();        }        private void Pump_OnCurrentFuellingStatusChange(object sender, FdcTransactionDoneEventArg e)        {            throw new NotImplementedException();        }                private void SetupCarplateServer()        {            var carPlateProcessors =                 processors.OfType<Edge.Core.Processor.GenericDeviceProcessor<System.String, Edge.Core.Parser.HttpMessageParser.BaseHttpMessage<System.String>>>();            carPlateServer = carPlateProcessors.Select(proc => (Sinochem_CarPlateRecognizeCamera_HuLianWangJia.Handler)proc.Context.Handler)                            .FirstOrDefault();            if (carPlateServer != null)            {                carPlateServer.NewCarPlateScanned += CarPlateServer_NewCarPlateScanned;                carPlateServer.QRCodePaid += CarPlateServer_QRCodePaid;                carPlateServer.IndoorPaid += CarPlateServer_IndoorPaid;                carPlateServer.OnMessageReceivedViaFdc += (msg) =>                {                    logger.Info($"fdc message:\n {msg}");                    if (msg.Length > 10 && msg.StartsWith("<Tran"))                    {                        var serializer = new XmlSerializer(typeof(TransactionOperation));                        using (var reader = new StringReader(msg))                        {                            var trxOpRequest = (TransactionOperation)serializer.Deserialize(reader);                            if (trxOpRequest != null)                            {                                                                                                if (!string.IsNullOrEmpty(trxOpRequest.NozzleNo))                                {                                    int nozzleNo = int.Parse(trxOpRequest.NozzleNo);                                    FuelingPoint fp = GetFp(nozzleNo);                                    if (fp != null)                                        fp.SignalTrxOpRequest(trxOpRequest);                                }                                else                                {                                    foreach (FuelingPoint fp in fuelingPoints)                                    {                                        if (fp.SignalTrxOpRequest(trxOpRequest))                                            break;                                    }                                }                            }                        }                    }                    else                    {                        var serializer = new XmlSerializer(typeof(DisplayResponse));                        using (var reader = new StringReader(msg))                        {                            var displayResp = (DisplayResponse)serializer.Deserialize(reader);                            logger.Info($"Display Response, id = {displayResp.RequestId}");                            if (displayResp != null && displayResp.OverallResult == ResultType.Success)                            {                                foreach (var fp in fuelingPoints)                                {                                    if (fp.ContainsRequestId(displayResp.RequestId))                                        fp.SignalDisplayResponseReceived(displayResp);                                }                            }                        }                    }                    return new Tuple<string, OverallResult>("DisplayResponse ACK", OverallResult.Success);                };            }            else            {                Console.WriteLine("carPlateServer not found!!!");                debugLogger.Add("carPlateServer not found!!!");            }        }                                        private void CarPlateServer_NewCarPlateScanned(CarPlateTrxRequest request)        {            int sitewiseNozzleId = InvalidNozzleId;            try            {                sitewiseNozzleId = int.Parse(request.gun);            }            catch(Exception ex)            {                logger.Error(ex.ToString());            }            if (IsNozzleConfiguredOpen(sitewiseNozzleId))            {                var fp = GetFp(sitewiseNozzleId);                fp?.SignalNewCarplate(request);            }            else            {                debugLogger.Add                    (string.Format($"Physical nozzle# {sitewiseNozzleId} is closed, so we ignore the car plate event!!!"));            }        }        private void CarPlateServer_QRCodePaid(QRCodePayResultRequest request)        {             logger.Info("CarPlateServer_QRCodePaid");            ThreadPool.QueueUserWorkItem(o =>            {                if (request != null)                {                    logger.Info($"CarPlateServer_QRCodePaid, ttc:{request.ttc}, nozzleId:{request.nozzleId}, status: {request.status}, openId:{request.openId}");                    EpsTransactionModel epsTrxModel = EpsTransactionQuery.GetEpsTrxByTTC(request.ttc, request.nozzleId);                    if (epsTrxModel != null && epsTrxModel.trx_status != EpsTrxStatus.PaymentOk)                    {                        EpsTransaction epsTrx = EpsTransaction.RestroeEpsTrxFrom(epsTrxModel);                        if (request.status == "1")                        {                            EpsTransactionQuery.UpdateEpsTrxByttc(request.ttc, request.nozzleId, request.amount, request.openId, "07_pos", EpsTrxStatus.PaymentOk);                                                        EpsTransactionQuery.DeleteXiaoFei2(epsTrxModel.liushuino, request.nozzleId);                            MultiFusionsSupport.DeleteXiaofei2FromTargetFusion(request.nozzleId, epsTrxModel.liushuino, debugLogger);                                                        TrxNotificationResponse response = NotifySuccessfulTrxToPos(EpsTransactionQuery.RefreshEpsTrx(epsTrx.Model.id), debugLogger);                            if (response != null && response.IsSuccessful())                            {                                epsTrx.UpdateNotifyPosFlagToDb(NotifyPosFlag.NotifyOk);                            }                        }                        else                        {                            epsTrx.UpdateTrxStatusToDb(EpsTrxStatus.PaymentFailed);                        }                                                BroadCastTrxCompleteToBigScreen(request.nozzleId);                    }                }            });        }        private void CarPlateServer_IndoorPaid(IndoorPayResultRequest request)        {            logger.Info("CarPlateServer_IndoorPaid");            ThreadPool.QueueUserWorkItem(o =>            {                if (request != null)                {                    logger.Info($"CarPlateServer_IndoorPaid, ttc:{request.liushuino}, nozzleId:{request.nozzleId}");                    EpsTransactionModel epsTrxModel = EpsTransactionQuery.GetEpsTrxByLiuShuiNO(request.liushuino, request.nozzleId);                    if (epsTrxModel != null)                    {                        EpsTransactionQuery.UpdateEpsTrxByliushuino(request.liushuino, request.nozzleId, request.amount, EpsTrxStatus.PaymentOk);                        BroadCastTrxCompleteToBigScreen(request.nozzleId);                    }                }            });        }        private void BroadCastTrxCompleteToBigScreen(int nozzleId)        {                        var fp = GetFp(nozzleId);            var validTrx = EpsTransactionQuery.GetValidCarPlateEpsTrxModels(                                fp.GetAllNozzlesOnThisSide(),                                ConfigurationValues.AlreadyDoneEpsTrxCountPerDisplay,                                fp.DebugLogger);            var availableNozzleInfo = fp.GetAvailableNozzleInfo();            if (availableNozzleInfo != null)            {                foreach (var trx in validTrx)                {                    if (trx.trx_status == EpsTrxStatus.BeforeFueling || trx.trx_status == EpsTrxStatus.Fueling)                    {                        trx.AvailableNozzleGrade = availableNozzleInfo;                    }                }            }            var text = fp.Eps.CreateDisplayTrxCommand(validTrx, out requestId, 0);            if (fp.Eps.CarPlateHandler != null)            {                fp.Eps.CarPlateHandler.BroadcastMessageViaFdc(text);            }        }                                public Dictionary<int, string> NozzleMappingGradeName()        {            return nozzleMappingGradeName;         }        public PumpState GetFPState(int fpId)        {            foreach (var pump in pumpControllers)            {                if (pump.PumpId == fpId)                {                    var state = pump.QueryStatusAsync().Result;                    if (state == LogicalDeviceState.FDC_CALLING)                        return PumpState.Calling;                    else if (state == LogicalDeviceState.FDC_AUTHORISED)                        return PumpState.Authorized;                    else if (state == LogicalDeviceState.FDC_FUELLING)                        return PumpState.Fuelling;                    else if (state == LogicalDeviceState.FDC_READY)                        return PumpState.Idle;                    else if (state == LogicalDeviceState.FDC_CLOSED)                        return PumpState.Closed;                }            }            return PumpState.Unknown;        }        private void SetupICCardReaderHandler()        {            var icCardReaderProcessors =                 processors.OfType<GenericDeviceProcessor<System.Byte[], WayneChina_IcCardReader_SinoChem.MessageEntity.IcCardReaderMessageBase>>();            cardReaderHandlers = icCardReaderProcessors.Select(proc => (WayneChina_IcCardReader_SinoChem.Handler)proc.Context.Handler);            if (cardReaderHandlers != null && cardReaderHandlers.Count() > 0)            {                Console.WriteLine($"Card reader handlers count: {cardReaderHandlers.Count()} ");                foreach (var handler in cardReaderHandlers)                {                    handler.OnCardReaderMessageReceived += Handler_OnCardReaderMessageReceived;                }            }            else            {                Console.WriteLine("card reader handlers not found!!");                logger.Info("card reader handlers not found!!");            }        }        private void Handler_OnCardReaderMessageReceived(object sender, WayneChina_IcCardReader_SinoChem.CardReaderMessageEventArgs e)        {                        var latestSqNo = GetCurrentSqNoForFp(e.FuelingPointId);                        if (latestSqNo.HasValue)            {                var currentSqNo = e.CardReaderMessage.MessageSeqNumber;                if (latestSqNo.Value != currentSqNo || currentSqNo == 0)                {                                        fpSqNoDict[e.FuelingPointId] = currentSqNo;                    var targetFP = fuelingPoints.First(_ => _.FuelingPointId == e.FuelingPointId);                    if (e.CardReaderMessage is ACK)                    {                        targetFP?.SingalAckReceived((ACK)e.CardReaderMessage);                    }                    else if (e.CardReaderMessage is SignDataResponse)                    {                        targetFP?.SignalSignedDataArrived((SignDataResponse)e.CardReaderMessage);                    }                    else if (e.CardReaderMessage is CardReaderStateEvent)                    {                        targetFP?.SingalCardReaderStateEvent((CardReaderStateEvent)e.CardReaderMessage);                    }                    else if (e.CardReaderMessage is CardExternalCheckErrorRequest)                    {                        targetFP?.SignalCardExternalCheckFailure((CardExternalCheckErrorRequest)e.CardReaderMessage);                    }                    else if (e.CardReaderMessage is CardOnlineVerificationRequest)                    {                        targetFP?.SignalCardOnlineVerification((CardOnlineVerificationRequest)e.CardReaderMessage);                    }                    else if (e.CardReaderMessage is HeartBeat)                    {                        targetFP?.SignalCardReaderHeartbeat((HeartBeat)e.CardReaderMessage);                    }                }                                                else                {                                        if (e.CardReaderMessage is HeartBeat)                    {                        var targetFP = fuelingPoints.First(_ => _.FuelingPointId == e.FuelingPointId);                        targetFP?.SignalCardReaderHeartbeat((HeartBeat)e.CardReaderMessage);                    }                    else                    {                        logger.Info($"A repeated message from terminal: {e}");                        var targetFP = fuelingPoints.First(_ => _.FuelingPointId == e.FuelingPointId);                        targetFP?.SignalRepeatedMessageRecieved(e.CardReaderMessage);                        return;                    }                }            }        }        private byte? GetCurrentSqNoForFp(int fpId)        {            if (fpSqNoDict.ContainsKey(fpId))            {                return fpSqNoDict[fpId];            }            else            {                return null;            }        }        #region Cloud interactions        internal RefundResponse SendRefundToCloud(EpsTransactionModel trxModel, DebugLogger debugLogger)        {            return cloudManager.Refund(trxModel, debugLogger);        }        internal TrxStatusInquiryResponse SendTrxQueryToCloud(EpsTransactionModel trxModel, DebugLogger debugLogger)        {            return cloudManager.TrxStatusInquiry(trxModel, debugLogger);        }        #endregion        #region FDC client interactions        private void FccClient_NozzleReplaced(int sitewiseNozzleId, int pumpId)        {            if (sitewiseNozzleId != 0)            {                var fp = GetFp(sitewiseNozzleId);                fp?.SignalNozzleReplaced(sitewiseNozzleId, pumpId);            }            else            {                var fp = GetFuelingPoint(pumpId);                fp?.SignalNozzleReplaced(sitewiseNozzleId, pumpId);            }        }        private void FccClient_AuthFailed(int sitewiseNozzleId)        {            var fp = GetFp(sitewiseNozzleId);            fp?.SignalAuthFailed();        }        private void FccClient_AuthOk(int sitewiseNozzleId, long? authId)        {            var fp = GetFp(sitewiseNozzleId);            fp?.SignalAuthOk(authId);        }        private void FccClient_NozzleLifted(int sitewiseNozzleId, IFdcPumpController callingPump)        {            logger.Info($"SiteNozzle number: {sitewiseNozzleId}, from Pump {callingPump.PumpId} lifted");            if (IsNozzleConfiguredOpen(sitewiseNozzleId))            {                var fp = GetFp(sitewiseNozzleId);                fp?.SignalNozzleLifted(callingPump, sitewiseNozzleId);            }            else            {                logger.Info(string.Format($"Physical nozzle# {sitewiseNozzleId} is closed, so we ignore the nozzle lift event!!!"));            }        }        private bool IsNozzleConfiguredOpen(int sitewiseNozzleId)        {            return !NozzleLockAccessor.IsNozzleClosedInTermsOfSale(sitewiseNozzleId);        }        private void FccClient_FuelingDone(int sitewiseNozzleId, int seqNum, decimal fuelAmount, decimal fuelingQty, long authId)        {            var fp = GetFp(sitewiseNozzleId);            fp?.SignalFuelingDone(sitewiseNozzleId, seqNum, fuelAmount, fuelingQty, authId);        }        public void AuthorizePumpAsync(IFdcPumpController callingPump, int sitewiseNozzleId, decimal authAmount)        {                        Task.Run(() =>            {                LogicalNozzle logicalNozzle = callingPump.Nozzles.First();                foreach (var nozzle in callingPump.Nozzles)                {                    if (SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(callingPump.PumpId, Convert.ToInt32(nozzle.LogicalId)) == sitewiseNozzleId)                    {                        logicalNozzle = nozzle;                    }                }                callingPump.AuthorizeWithAmountAsync((int)((double)authAmount * Math.Pow(10, callingPump.AmountDecimalDigits)), logicalNozzle.LogicalId);            });        }        #endregion        private void TeardownFccClient()        {                                                                                                        }        public void Shutdown()        {            TeardownCarplateServer();            TeardownCardReaderHandler();            if (fuelingPoints != null)            {                foreach (var fp in fuelingPoints)                {                    fp.SignalShutdown();                }            }        }        public WayneChina_IcCardReader_SinoChem.Handler GetHandler(int fuelingPointId)        {                        foreach (var handler in cardReaderHandlers)            {                if (handler.SupportedNozzles.Contains(fuelingPointId))                    return handler;            }            return null;        }        private void TeardownCarplateServer()        {            if (carPlateServer != null)            {                carPlateServer.NewCarPlateScanned -= CarPlateServer_NewCarPlateScanned;                carPlateServer.QRCodePaid -= CarPlateServer_QRCodePaid;                carPlateServer.IndoorPaid -= CarPlateServer_IndoorPaid;            }        }        private void TeardownCardReaderHandler()        {            if (cardReaderHandlers != null)            {                foreach (var handler in cardReaderHandlers)                {                    handler.OnCardReaderMessageReceived -= Handler_OnCardReaderMessageReceived;                }            }        }        public Sinochem_CarPlateRecognizeCamera_HuLianWangJia.Handler CarPlateHandler => carPlateServer;        public WayneChina_IcCardReader_SinoChem.Handler GetCardReader(int nozzleId)        {            return cardReaderHandlers.FirstOrDefault(h => h.SupportedNozzles.Contains(nozzleId));        }        public FuelingPoint GetFp(int nozzleId)        {            foreach (var fp in fuelingPoints)            {                if (fp.AssociatedNozzles != null && fp.AssociatedNozzles.Contains(nozzleId))                    return fp;            }            return null;        }        public FuelingPoint GetFuelingPoint(int fpId)        {            return fuelingPoints?.FirstOrDefault(_ => fpId == _.FuelingPointId);        }        public int GetNextRequestId()        {            lock (requestSyncObj)            {                if (requestId == int.MaxValue)                {                    requestId = 0;                }                return requestId++;            }        }                                                                public string CreateDisplayTrxCommand(IEnumerable<EpsTransactionModel> epsTrxList, out int requestId, int i)        {            Display display;            string cmdText = "";            XmlSerializer serializer = new XmlSerializer(typeof(Display));            MemoryStream ms;            StreamReader sr;            display = new Display();            display.ScreenType = ScreenType.ShowTrxList;            display.RequestId = GetNextRequestId();            display.TimeoutSpecified = true;            display.Timeout = 300;            requestId = display.RequestId;            display.PumpInfo = new DisplayPumpInfo            {                Id = 1,                NozzleId = 1            };            int count = epsTrxList.Count();            display.TrxList = new DisplayTrx[epsTrxList.Count()];            for (int index = 0; index < count; index++)            {                display.TrxList[index] = ConvertEpsTrxModelToDisplayTrx(epsTrxList.ToArray()[index]);            }            ms = new MemoryStream();            serializer.Serialize(ms, display);            ms.Position = 0;            sr = new StreamReader(ms, true);            cmdText = sr.ReadToEnd();            ms.Close();            sr.Close();            return cmdText;        }                       public DisplayTrx ConvertEpsTrxModelToDisplayTrx(EpsTransactionModel epsTrxModel)        {            TrxStatus state = TrxStatus.ReadyForFillingStart;            if (epsTrxModel.trx_status == EpsTrxStatus.BeforeFueling)                state = TrxStatus.ReadyForFillingStart;            else if (epsTrxModel.trx_status == EpsTrxStatus.BeforePayment)                state = TrxStatus.PendingForPayment;            else if (epsTrxModel.trx_status == EpsTrxStatus.Fueling)                state = TrxStatus.FillingOngoing;            else if (epsTrxModel.trx_status == EpsTrxStatus.FuelingDone)                state = TrxStatus.PendingForPayment;            else if (epsTrxModel.trx_status == EpsTrxStatus.PaymentOk)                state = TrxStatus.Success;            else if (epsTrxModel.trx_status == EpsTrxStatus.PaymentFailed)                state = TrxStatus.Failed;            else if (epsTrxModel.trx_status == EpsTrxStatus.PaymentNeedConfirm)                state = TrxStatus.Failed;            else if (epsTrxModel.trx_status == EpsTrxStatus.PaymentOkButNeedRefund)                state = TrxStatus.Failed;            else if (epsTrxModel.trx_status == EpsTrxStatus.PaymentRefunded)                state = TrxStatus.Failed;            var displayTrx = new DisplayTrx            {                Id = epsTrxModel.id.ToString(),                State = state,                TimeStamp = new DisplayTrxTimeStamp                {                    StartTime = epsTrxModel.created_time,                    FinishTime = string.IsNullOrEmpty(epsTrxModel.xf_time) ?                                epsTrxModel.created_time :                                 Utilities.CombineDateAndTime(epsTrxModel.xf_date, epsTrxModel.xf_time)                },                MemberInfo = new DisplayTrxMemberInfo                {                    Id = epsTrxModel.cardNo_masked,                    LicensePlateNo = epsTrxModel.car_number                },                FillingInfo = new DisplayTrxFillingInfo                {                    PumpId = 1,                    NozzleId = epsTrxModel.jihao,                    ProductNo = 1,                    UnitPrice = (decimal)epsTrxModel.danjia,                    ProductType = epsTrxModel.youpin,                    ProductDiscription = "汽油",                    Amount = (decimal)epsTrxModel.amount,                    AmountPaid = (decimal)epsTrxModel.real_pay_amount,                    Volume = (decimal)epsTrxModel.qty,                    NozzleSelected = epsTrxModel.nozzleSelected,                }            };            if (epsTrxModel.AvailableNozzleGrade != null)            {                displayTrx.NozzleList = new DisplayTrxNozzle[epsTrxModel.AvailableNozzleGrade.Count];                int index = 0;                foreach (var pair in epsTrxModel.AvailableNozzleGrade)                {                    displayTrx.NozzleList[index] = new DisplayTrxNozzle                    {                        NozzleId = pair.Key,                        Prompt = pair.Key.ToString() + "号枪" +pair.Value,                    };                    index++;                }            }            return displayTrx;        }        public string CreateDisplayCommand(ScreenType screenType)        {            Display display;            string cmdText = "";            XmlSerializer serializer = new XmlSerializer(typeof(Display));            MemoryStream ms;            StreamReader sr;            switch (screenType)            {                case ScreenType.Idle:                                        display = new Display();                    display.ScreenType = screenType;                    display.CompanyContactInfo = new DisplayCompanyContactInfo                    {                        Tel = "010-59569575",                        Address = "世界500强企业\n中国第四大国家石油公司"                    };                    display.StationInfo = new DisplayStationInfo                    {                        StationNo = "000001",                        StationName = "沈阳望花中街加油加气站"                    };                    display.PumpInfo = new DisplayPumpInfo                    {                        Id = 1,                        NozzleId = 1                    };                    ms = new MemoryStream();                    serializer.Serialize(ms, display);                    ms.Position = 0;                    sr = new StreamReader(ms, true);                    cmdText = sr.ReadToEnd();                    break;                case ScreenType.Welcome:                    display = new Display();                    display.ScreenType = screenType;                    display.CompanyContactInfo = new DisplayCompanyContactInfo                    {                        Tel = "010-59569575",                        Address = "世界500强企业\n中国第四大国家石油公司"                    };                    display.MemberInfo = new DisplayMemberInfo                    {                        LicensePlateNo = "京A88888",                        Id = "1234567"                    };                    display.PumpInfo = new DisplayPumpInfo                    {                        Id = 1,                        NozzleId = 1                    };                                        ms = new MemoryStream();                    serializer.Serialize(ms, display);                    ms.Position = 0;                    sr = new StreamReader(ms, true);                    cmdText = sr.ReadToEnd();                    break;                case ScreenType.ShowTrxList:                    display = new Display();                    display.ScreenType = screenType;                    display.PumpInfo = new DisplayPumpInfo                    {                        Id = 1,                        NozzleId = 1                    };                    display.TrxList = new DisplayTrx[]                    {                        new DisplayTrx                        {                            Id = "100",                            FillingInfo = new DisplayTrxFillingInfo                            {                                Amount = 200,                                NozzleId = 1,                                ProductType = "95#",                                UnitPrice = 6.78m,                                Volume = 35,                                ProductDiscription = "汽油"                            },                            MemberInfo = new DisplayTrxMemberInfo                            {                                LicensePlateNo = "京A88888",                                Id = "1234567"                            },                            State = TrxStatus.Success,                            TimeStamp = new DisplayTrxTimeStamp                            {                                StartTime = DateTime.Now,                                FinishTime =DateTime.Now                            }                        },                        new DisplayTrx                        {                            Id = "101",                            FillingInfo = new DisplayTrxFillingInfo                            {                                Amount = 211,                                NozzleId = 1,                                ProductType = "95#",                                UnitPrice = 6.78m,                                Volume = 35,                                ProductDiscription = "汽油"                            },                            MemberInfo = new DisplayTrxMemberInfo                            {                                LicensePlateNo = "京A88888",                                Id = "1234567"                            },                            State = TrxStatus.Success,                            TimeStamp = new DisplayTrxTimeStamp                            {                                StartTime = DateTime.Now,                                FinishTime =DateTime.Now                            }                        },                        new DisplayTrx                        {                            Id = "102",                            FillingInfo = new DisplayTrxFillingInfo                            {                                Amount = 222,                                NozzleId = 1,                                ProductType = "95#",                                UnitPrice = 6.78m,                                Volume = 35,                                ProductDiscription = "汽油"                            },                            MemberInfo = new DisplayTrxMemberInfo                            {                                LicensePlateNo = "京A88888",                                Id = "1234567"                            },                            State = TrxStatus.Success,                            TimeStamp = new DisplayTrxTimeStamp                            {                                StartTime = DateTime.Now,                                FinishTime =DateTime.Now                            }                        },                        new DisplayTrx                        {                            Id = "103",                            FillingInfo = new DisplayTrxFillingInfo                            {                                Amount = 233,                                NozzleId = 1,                                ProductType = "95#",                                UnitPrice = 6.78m,                                Volume = 35,                                ProductDiscription = "汽油"                            },                            MemberInfo = new DisplayTrxMemberInfo                            {                                LicensePlateNo = "京A88888",                                Id = "1234567"                            },                            State = TrxStatus.Success,                            TimeStamp = new DisplayTrxTimeStamp                            {                                StartTime = DateTime.Now,                                FinishTime =DateTime.Now                            }                        },                        new DisplayTrx                        {                            Id = "104",                            FillingInfo = new DisplayTrxFillingInfo                            {                                Amount = 244,                                NozzleId = 1,                                ProductType = "95#",                                UnitPrice = 6.78m,                                Volume = 35,                                ProductDiscription = "汽油"                            },                            MemberInfo = new DisplayTrxMemberInfo                            {                                LicensePlateNo = "京A88888",                                Id = "1234567"                            },                            State = TrxStatus.Success,                            TimeStamp = new DisplayTrxTimeStamp                            {                                StartTime = DateTime.Now,                                FinishTime =DateTime.Now                            }                        }                    };                    ms = new MemoryStream();                    serializer.Serialize(ms, display);                    ms.Position = 0;                    sr = new StreamReader(ms, true);                    cmdText = sr.ReadToEnd();                    break;                case ScreenType.TrxResult:                    display = new Display();                    display.ScreenType = screenType;                    display.PumpInfo = new DisplayPumpInfo                    {                        Id = 1,                        NozzleId = 1                    };                    display.TrxList = new DisplayTrx[]                    {                        new DisplayTrx                        {                            Id = "100",                            FillingInfo = new DisplayTrxFillingInfo                            {                                Amount = 200,                                NozzleId = 1,                                ProductType = "95#",                                UnitPrice = 6.78m,                                Volume = 35,                                ProductDiscription = "汽油"                            },                            MemberInfo = new DisplayTrxMemberInfo                            {                                LicensePlateNo = "京A88888",                                Id = "1234567"                            },                            State = TrxStatus.Success,                            TimeStamp = new DisplayTrxTimeStamp                            {                                StartTime = DateTime.Now,                                FinishTime =DateTime.Now                            }                        }                    };                    ms = new MemoryStream();                    serializer.Serialize(ms, display);                    ms.Position = 0;                    sr = new StreamReader(ms, true);                    cmdText = sr.ReadToEnd();                    break;                default:                    throw new Exception("Unsupported screen type");            }            return cmdText;        }        #region IDisposable Support        private bool disposedValue = false;         protected virtual void Dispose(bool disposing)        {            if (!disposedValue)            {                if (disposing)                {                                        if (fuelingPoints != null)                    {                        foreach (var fp in fuelingPoints)                        {                            fp.Dispose();                        }                    }                    if(epsTrxCleanupManager != null)                    {                        epsTrxCleanupManager.Dispose();                    }                    if(debugLogger != null)                    {                        debugLogger.Dispose();                    }                }                                                disposedValue = true;            }        }                                                        public void Dispose()        {                        Dispose(true);                                }        #endregion    }    public enum PumpState    {                                Closed,                                Inoperative,                                Idle,                                Calling,                                Authorized,                                Fuelling,                                Suspended,                                Unknown,                                Error,                                Offline,                                Starting,                                Stopped,    }}
 |