| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697 | using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;using Dfs.WayneChina.HengshanPayTerminal.MessageEntity;using Dfs.WayneChina.HengshanPayTerminal.MessageEntity.Incoming;using Dfs.WayneChina.HengshanPayTerminal.Support;using System;using System.Collections;using System.Collections.Concurrent;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using Wayne.FDCPOSLibrary;using Edge.Core.Processor.Dispatcher.Attributes;using System.Text.RegularExpressions;using System.Text.Json;namespace Dfs.WayneChina.HengshanPayTerminal{                [MetaPartsDescriptor(        "lang-zh-cn:恒山IC卡终端(UI板) Applang-en-us:Hengshan IC card terminal (UI Board)",        "lang-zh-cn:用于与UI板通讯控制加油机" +        "lang-en-us:Used for terminal communication to control pumps",        new[]         {             "lang-zh-cn:恒山IC卡终端lang-en-us:HengshanICTerminal"        })]    public class HengshanPayTermHandler : IEnumerable<IFdcPumpController>, IDeviceHandler<byte[], CardMessageBase>    {        #region Fields        private string pumpIds;        private string pumpSubAddresses;        private string pumpNozzles;        private string pumpSiteNozzleNos;        private string nozzleLogicIds;        private IContext<byte[], CardMessageBase> _context;        private List<HengshanPumpHandler> pumpHandlers = new List<HengshanPumpHandler>();        public Queue<CardMessageBase> queue = new Queue<CardMessageBase>();        private object syncObj = new object();        private ConcurrentDictionary<int, PumpStateHolder> statusDict = new ConcurrentDictionary<int, PumpStateHolder>();        public ConcurrentDictionary<int, PumpStateHolder> PumpStatusDict => statusDict;        private Dictionary<int, int> pumpIdSubAddressDict;        public Dictionary<int, List<int>> PumpNozzlesDict { get; private set; }        public Dictionary<int, int> NozzleLogicIdDict { get; private set; }        public Dictionary<int, List<int>> PumpSiteNozzleNoDict { get; private set; }        #endregion        #region Logger        private static NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("IPosPlusApp");        #endregion        #region Constructor                                                                                                                                                                [ParamsJsonSchemas("TermHandlerGroupCtorParamsJsonSchemas")]        public HengshanPayTermHandler(HengshanPayTerminalHanlderGroupConfigV2 config)            : this(config.PumpIds,                  string.Join(";", config.PumpSubAddresses.Select(m => $"{m.PumpId}={m.SubAddress}")),                  string.Join(";", config.PumpNozzleLogicIds.Select(m => $"{m.PumpId}={m.LogicIds}")),                  string.Join(";", config.PumpSiteNozzleNos.Select(m => $"{m.PumpId}={m.SiteNozzleNos}")),                  string.Join(";", config.NozzleLogicIds.Select(m => $"{m.NozzleNo}={m.LogicId}")))        {        }        public HengshanPayTermHandler(            string pumpIds,            string pumpSubAddresses,            string pumpNozzles,             string pumpSiteNozzleNos,             string nozzleLogicIds)        {            this.pumpIds = pumpIds;            this.pumpSubAddresses = pumpSubAddresses;            this.pumpNozzles = pumpNozzles;            this.pumpSiteNozzleNos = pumpSiteNozzleNos;            this.nozzleLogicIds = nozzleLogicIds;            AssociatedPumpIds = GetPumpIdList(pumpIds);            pumpIdSubAddressDict = InitializePumpSubAddressMapping();            PumpNozzlesDict = ParsePumpNozzlesList(pumpNozzles);            PumpSiteNozzleNoDict = ParsePumpSiteNozzleNoList(pumpSiteNozzleNos);            NozzleLogicIdDict = InitializeNozzleLogicIdMapping(nozzleLogicIds);            InitializePumpHandlers();        }        #endregion        public void OnFdcServerInit(Dictionary<string, object> parameters)        {            logger.Info("OnFdcServerInit called");            if (parameters.ContainsKey("LastPriceChange"))            {                                var lastPriceChanges = parameters["LastPriceChange"] as Dictionary<byte, int>;                foreach (var priceChange in lastPriceChanges)                {                }            }        }        #region Event handler        public event EventHandler<TerminalMessageEventArgs> OnTerminalMessageReceived;        public event EventHandler<TotalizerDataEventArgs> OnTotalizerReceived;        public event EventHandler<FuelPriceChangeRequestEventArgs> OnFuelPriceChangeRequested;        public event EventHandler<FuelPriceDownloadRequestedEventArgs> OnTerminalFuelPriceDownloadRequested;        public event EventHandler<CheckCommandEventArgs> OnCheckCommandReceived;        public event EventHandler<LockUnlockEventArgs> OnLockUnlockCompleted;        #endregion        #region Properties        public List<int> AssociatedPumpIds { get; private set; }        public IContext<byte[], CardMessageBase> Context        {            get { return _context; }        }        public string PumpIdList => pumpIds;                #endregion        #region Methods        public int GetSubAddressForPump(int pumpId)        {            return pumpIdSubAddressDict.First(d => d.Key == pumpId).Value;        }        private List<int> GetPumpIdList(string pumpIds)        {            var pumpIdList = new List<int>();            if (!string.IsNullOrEmpty(pumpIds) && pumpIds.Contains(','))             {                var arr = pumpIds.Split(',');                foreach (var item in arr)                {                    pumpIdList.Add(int.Parse(item));                }                return pumpIdList;            }            else if (!string.IsNullOrEmpty(pumpIds) && pumpIds.Length == 1 || pumpIds.Length == 2)             {                return new List<int> { int.Parse(pumpIds) };            }            else            {                throw new ArgumentException("Pump id list not specified!");            }        }        private Dictionary<int, int> InitializePumpSubAddressMapping()        {            var dict = new Dictionary<int, int>();            if (!string.IsNullOrEmpty(pumpSubAddresses))            {                var sequence = pumpSubAddresses.Split(';')                    .Select(s => s.Split('='))                    .Select(a => new { PumpId = int.Parse(a[0]), SubAddress = int.Parse(a[1]) });                foreach (var pair in sequence)                {                    if (!dict.ContainsKey(pair.PumpId))                    {                        dict.Add(pair.PumpId, pair.SubAddress);                    }                }                return dict;            }            else            {                throw new ArgumentException("Pump id and sub address mapping does not exist");            }        }        private Dictionary<int, List<int>> ParsePumpNozzlesList(string pumpNozzles)        {            Dictionary<int, List<int>> pumpNozzlesDict = new Dictionary<int, List<int>>();            if (!string.IsNullOrEmpty(pumpNozzles) && pumpNozzles.Contains(';'))            {                var arr = pumpNozzles.Split(';');                foreach (var subMapping in arr)                {                    var pair = new KeyValuePair<int, int>(int.Parse(subMapping.Split('=')[0]), int.Parse(subMapping.Split('=')[1]));                    Console.WriteLine($"{pair.Key}, {pair.Value}");                    if (!pumpNozzlesDict.ContainsKey(pair.Key))                    {                        pumpNozzlesDict.Add(pair.Key, new List<int> { pair.Value });                    }                    else                    {                        List<int> nozzlesForThisPump;                        pumpNozzlesDict.TryGetValue(pair.Key, out nozzlesForThisPump);                        if (nozzlesForThisPump != null && !nozzlesForThisPump.Contains(pair.Value))                        {                            nozzlesForThisPump.Add(pair.Value);                        }                    }                }            }            else if (!string.IsNullOrEmpty(pumpNozzles) && pumpNozzles.Count(c => c == '=') == 1)             {                try                {                    pumpNozzlesDict.Add(                        int.Parse(pumpNozzles.Split('=')[0]),                        new List<int> { int.Parse(pumpNozzles.Split('=')[1]) });                }                catch (Exception ex)                {                    Console.WriteLine(ex);                }            }            else            {                throw new ArgumentException("Wrong mapping between pump and its associated nozzles!");            }            return pumpNozzlesDict;        }        static Dictionary<int, List<int>> ParsePumpSiteNozzleNoList(string pumpSiteNozzleNos)        {            Dictionary<int, List<int>> pumpSiteNozzleNoDict = new Dictionary<int, List<int>>();            if (!string.IsNullOrEmpty(pumpSiteNozzleNos) && pumpSiteNozzleNos.Contains(';'))            {                var arr = pumpSiteNozzleNos.Split(';');                foreach (var subMapping in arr)                {                    var pair = new KeyValuePair<int, List<int>>(                        int.Parse(subMapping.Split('=')[0]), subMapping.Split('=')[1].Split(',').Select(a => int.Parse(a)).ToList());                    Console.WriteLine($"{pair.Key}, {pair.Value}");                    if (!pumpSiteNozzleNoDict.ContainsKey(pair.Key))                    {                        pumpSiteNozzleNoDict.Add(pair.Key, pair.Value);                    }                }            }            else if (!string.IsNullOrEmpty(pumpSiteNozzleNos) && pumpSiteNozzleNos.Count(c => c == '=') == 1)            {                try                {                    string[] strArr = pumpSiteNozzleNos.Split('=');                    pumpSiteNozzleNoDict.Add(                        int.Parse(strArr[0]), new List<int> { int.Parse(strArr[1]) });                }                catch (Exception ex)                {                    Console.WriteLine(ex);                }            }            else            {                throw new ArgumentException("Wrong mapping between pump and its associated nozzles!");            }            return pumpSiteNozzleNoDict;        }        private Dictionary<int, int> InitializeNozzleLogicIdMapping(string nozzleLogicIds)        {            var dict = new Dictionary<int, int>();            if (!string.IsNullOrEmpty(nozzleLogicIds))            {                var sequence = nozzleLogicIds.Split(';')                    .Select(s => s.Split('='))                    .Select(a => new { NozzleNo = int.Parse(a[0]), LogicId = int.Parse(a[1]) });                foreach (var pair in sequence)                {                    if (!dict.ContainsKey(pair.NozzleNo))                    {                        Console.WriteLine($"nozzle, logic id: {pair.NozzleNo} - {pair.LogicId}");                        dict.Add(pair.NozzleNo, pair.LogicId);                    }                }                return dict;            }            else if (!string.IsNullOrEmpty(nozzleLogicIds) && nozzleLogicIds.Count(c => c == '=') == 1)            {                try                {                    string[] sequence = nozzleLogicIds.Split('=');                    dict.Add(int.Parse(sequence[0]), int.Parse(sequence[1]));                }                catch (Exception ex)                {                    Console.WriteLine(ex);                }                return dict;            }            else            {                throw new ArgumentException("Pump id and sub address mapping does not exist");            }        }        private void InitializePumpHandlers()        {            var pumpIdList = GetPumpIdList(pumpIds);            foreach (var item in pumpIdList)            {                var nozzleList = GetNozzleListForPump(item);                var siteNozzleNoList = PumpSiteNozzleNoDict[item];                HengshanPumpHandler pumpHandler = new HengshanPumpHandler(this, $"Pump_{item}", item, nozzleList, siteNozzleNoList);                pumpHandler.OnFuelPriceChangeRequested += PumpHandler_OnFuelPriceChangeRequested;                pumpHandlers.Add(pumpHandler);            }        }        private List<int> GetNozzleListForPump(int pumpId)        {            List<int> nozzles;            PumpNozzlesDict.TryGetValue(pumpId, out nozzles);            return nozzles;        }        private void PumpHandler_OnFuelPriceChangeRequested(object sender, FuelPriceChangeRequestEventArgs e)        {            InfoLog($"Change price, Pump {e.PumpId}, Nozzle {e.NozzleId}, Price {e.Price}");            OnFuelPriceChangeRequested?.Invoke(sender, e);        }        IEnumerator<IFdcPumpController> IEnumerable<IFdcPumpController>.GetEnumerator()        {            return pumpHandlers.GetEnumerator();        }        #endregion        #region IHandler implementation        public void Init(IContext<byte[], CardMessageBase> context)        {            CommIdentity = context.Processor.Communicator.Identity;            _context = context;        }        public string CommIdentity { get; private set; }        public async Task Process(IContext<byte[], CardMessageBase> context)        {            if (context.Incoming.Message != null && context.Incoming.Message.Handle == (byte)Command.VolumeTotalizerResult)            {                var volumeTotalResult = context.Incoming.Message as VolumeTotal;                if (volumeTotalResult != null)                {                    byte nozzleNo = volumeTotalResult.VolumeTotalizers.First().NozzleNo;                    var volumeTotal = volumeTotalResult.VolumeTotalizers.                        First(n => AssociatedPumpIds.Contains(n.NozzleNo)).VolumeTotal.ToUInt32();                    var result = new Tuple<int, int>(-1, Convert.ToInt32(volumeTotal));                    OnTotalizerReceived?.Invoke(this, new TotalizerDataEventArgs(nozzleNo, result));                }            }            else if (context.Incoming.Message != null && context.Incoming.Message.Handle == (byte)Command.ReturnTotalizer)            {                var totalizerResult = context.Incoming.Message as Totalizer;                if (totalizerResult != null)                {                    foreach (var item in totalizerResult.TotalizerData)                    {                                                var nozzleNo = item.NozzleNo;                                                                        var volumeTotal = item.VolumeTotal.ToUInt32();                        var amountTotal = item.AmountTotal.ToUInt32();                        var result = new Tuple<int, int>(Convert.ToInt32(amountTotal), Convert.ToInt32(volumeTotal));                        OnTotalizerReceived?.Invoke(this, new TotalizerDataEventArgs(nozzleNo, result));                    }                }            }            else if (context.Incoming.Message != null && context.Incoming.Message.Handle == (byte)Command.LockOrUnlockPumpAck)            {                var lockUnlockResult = context.Incoming.Message as LockOrUnlockPumpAck;                if (lockUnlockResult != null)                {                                                                                                                                                                                                                                                                }            }            else            {                if (context.Incoming.Message != null && context.Incoming.Message.Handle == (byte)Command.DataRequest)                {                    var request = context.Incoming.Message as DataDownloadRequest;                    if (request != null && request.DataContentType == DataContentType.FuelPriceList)                    {                        InfoLog($"Fuel Price length download request from terminal address: {request.SourceAddress}");                        OnTerminalFuelPriceDownloadRequested?.Invoke(this, new FuelPriceDownloadRequestedEventArgs(true));                    }                }                else if (context.Incoming.Message != null && context.Incoming.Message.Handle == (byte)Command.Check_cmd)                {                    CheckStatus((CheckCmdRequest)context.Incoming.Message);                    OnCheckCommandReceived?.Invoke(this, new CheckCommandEventArgs((CheckCmdRequest)context.Incoming.Message));                }                OnTerminalMessageReceived?.Invoke(this, new TerminalMessageEventArgs(pumpIds, context.Incoming.Message));            }            await Task.CompletedTask;        }        private void CheckStatus(CheckCmdRequest request)        {            if(!statusDict.ContainsKey(request.FuelingPoint.PumpNo))            {                var result = statusDict.TryAdd(request.FuelingPoint.PumpNo,                     new PumpStateHolder                    {                        PumpNo = request.FuelingPoint.PumpNo,                        NozzleNo = 1,                        State = request,                        OperationType = LockUnlockOperation.None                    });                logger.Info($"Adding FuelingPoint {request.FuelingPoint.PumpNo} to dict");                if (!result)                {                    statusDict.TryAdd(request.FuelingPoint.PumpNo, null);                }            }            else            {                PumpStateHolder stateHolder = null;                statusDict.TryGetValue(request.FuelingPoint.PumpNo, out stateHolder);                if (stateHolder != null)                {                    logger.Debug($"State holder, PumpNo: {stateHolder.PumpNo}, dispenser state: {stateHolder.State.DispenserState}, " +                        $"operation: {stateHolder.OperationType}");                }                if (stateHolder != null && stateHolder.OperationType != LockUnlockOperation.None)                {                    logger.Debug($"PumpNo: {request.FuelingPoint.PumpNo}, Last Dispenser State: {stateHolder.State.DispenserState}, " +                        $"Current Dispenser State: {request.DispenserState}");                    if (stateHolder.State.DispenserState == 3 && request.DispenserState == 2)                    {                                                if (stateHolder.OperationType != LockUnlockOperation.None)                        {                            logger.Info("Locking done!");                            stateHolder.State = request;                             OnLockUnlockCompleted?.Invoke(this, new LockUnlockEventArgs(stateHolder.OperationType, true));                        }                    }                    else if (stateHolder.State.DispenserState == 2 && request.DispenserState == 3)                    {                                                if (stateHolder.OperationType != LockUnlockOperation.None)                        {                            logger.Info($"Unlocking done!");                            stateHolder.State = request;                             OnLockUnlockCompleted?.Invoke(this, new LockUnlockEventArgs(stateHolder.OperationType, true));                        }                    }                }                else if (stateHolder != null && stateHolder.OperationType == LockUnlockOperation.None)                {                    if (stateHolder.State.DispenserState != request.DispenserState)                    {                        logger.Warn($"Observed a pump state change, {stateHolder.State.DispenserState} -> {request.DispenserState}");                        stateHolder.State = request;                     }                }            }        }        public void Write(CardMessageBase cardMessage)        {            _context.Outgoing.Write(cardMessage);        }        public async Task<CardMessageBase> WriteAsync(CardMessageBase request, Func<CardMessageBase, CardMessageBase, bool> responseCapture,             int timeout)        {            var resp = await _context.Outgoing.WriteAsync(request, responseCapture, timeout);            return resp;        }        #endregion        #region IEnumerable<IFdcPumpController> implementation        public IEnumerator<IFdcPumpController> GetEnumerator()        {            return pumpHandlers.GetEnumerator();        }        IEnumerator IEnumerable.GetEnumerator()        {            return pumpHandlers.GetEnumerator();        }        #endregion        public void PendMessage(CardMessageBase message)        {            lock (syncObj)            {                queue.Enqueue(message);            }        }        public bool TrySendNextMessage()        {            lock (syncObj)            {                if (queue.Count > 0)                {                    DebugLog($"queue count: {queue.Count}");                    var message = queue.Dequeue();                    Write(message);                    return true;                }            }            return false;        }        public void StoreLatestFrameSqNo(int pumpId, byte frameSqNo)        {            var pump = GetPump(pumpId);            if (pump != null)            {                pump.FrameSqNo = frameSqNo;            }        }        public void UpdatePumpState(int pumpId, int logicId, LogicalDeviceState state)        {            var currentPump = GetPump(pumpId);            currentPump?.FirePumpStateChange(state, Convert.ToByte(logicId));        }        public void UpdateFuelingStatus(int pumpId, FdcTransaction fuelingTransaction)        {            var currentPump = GetPump(pumpId);            currentPump?.FireFuelingStatusChange(fuelingTransaction);        }        private HengshanPumpHandler GetPump(int pumpId)        {            return pumpHandlers.FirstOrDefault(p => p.PumpId == pumpId);        }        public void SetRealPrice(int pumpId,int price)        {            var currentPump = GetPump(pumpId);            var nozzle = currentPump?.Nozzles.FirstOrDefault();            if (nozzle != null)                nozzle.RealPriceOnPhysicalPump = price;        }        #region Log methods        private void InfoLog(string info)        {            logger.Info("PayTermHdlr " + info);        }        private void DebugLog(string debugMsg)        {            logger.Debug("PayTermHdlr " + debugMsg);        }        #endregion    }    public class HengshanPayTerminalHanlderGroupConfigV1    {        public string PumpIds { get; set; }        public List<PumpSubAddress> PumpSubAddresses { get; set; }    }    public class HengshanPayTerminalHanlderGroupConfigV2    {        public string PumpIds { get; set; }        public List<PumpSubAddress> PumpSubAddresses { get; set; }        public List<PumpNozzleLogicId> PumpNozzleLogicIds { get; set; }        public List<PumpSiteNozzleNo> PumpSiteNozzleNos { get; set; }        public List<NozzleLogicId> NozzleLogicIds { get; set; }    }    public class PumpSubAddress    {        public byte PumpId { get; set; }        public byte SubAddress { get; set; }    }    public class PumpNozzleLogicId    {        public byte PumpId { get; set; }        public string LogicIds { get; set; }    }    public class PumpSiteNozzleNo    {        public byte PumpId { get; set; }        public string SiteNozzleNos { get; set; }    }    public class NozzleLogicId    {        public byte NozzleNo { get; set; }        public byte LogicId { get; set; }    }}
 |