| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- using Censtar_31064V105OrV106_Pump.MessageEntity.Incoming;
- using Censtar_31064V105OrV106_Pump.MessageEntity.Outgoing;
- using Edge.Core.IndustryStandardInterface.Pump;
- using Edge.Core.Parser.BinaryParser.MessageEntity;
- using Edge.Core.Processor;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.Logging.Abstractions;
- using Stateless;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text.Json;
- using System.Text.Json.Serialization;
- using System.Threading.Tasks;
- using Wayne.FDCPOSLibrary;
- using static Censtar_31064V105OrV106_Pump.PumpGroupHandler;
- using static Censtar_31064V105OrV106_Pump.PumpGroupHandler.DispenserParameter;
- namespace Censtar_31064V105OrV106_Pump
- {
- public class StatePumpHandler : IFdcPumpController
- {
- public DispenserModelEnum DispenserModel { get; set; }
- public DispenserAuthorizeModeEnum DispenserAuthorizeMode { get; set; }
- public SetPostFuelingLockTypeRequest.LockTypeEnum DispenserPostFuelingLockMode { get; set; }
- private IServiceProvider services;
- static ILogger logger = NullLogger.Instance;
- private LogicalDeviceState lastLogicalDeviceState = LogicalDeviceState.FDC_OFFLINE;
- private DateTime lastLogicalDeviceStateReceivedTime;
- // by seconds, change this value need change the correlated deviceOfflineCountdownTimer's interval as well
- public const int lastLogicalDeviceStateExpiredTime = 6;
- private List<LogicalNozzle> nozzles = new List<LogicalNozzle>();
- private System.Timers.Timer deviceOfflineCountdownTimer;
- private IContext<byte[], MessageTemplateBase> context;
- private bool isOnFdcServerInitCalled = false;
- StateMachine<LogicalDeviceState, Trigger> stateless
- = new StateMachine<LogicalDeviceState, Trigger>(LogicalDeviceState.FDC_OFFLINE);
- private int amountDecimalDigits;
- private int volumeDecimalDigits;
- private int priceDecimalDigits;
- private int volumeTotalizerDecimalDigits;
- private StateMachine<LogicalDeviceState, Trigger>.TriggerWithParameters<FdcTransaction> nozzleFuelNumbersIsRunningTrigger;
- private int pumpId;
- public string Name => "Censtar_31064V105OrV106_Pump";
- public int PumpId => this.pumpId;
- public IEnumerable<LogicalNozzle> Nozzles => this.nozzles;
- public int AmountDecimalDigits { get; set; }
- public int VolumeDecimalDigits { get; set; }
- public int PriceDecimalDigits { get; set; }
- public int VolumeTotalizerDecimalDigits { get; set; }
- public Guid Id => Guid.NewGuid();
- public int PumpPhysicalId { get; set; }
- public event EventHandler<FdcPumpControllerOnStateChangeEventArg> OnStateChange;
- public event EventHandler<FdcTransactionDoneEventArg> OnCurrentFuellingStatusChange;
- private enum Trigger
- {
- //AnyPumpMsgReceived,
- AnyPumpMsgHaveNotReceivedForWhile,
- 关机_解锁_无交易可读,
- 关机_解锁_有交易可读,
- 关机_上锁_无交易可读,
- 关机_上锁_有交易可读,
- 加油_非总台预置加油,
- 加油_总台预置加油,
- NozzleFuelNumbersIsRunning
- }
- public StatePumpHandler(PumpParameter pumpParameter,
- IServiceProvider services)
- {
- this.services = services;
- var loggerFactory = services.GetRequiredService<ILoggerFactory>();
- logger = loggerFactory.CreateLogger("PumpHandler");
- var jsonSerializerOptions = new JsonSerializerOptions()
- {
- WriteIndented = true,
- };
- jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
- this.pumpId = pumpParameter.PumpId;
- if (pumpParameter.NozzleParameters == null || !pumpParameter.NozzleParameters.Any())
- throw new ArgumentException("Pump must has nozzle configs");
- for (byte i = 0; i < pumpParameter.NozzleParameters.Count(); i++)
- {
- this.nozzles.Add(
- new LogicalNozzle(this.pumpId,
- pumpParameter.NozzleParameters.ElementAt(i).NozzleNumber,
- (byte)(i + 1), null));
- logger.LogInformation("Pump: " + this.pumpId + " created a nozzle with NozzlePhysicalId: " +
- pumpParameter.NozzleParameters.ElementAt(i).NozzleNumber +
- ", NozzleLogicalId: " + (i + 1));
- }
- this.deviceOfflineCountdownTimer = new System.Timers.Timer(2000);
- this.deviceOfflineCountdownTimer.Elapsed += async (_, __) =>
- {
- if (DateTime.Now.Subtract(this.lastLogicalDeviceStateReceivedTime).TotalSeconds
- >= lastLogicalDeviceStateExpiredTime)
- await this.stateless.FireAsync(Trigger.AnyPumpMsgHaveNotReceivedForWhile);
- };
- this.deviceOfflineCountdownTimer.Start();
- this.stateless.OnTransitioned(async (transition) =>
- {
- if (transition.Destination != transition.Source)
- {
- this.lastLogicalDeviceState = transition.Destination;
- logger.LogInformation("Pump: " + this.pumpId + ", " + " State switched from: " + transition.Source + " to " + transition.Destination);
- this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(transition.Destination, this.nozzles.FirstOrDefault()));
- }
- if (transition.Source == LogicalDeviceState.FDC_OFFLINE)
- {
- this.context.Outgoing.Write(new ReadVersionRequest() { NozzleNumber = this.nozzles.First().PhysicalId });
- this.context.Outgoing.Write(new SetDateAndTimeRequest(DateTime.Now) { NozzleNumber = this.nozzles.First().PhysicalId });
- this.context.Outgoing.Write(new ReadUnitOfMeasurementRequest() { NozzleNumber = this.nozzles.First().PhysicalId });
- logger.LogInformation("Pump: " + this.pumpId + ", will Set AuthorizeMode to: " + this.DispenserAuthorizeMode);
- var response = await this.context.Outgoing.WriteAsync(
- this.DispenserAuthorizeMode == DispenserAuthorizeModeEnum.开放 ?
- new SetMonitoringModeRequest(SetMonitoringModeRequest.MonitoringModeEnum.开放) { NozzleNumber = this.nozzles.First().PhysicalId }
- : new SetMonitoringModeRequest(SetMonitoringModeRequest.MonitoringModeEnum.监控) { NozzleNumber = this.nozzles.First().PhysicalId },
- (_, testResponse) => testResponse is SetMonitoringModeAnswer, 3000);
- logger.LogInformation("Pump: " + this.pumpId + ", Set AuthorizeMode " + (response == null ? "failed" : "succeed"));
- logger.LogInformation("Pump: " + this.pumpId + ", will Set PostFuelingLockType to: " + this.DispenserPostFuelingLockMode);
- response = await this.context.Outgoing.WriteAsync(
- new SetPostFuelingLockTypeRequest(this.DispenserPostFuelingLockMode, "123") { NozzleNumber = this.nozzles.First().PhysicalId },
- (_, testResponse) => testResponse is SetPostFuelingLockTypeAnswer, 3000);
- logger.LogInformation("Pump: " + this.pumpId + ", Set PostFuelingLockType " + (response == null ? "failed" : "succeed"));
- }
- });
- this.stateless.Configure(LogicalDeviceState.FDC_OFFLINE)
- .OnEntryAsync(async () =>
- {
- if (this.DispenserAuthorizeMode == DispenserAuthorizeModeEnum.监控)
- {
- logger.LogInformation("Pump: " + this.pumpId + ", 送开机信号... due to DispenserAuthorizeMode==监控");
- var response = await this.context.Outgoing.WriteAsync(
- new StartRequest() { NozzleNumber = this.nozzles.First().PhysicalId },
- (_, testResponse) => testResponse is StartAnswer, 3000);
- logger.LogInformation("Pump: " + this.pumpId + ", 送开机信号 acked with " + (response == null ? "failed" : "succeed"));
- }
- })
- //.Ignore(Trigger.AnyPumpMsgReceived)
- .Ignore(Trigger.AnyPumpMsgHaveNotReceivedForWhile)
- .PermitIf(Trigger.加油_总台预置加油, LogicalDeviceState.FDC_CALLING, () => true)
- .PermitIf(Trigger.加油_非总台预置加油, LogicalDeviceState.FDC_CALLING, () => true)
- .PermitIf(Trigger.关机_上锁_无交易可读, LogicalDeviceState.FDC_READY, () => true)
- // use FDC_OUTOFORDER for reading the trx
- .PermitIf(Trigger.关机_上锁_有交易可读, LogicalDeviceState.FDC_OUTOFORDER, () => true)
- .PermitIf(Trigger.关机_解锁_无交易可读, LogicalDeviceState.FDC_READY, () => true)
- .PermitIf(Trigger.关机_解锁_有交易可读, LogicalDeviceState.FDC_OUTOFORDER, () => true);
- this.stateless.Configure(LogicalDeviceState.FDC_CALLING)
- .OnEntryAsync(async () => { })
- .Ignore(Trigger.AnyPumpMsgHaveNotReceivedForWhile)
- .PermitIf(Trigger.加油_总台预置加油, LogicalDeviceState.FDC_AUTHORISED, () => true)
- .PermitIf(Trigger.加油_非总台预置加油, LogicalDeviceState.FDC_AUTHORISED, () => true);
- this.stateless.Configure(LogicalDeviceState.FDC_AUTHORISED)
- .OnEntryAsync(async () => { })
- .Ignore(Trigger.AnyPumpMsgHaveNotReceivedForWhile)
- .PermitIf(Trigger.加油_总台预置加油, LogicalDeviceState.FDC_FUELLING, () => true)
- .PermitIf(Trigger.加油_非总台预置加油, LogicalDeviceState.FDC_FUELLING, () => true);
- this.nozzleFuelNumbersIsRunningTrigger =
- this.stateless.SetTriggerParameters<FdcTransaction>(Trigger.NozzleFuelNumbersIsRunning);
- this.stateless.Configure(LogicalDeviceState.FDC_FUELLING)
- .OnEntryFromAsync(this.nozzleFuelNumbersIsRunningTrigger, async (arg) =>
- {
- this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(arg));
- })
- .OnEntryAsync(async () => { })
- .OnExitAsync(async () => { })
- .PermitReentry(Trigger.NozzleFuelNumbersIsRunning)
- .Ignore(Trigger.AnyPumpMsgHaveNotReceivedForWhile)
- .PermitIf(Trigger.关机_上锁_有交易可读, LogicalDeviceState.FDC_OUTOFORDER, () => true)
- .PermitIf(Trigger.关机_解锁_有交易可读, LogicalDeviceState.FDC_OUTOFORDER, () => true)
- .PermitIf(Trigger.关机_上锁_无交易可读, LogicalDeviceState.FDC_READY, () => true)
- .PermitIf(Trigger.关机_解锁_无交易可读, LogicalDeviceState.FDC_READY, () => true);
- this.stateless.Configure(LogicalDeviceState.FDC_OUTOFORDER)
- .OnEntryAsync(async () =>
- {
- logger.LogInformation("Pump: " + this.pumpId + ", Reading last sale...");
- var response = await this.context.Outgoing.WriteAsync(
- new ReadTransactionDataRequest() { NozzleNumber = this.nozzles.First().PhysicalId },
- (_, testResponse) =>
- (testResponse is ReadTransactionDataAndHasNoDataAnswer)
- || (testResponse is ReadTransactionDataAndHasDataAnswer),
- 3000);
- if (response is ReadTransactionDataAndHasDataAnswer hasDataAnswer)
- {
- logger.LogDebug("Pump: " + this.pumpId + ", Retrieved fuel sale trx, amt: " +
- hasDataAnswer.金额 + ", vol: " + hasDataAnswer.升或公斤 +
- ", seqNo.: " + hasDataAnswer.成交链号_SequenceNumber);
- var newTrx = new FdcTransaction()
- {
- // 只有一把枪
- Nozzle = this.nozzles.First(),
- Amount = hasDataAnswer.金额,
- Volumn = hasDataAnswer.升或公斤,
- Price = this.nozzles.First().RealPriceOnPhysicalPump ?? -1,
- SequenceNumberGeneratedOnPhysicalPump = hasDataAnswer.成交链号_SequenceNumber,
- Finished = true,
- };
- this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(newTrx));
- }
- else if(response is ReadTransactionDataAndHasNoDataAnswer hasNoDataAnswer)
- {
- // should switch to FdcReady?
- }
- else
- {
- logger.LogWarning("Pump: " + this.pumpId + ", fuel sale trx is missing for final read(auto tried one more time and still failed), trx will lost");
- return;
- }
- })
- .Ignore(Trigger.AnyPumpMsgHaveNotReceivedForWhile)
- .PermitIf(Trigger.关机_上锁_无交易可读, LogicalDeviceState.FDC_READY, () => true)
- .PermitIf(Trigger.关机_解锁_无交易可读, LogicalDeviceState.FDC_READY, () => true);
- }
- public async Task Process(IContext<byte[], MessageTemplateBase> context)
- {
- if (context.Incoming.Message is ReadRealTimeFuelingDataInPumpFuelingStateAnswer
- fuelingStateDataResponse)
- {
- await this.stateless.FireAsync(Trigger.加油_非总台预置加油);
- //await this.stateless.FireAsync(Trigger.NozzleFuelNumbersIsRunning);
- await this.stateless.FireAsync(this.nozzleFuelNumbersIsRunningTrigger,
- new FdcTransaction()
- {
- // 只有一把枪
- Nozzle = this.nozzles.First(),
- //Amount = readFuelingDataResponse.Amount,
- Volumn = fuelingStateDataResponse.数量,
- Price = this.nozzles.First().RealPriceOnPhysicalPump ?? -1,
- Finished = false,
- });
- }
- else if (context.Incoming.Message is ReadRealTimeFuelingAmountDataInPumpNonFuelingStateAnswer
- IdleStateAmountDataResponse)
- {
- if (this.DispenserPostFuelingLockMode == SetPostFuelingLockTypeRequest.LockTypeEnum.加油后加锁_不可解锁
- || this.DispenserPostFuelingLockMode == SetPostFuelingLockTypeRequest.LockTypeEnum.加油后加锁_可解锁
- || this.DispenserPostFuelingLockMode == SetPostFuelingLockTypeRequest.LockTypeEnum.室外解锁密码)
- await this.stateless.FireAsync(Trigger.关机_上锁_有交易可读);
- else
- await this.stateless.FireAsync(Trigger.关机_解锁_有交易可读);
- }
- else if (context.Incoming.Message is ReadRealTimeFuelingVolumeDataInPumpNonFuelingStateAnswer
- IdleStateVolumeDataResponse)
- {
- if (this.DispenserPostFuelingLockMode == SetPostFuelingLockTypeRequest.LockTypeEnum.加油后加锁_不可解锁
- || this.DispenserPostFuelingLockMode == SetPostFuelingLockTypeRequest.LockTypeEnum.加油后加锁_可解锁
- || this.DispenserPostFuelingLockMode == SetPostFuelingLockTypeRequest.LockTypeEnum.室外解锁密码)
- await this.stateless.FireAsync(Trigger.关机_上锁_有交易可读);
- else
- await this.stateless.FireAsync(Trigger.关机_解锁_有交易可读);
- }
- }
- public Task<bool> AuthorizeAsync(byte logicalNozzleId)
- {
- throw new NotImplementedException();
- }
- public Task<bool> AuthorizeWithAmountAsync(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId)
- {
- throw new NotImplementedException();
- }
- public Task<bool> AuthorizeWithVolumeAsync(int volumnWithoutDecimalPoint, byte logicalNozzleId)
- {
- throw new NotImplementedException();
- }
- public Task<bool> ChangeFuelPriceAsync(int newPriceWithoutDecimalPoint, byte logicalNozzleId)
- {
- throw new NotImplementedException();
- }
- public Task<bool> FuelingRoundUpByAmountAsync(int amount)
- {
- throw new NotImplementedException();
- }
- public Task<bool> FuelingRoundUpByVolumeAsync(int volume)
- {
- throw new NotImplementedException();
- }
- public void Init(IContext<byte[], MessageTemplateBase> context)
- {
- throw new NotImplementedException();
- }
- public Task<bool> LockNozzleAsync(byte logicalNozzleId)
- {
- throw new NotImplementedException();
- }
- public void OnFdcServerInit(Dictionary<string, object> parameters)
- {
- throw new NotImplementedException();
- }
- public Task<LogicalDeviceState> QueryStatusAsync()
- {
- throw new NotImplementedException();
- }
- public Task<Tuple<int, int>> QueryTotalizerAsync(byte logicalNozzleId)
- {
- throw new NotImplementedException();
- }
- public Task<bool> ResumeFuellingAsync()
- {
- throw new NotImplementedException();
- }
- public Task<bool> SuspendFuellingAsync()
- {
- throw new NotImplementedException();
- }
- public Task<bool> UnAuthorizeAsync(byte logicalNozzleId)
- {
- throw new NotImplementedException();
- }
- public Task<bool> UnlockNozzleAsync(byte logicalNozzleId)
- {
- throw new NotImplementedException();
- }
- }
- }
|