123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- using Edge.Core.IndustryStandardInterface.Pump.Fdc;
- using Edge.Core.IndustryStandardInterface.Pump;
- using HengshanPaymentTerminal;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using HengshanPaymentTerminal.MessageEntity.Outgoing;
- namespace HengshanPaymentTerminal
- {
- /// <summary>
- /// A neutral pump handler that represents a Hengshan pump regardless of its protocol (Hengshan or TQC).
- /// Actuall this is a fake pump handler since it doesn't have direct control over the pumps.
- /// </summary>
- public class HengshanPumpHandler : IFdcPumpController
- {
- #region Fields
- //The parent of the pump handler.
- private HengshanPayTermHandler terminalHandler;
- private LogicalDeviceState state = LogicalDeviceState.FDC_READY;
- //private SpsManager spsManager;
- private byte frameSqNo;
- private object syncObj = new object();
- private bool totalizerRequested = false;
- private byte totalizerRequestedNozzleLogicId = 0;
- private bool lockPumpRequested = false;
- private bool unlockPumpRequested = false;
- private LogicalNozzle nozzle;
- private List<LogicalNozzle> nozzleList;
- private List<int> siteNozzleNoList;
- #endregion
- #region Logger
- private static NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("IPosPlusApp");
- #endregion
- #region Constructor
- public HengshanPumpHandler(HengshanPayTermHandler terminalHandler, string name, int pumpId, List<int> nozzles, List<int> siteNozzleNoList)
- {
- this.terminalHandler = terminalHandler;
- this.terminalHandler.OnCheckCommandReceived += TerminalHandler_OnCheckCommandReceived;
- Name = name;
- PumpId = pumpId;
- nozzle = new LogicalNozzle(pumpId, Convert.ToByte(pumpId), 1, null);
- //spsManager = new SpsManager();
- this.siteNozzleNoList = siteNozzleNoList;
- nozzleList = new List<LogicalNozzle>();
- foreach (var nozzleNo in nozzles)
- {
- var logicalNozzle = new LogicalNozzle(PumpId, Convert.ToByte(nozzleNo), Convert.ToByte(nozzleNo), null);
- nozzleList.Add(logicalNozzle);
- }
- }
- #endregion
- #region Properties
- public string Name { get; }
- public int PumpId { get; }
- public int PumpPhysicalId => 1;
- public IEnumerable<LogicalNozzle> Nozzles
- {
- get
- {
- //For Hengshan pumps, there is only one logical nozzle per pump, no real price set.
- return nozzleList;//return new List<LogicalNozzle> { nozzle };
- }
- }
- //Decimal digit settings, China domestic standard
- //小数点后位数设定,中国国内标准,一般为2位
- public int AmountDecimalDigits => 2;
- public int VolumeDecimalDigits => 2;
- public int PriceDecimalDigits => 2;
- public int VolumeTotalizerDecimalDigits => 2;
- public byte FrameSqNo
- {
- get
- {
- lock (syncObj)
- {
- return frameSqNo;
- }
- }
- set
- {
- lock (syncObj)
- {
- frameSqNo = value;
- }
- }
- }
- #endregion
- #region Events
- public event EventHandler<FdcPumpControllerOnStateChangeEventArg> OnStateChange;
- public event EventHandler<FdcTransactionDoneEventArg> OnCurrentFuellingStatusChange;
- public event EventHandler<FuelPriceChangeRequestEventArgs> OnFuelPriceChangeRequested;
- #endregion
- #region Event handlers
- private byte GetSiteNozzleNoByLogicId(byte logicId)
- {
- foreach (var nozzleNo in siteNozzleNoList)
- {
- if (terminalHandler.NozzleLogicIdDict[nozzleNo] == logicId)
- return Convert.ToByte(nozzleNo);
- }
- return Convert.ToByte(PumpId);
- }
- private void TerminalHandler_OnCheckCommandReceived(object sender, CheckCommandEventArgs e)
- {
- if (totalizerRequested
- && e.CheckCommandRequest.SourceAddress == terminalHandler.GetSubAddressForPump(PumpId))
- {
- var request = new ReadVolumeTotal
- {
- Prefix = 0xFA,
- SourceAddress = Convert.ToByte(terminalHandler.GetSubAddressForPump(PumpId)),
- DestinationAddress = Convert.ToByte(terminalHandler.GetSubAddressForPump(PumpId)),
- FrameSqNoByte = e.CheckCommandRequest.FrameSqNoByte,
- Handle = (byte)MessageEntity.Command.ReadVolumeTotalizer,
- NozzleNo = GetSiteNozzleNoByLogicId(totalizerRequestedNozzleLogicId)//Convert.ToByte(PumpId)
- };
- terminalHandler.PendMessage(request);
- Log("Request to terminal to get totalizer, pending!");
- }
- else if ((lockPumpRequested || unlockPumpRequested)
- && e.CheckCommandRequest.SourceAddress == terminalHandler.GetSubAddressForPump(PumpId))
- {
- LockOrUnlockPumpRequest request = new LockOrUnlockPumpRequest
- {
- Prefix = 0xFA,
- SourceAddress = Convert.ToByte(terminalHandler.GetSubAddressForPump(PumpId)),
- DestinationAddress = Convert.ToByte(terminalHandler.GetSubAddressForPump(PumpId)),
- Handle = Convert.ToByte(MessageEntity.Command.LockOrUnlockPump),
- FrameSqNoByte = e.CheckCommandRequest.FrameSqNoByte,
- FPCode = EncodeFPCodeString(PumpId, PumpId),
- OperationType = lockPumpRequested ? Support.LockUnlockOperation.Lock : Support.LockUnlockOperation.Unlock
- };
- terminalHandler.PendMessage(request);
- var currentPump = terminalHandler.PumpStatusDict.GetValueOrDefault(PumpId);
- if (currentPump == null)
- Log("Could not get the pump state holder");
- else
- Log($"Current pump state holder, pump no: {currentPump.PumpNo}");
- currentPump.OperationType = request.OperationType;
- Log($"Current pump state holder, operation type set to: {request.OperationType}");
- //terminalHandler.Write(request);
- if (lockPumpRequested)
- Log($"PumpNo: {PumpId}, Request to lock pump, pending!");
- if (unlockPumpRequested)
- Log($"PumpNo: {PumpId}, Request to unlock pump, pending!");
- }
- }
- public void FirePumpStateChange(LogicalDeviceState state, byte nozzeLogicId)
- {
- var logicalNozzle = new LogicalNozzle(PumpId, Convert.ToByte(PumpPhysicalId), nozzeLogicId, null);
- this.state = state;
- if (state == LogicalDeviceState.FDC_READY)
- OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_READY));
- else
- OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(state, logicalNozzle));
- }
- public void FireNozzleStateChange(LogicalDeviceState? state)
- {
- nozzle = new LogicalNozzle(PumpId, Convert.ToByte(PumpPhysicalId), 1, null);
- if (state.HasValue)
- nozzle.LogicalState = state;
- else
- nozzle.LogicalState = null;
- OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_READY, nozzle));
- }
- //2023-08-22 状态变更
- public void FireFuelingStatusChange(FdcTransaction fuelingTransaction)
- {
- OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(fuelingTransaction));
- }
- #endregion
- #region Pump interactions
- public LogicalDeviceState QueryStatus()
- {
- var stateHolder = terminalHandler.PumpStatusDict.GetValueOrDefault(PumpId);
- if (stateHolder != null)
- {
- if (stateHolder.State.DispenserState == 3)
- {
- return LogicalDeviceState.FDC_READY;
- }
- else if (stateHolder.State.DispenserState == 2)
- {
- return LogicalDeviceState.FDC_LOCKED;
- }
- }
- return LogicalDeviceState.FDC_READY;
- }
- /// <summary>
- /// locks a nozzle
- /// </summary>
- /// <param name="logicalNozzleId"></param>
- /// <returns></returns>
- public Task<bool> LockNozzleAsync(byte logicalNozzleId)
- {
- var stateHolder = terminalHandler.PumpStatusDict.GetValueOrDefault(PumpId);
- if (stateHolder != null)
- {
- if (stateHolder.State.DispenserState == 2)
- {
- // If the pump is already locked, reply with success immediately.
- return Task.FromResult(true);
- }
- else if (stateHolder.State.DispenserState != 3)
- {
- // Avoid handling locking when pump is fueling or in other states.
- return Task.FromResult(false);
- }
- }
- TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
- EventHandler<LockUnlockEventArgs> eventHandler = null;
- eventHandler += (o, e) =>
- {
- Log($"Locking pump, result received, success? {e.Result}");
- lockPumpRequested = false;
- tcs.SetResult(e.Result);
- if (e.Result)
- {
- FireNozzleStateChange(LogicalDeviceState.FDC_LOCKED);
- }
- terminalHandler.OnLockUnlockCompleted -= eventHandler;
- var currentPump = terminalHandler.PumpStatusDict.GetValueOrDefault(PumpId);
- if (currentPump != null)
- {
- currentPump.OperationType = Support.LockUnlockOperation.None;
- }
- };
- terminalHandler.OnLockUnlockCompleted += eventHandler;
- lockPumpRequested = true;
- return tcs.Task;
- }
- /// <summary>
- /// unlock a locked nozzle
- /// </summary>
- /// <param name="logicalNozzleId"></param>
- /// <returns></returns>
- public Task<bool> UnlockNozzleAsync(byte logicalNozzleId)
- {
- var stateHolder = terminalHandler.PumpStatusDict.GetValueOrDefault(PumpId);
- if (stateHolder != null)
- {
- if (stateHolder.State.DispenserState == 3)
- {
- // Need to do nothing when pump is not locked.
- return Task.FromResult(true);
- }
- }
- TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
- EventHandler<LockUnlockEventArgs> eventHandler = null;
- eventHandler += (o, e) =>
- {
- Log($"Unlocking pump, result received, success? {e.Result}");
- unlockPumpRequested = false;
- tcs.SetResult(e.Result);
- if (e.Result)
- {
- FireNozzleStateChange(null);
- }
- terminalHandler.OnLockUnlockCompleted -= eventHandler;
- var currentPump = terminalHandler.PumpStatusDict.GetValueOrDefault(PumpId);
- if (currentPump != null)
- {
- currentPump.OperationType = Support.LockUnlockOperation.None;
- }
- };
- terminalHandler.OnLockUnlockCompleted += eventHandler;
- unlockPumpRequested = true;
- return tcs.Task;
- }
- #endregion
- #region FDC Server Init
- public void OnFdcServerInit(Dictionary<string, object> parameters)
- {
- }
- public Task<LogicalDeviceState> QueryStatusAsync()
- {
- return Task.FromResult(state);
- }
- public void QueryTotalizerAsync(int pumpId, byte logicalNozzleId)
- {
- Log($"Query totalizer internally...");
- var request = new ReadVolumeTotal
- {
- Prefix = 0xFA,
- SourceAddress = Convert.ToByte(pumpId),
- DestinationAddress = Convert.ToByte(pumpId),
- FrameSqNoByte = frameSqNo,
- Handle = (byte)MessageEntity.Command.ReadVolumeTotalizer,
- NozzleNo = logicalNozzleId
- };
- terminalHandler.Write(request);
- }
- public Task<Tuple<long, long>> QueryTotalizerAsync(byte logicalNozzleId)
- {
- Log($"LogicalNozzle {logicalNozzleId} querying totalizer...");
- TaskCompletionSource<Tuple<long, long>> tcs = new TaskCompletionSource<Tuple<long, long>>();
- EventHandler<TotalizerDataEventArgs> eventHandler = null;
- eventHandler += (o, e) =>
- {
- Log($"Totalizer data for nozzle: {e.NozzleNo}, money acc: {e.Totalizer.Item1}, volume acc: {e.Totalizer.Item2}");
- totalizerRequested = false; //Reset the value so that no more request will be sent.
- if (e.NozzleNo == GetSiteNozzleNoByLogicId(logicalNozzleId))
- {
- Log("Totalizer data ready");
- tcs.SetResult(e.Totalizer);
- terminalHandler.OnTotalizerReceived -= eventHandler;
- }
- totalizerRequestedNozzleLogicId = 0;
- };
- terminalHandler.OnTotalizerReceived += eventHandler;
- totalizerRequestedNozzleLogicId = logicalNozzleId;
- totalizerRequested = true;
- return tcs.Task;
- }
- public Task<bool> ChangeFuelPriceAsync(int newPriceWithoutDecimalPoint, byte logicalNozzleId)
- {
- logger.Info($"Change price received, Pump {PumpId}, LogicalNozzle {logicalNozzleId}, Price {newPriceWithoutDecimalPoint}");
- //Let the iPOS App handle the fuel price change.
- OnFuelPriceChangeRequested?.Invoke(this, new FuelPriceChangeRequestEventArgs
- {
- NozzleId = logicalNozzleId,
- PumpId = (byte)PumpId,
- Price = newPriceWithoutDecimalPoint
- });
- Log($"application handled fuel price change");
- return Task.FromResult(true);
- //TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
- //EventHandler<FuelPriceDownloadRequestedEventArgs> eventHandler = null;
- //eventHandler += (o, e) =>
- //{
- // Log("Terminal requested fuel price");
- // tcs.SetResult(e.Requested);
- // terminalHandler.OnTerminalFuelPriceDownloadRequested -= eventHandler;
- //};
- //terminalHandler.OnTerminalFuelPriceDownloadRequested += eventHandler;
- //return tcs.Task;
- }
- public Task<bool> AuthorizeAsync(byte logicalNozzleId)
- {
- return Task.FromResult(false);
- }
- public Task<bool> UnAuthorizeAsync(byte logicalNozzleId)
- {
- return Task.FromResult(false);
- }
- public Task<bool> AuthorizeWithAmountAsync(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId)
- {
- return Task.FromResult(false);
- }
- public Task<bool> AuthorizeWithVolumeAsync(int volumnWithoutDecimalPoint, byte logicalNozzleId)
- {
- return Task.FromResult(false);
- }
- public Task<bool> FuelingRoundUpByAmountAsync(int amount)
- {
- return Task.FromResult(false);
- }
- public Task<bool> FuelingRoundUpByVolumeAsync(int volume)
- {
- return Task.FromResult(false);
- }
- public Task<bool> SuspendFuellingAsync()
- {
- return Task.FromResult(false);
- }
- public Task<bool> ResumeFuellingAsync()
- {
- return Task.FromResult(false);
- }
- #endregion
- private string EncodeFPCodeString(int nozzleId, int pumpId)
- {
- return nozzleId.ToString("X").PadLeft(2, '0') + pumpId.ToString("X").PadLeft(2, '0');
- }
- #region Log
- private void Log(string message)
- {
- logger.Info($"Pump Handler: {PumpId}, {message}");
- }
- #endregion
- }
- }
|