using Edge.Core.IndustryStandardInterface.Pump; using Edge.Core.Processor; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; using System.Xml.Serialization; using Wayne.FDCPOSLibrary; namespace Bogus_Pump { public class PumpHandler : IFdcPumpController, IDisposable { private bool enableFuelSimulation; private int pumpId; private int pumpPhysicalId; private IEnumerable nozzles; private LogicalDeviceState currentState = LogicalDeviceState.FDC_READY; public string Name => "Bogus_Pump handler"; public int PumpId => this.pumpId; public int PumpPhysicalId => this.pumpPhysicalId; public IEnumerable Nozzles => this.nozzles; public int AmountDecimalDigits => 2; public int VolumeDecimalDigits => 2; public int PriceDecimalDigits => 2; public int VolumeTotalizerDecimalDigits => 2; public event EventHandler OnStateChange; public event EventHandler OnCurrentFuellingStatusChange; private System.Timers.Timer autoCallingTimer; /// /// /// /// /// /// set to true to enable auto calling, fueling, and idle. otherwise, nozzle keeps idle. public PumpHandler(int pumpId, byte nozzleCount, bool enableFuelSimulation) { this.enableFuelSimulation = enableFuelSimulation; this.pumpId = pumpId; this.pumpPhysicalId = pumpId; this.nozzles = Enumerable.Range(1, nozzleCount).Select(i => new LogicalNozzle(pumpId, (byte)i, (byte)i, 831)).ToList(); if (this.enableFuelSimulation) { this.autoCallingTimer = new System.Timers.Timer(new Random().Next(60, 200) * 100); this.autoCallingTimer.Elapsed += (_, __) => { if (this.currentState == LogicalDeviceState.FDC_AUTHORISED || this.currentState == LogicalDeviceState.FDC_FUELLING) { return; } //now simply only allow first nozzle var operatingNozzle = this.nozzles.FirstOrDefault(); if (this.currentState == LogicalDeviceState.FDC_CALLING) { this.currentState = LogicalDeviceState.FDC_READY; this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(this.currentState, operatingNozzle)); return; } if (this.currentState == LogicalDeviceState.FDC_READY) { this.currentState = LogicalDeviceState.FDC_CALLING; this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(this.currentState, operatingNozzle)); } }; } } public async Task AuthorizeAsync(byte logicalNozzleId) { //if (this.enableFuelSimulation) // return false; if (this.currentState == LogicalDeviceState.FDC_AUTHORISED || this.currentState == LogicalDeviceState.FDC_FUELLING) return false; LogicalNozzle n = this.nozzles.FirstOrDefault(n => n.LogicalId == logicalNozzleId); if (n == null) return await Task.FromResult(false); await Task.Delay(1000); this.currentState = LogicalDeviceState.FDC_AUTHORISED; this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(this.currentState, n)); await Task.Delay(1000); this.currentState = LogicalDeviceState.FDC_FUELLING; this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(this.currentState, n)); await Task.Delay(300); _ = Task.Run(() => { int amt = 0; int vol = 0; int seqNumber = (int)(DateTime.Now.Subtract(new DateTime(2021, 1, 1)).TotalSeconds); const int FireMaxTimes = 12; int firedTimes = 0; while (true) { amt += new Random().Next(500, 1000); vol += new Random().Next(100, 300); this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction() { Amount = amt, Volumn = vol, Nozzle = n, Price = n.RealPriceOnPhysicalPump ?? -1, SequenceNumberGeneratedOnPhysicalPump = seqNumber, Finished = false })); Thread.Sleep(1000); firedTimes++; if (firedTimes > FireMaxTimes) { this.currentState = LogicalDeviceState.FDC_READY; this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(this.currentState, n)); this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction() { Amount = amt, Volumn = vol, Nozzle = n, Price = n.RealPriceOnPhysicalPump ?? -1, SequenceNumberGeneratedOnPhysicalPump = seqNumber, AmountTotalizer = new Random().Next(9999, 99999999), VolumeTotalizer = new Random().Next(1000, 11999999), Finished = true })); break; } } }); return await Task.FromResult(true); } public Task AuthorizeWithAmountAsync(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId) { return this.AuthorizeAsync(logicalNozzleId); } public Task AuthorizeWithVolumeAsync(int volumnWithoutDecimalPoint, byte logicalNozzleId) { return this.AuthorizeAsync(logicalNozzleId); } public Task ChangeFuelPriceAsync(int newPriceWithoutDecimalPoint, byte logicalNozzleId) { if (this.enableFuelSimulation) return Task.FromResult(false); var targetNozzle = this.nozzles.FirstOrDefault(n => n.LogicalId == logicalNozzleId); if (targetNozzle != null) { targetNozzle.RealPriceOnPhysicalPump = newPriceWithoutDecimalPoint; targetNozzle.ExpectingPriceOnFcSide = newPriceWithoutDecimalPoint; return Task.FromResult(true); } else return Task.FromResult(false); } public Task FuelingRoundUpByAmountAsync(int amount) { return Task.FromResult(true); } public Task FuelingRoundUpByVolumeAsync(int volume) { return Task.FromResult(true); } public Task LockNozzleAsync(byte logicalNozzleId) { return Task.FromResult(true); } public void OnFdcServerInit(Dictionary parameters) { if (parameters.ContainsKey("LastPriceChange")) { // nozzle logical id:rawPrice var lastPriceChanges = parameters["LastPriceChange"] as Dictionary; foreach (var priceChange in lastPriceChanges) { var targetNozzle = this.nozzles.FirstOrDefault(n => n.LogicalId == priceChange.Key); if (targetNozzle != null) { targetNozzle.RealPriceOnPhysicalPump = priceChange.Value; targetNozzle.ExpectingPriceOnFcSide = priceChange.Value; } } } this.autoCallingTimer?.Start(); } public Task QueryStatusAsync() { return Task.FromResult(this.currentState); } public Task> QueryTotalizerAsync(byte logicalNozzleId) { if (this.enableFuelSimulation) Task.FromResult(new Tuple(-1, -1)); return Task.FromResult( new Tuple( new Random().Next(100000, 999999), new Random().Next(50000, 99999))); } public Task ResumeFuellingAsync() { return Task.FromResult(true); } public Task SuspendFuellingAsync() { return Task.FromResult(true); } public Task UnAuthorizeAsync(byte logicalNozzleId) { return Task.FromResult(true); } public Task UnlockNozzleAsync(byte logicalNozzleId) { return Task.FromResult(true); } public void Dispose() { this.autoCallingTimer?.Dispose(); } internal void DrivenPumpStateTo(byte? logicalNozzleId, LogicalDeviceState pumpNewState, int? amountWithoutDecimalPoint, int? volumeWithoutDecimalPoint, int? priceWithoutDecimalPoint) { var targetNozzle = this.nozzles?.FirstOrDefault(n => n.LogicalId == logicalNozzleId); if (pumpNewState == LogicalDeviceState.FDC_FUELLING) { this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction() { Amount = amountWithoutDecimalPoint ?? 0, Volumn = volumeWithoutDecimalPoint ?? 0, Nozzle = targetNozzle, Price = priceWithoutDecimalPoint ?? 0, //SequenceNumberGeneratedOnPhysicalPump = seqNumber, Finished = false })); } if (this.currentState == pumpNewState) return; this.currentState = pumpNewState; this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(this.currentState, targetNozzle)); } internal void DrivenPumpRaiseNewTrx(byte? logicalNozzleId, int amountWithoutDecimalPoint, int volumeWithoutDecimalPoint, int priceWithoutDecimalPoint, int trxSeqNumber) { var targetNozzle = this.nozzles?.FirstOrDefault(n => n.LogicalId == logicalNozzleId); this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction() { Amount = amountWithoutDecimalPoint, Volumn = volumeWithoutDecimalPoint, Nozzle = targetNozzle, Price = priceWithoutDecimalPoint, SequenceNumberGeneratedOnPhysicalPump = trxSeqNumber, Finished = true })); } } }