123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705 |
- using Edge.Core.Database.Models;
- using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;
- using Edge.Core.Parser.BinaryParser.Util;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Xml;
- using Wayne.FDCPOSLibrary;
- using Global_Pump_Fdc.MessageEntity;
- using Global_Pump_Fdc.MessageEntity.Outgoing;
- using System.Collections.Specialized;
- using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;
- namespace Global_Pump_Fdc
- {
- public class PumpHandler : IFdcPumpController, IDeviceHandler<object, FccMessageBase>
- {
- static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("PumpHandler");
- private LogicalDeviceState lastLogicalDeviceState = LogicalDeviceState.FDC_OFFLINE;
- private DateTime lastLogicalDeviceStateReceivedTime;
- // by minutes, change this value need change the correlated deviceOfflineCountdownTimer's interval as well
- private const int lastLogicalDeviceStateExpiredTime = 3;
- private List<LogicalNozzle> nozzles = new List<LogicalNozzle>();
- private System.Timers.Timer deviceOfflineCountdownTimer;
- /// <summary>
- /// will set to true once nozzle change to OUT while internal fdc state is FDC_FUELING, indicates a fule is done.
- /// 'true' will block pump auth request from outer
- /// </summary>
- private bool lastFillRetrievedSinceNozzleDownAtFdcFueling = true;
- /// <summary>
- /// the pump calling, fuelling message does not contain nozzle info, need track the operating nozzle
- /// seperatly in other message which will be kept in this variable.
- /// default set to 0 which is an invalid nozzle id for wayne dart pump(valid should starts from 1).
- /// </summary>
- private byte operatingNozzlePhysicalId = 0;
- private IContext<object, FccMessageBase> context;
- private bool isOnFdcServerInitCalled = false;
- private int pumpId;
- private PumpGroupHandler parent;
- /// <summary>
- /// address used in wayne dart protocol to comm with physical pump.
- /// 0x4F + physical pump mother board side config address(range from 1-32)
- /// </summary>
- private byte dartPumpCommAddress;
- /// <summary>
- /// when first time connected with physical pump , in some case, the pump will not report any status actively,
- /// so need send a status query from FC.
- /// From then on, pump will actively notify FC when state changes, no need to send query anymore from FC.
- /// </summary>
- private bool initialPumpStatueEverRetrieved = false;
- private int amountDecimalDigits;
- private int volumeDecimalDigits;
- private int priceDecimalDigits;
- private int volumeTotalizerDecimalDigits;
- /// <summary>
- /// for avoid a case that FC may miss a pump status change event(pump side issue? or wire issue?),
- /// we timely actively request the pump state.
- /// </summary>
- //private System.Timers.Timer pollingPumpStatus;
- //private int pollingPumpStatusInterval = 30000;
- #region MyRegion
- public string Name => "Global_Pump_Fdc";
- public int PumpId => this.pumpId;
- /// <summary>
- /// Gets the pump physical id.
- /// address used in wayne dart protocol to comm with physical pump.
- /// 0x4F + physical pump mother board side config address(range from 1-32)
- /// </summary>
- public int PumpPhysicalId => this.dartPumpCommAddress;
- public IEnumerable<LogicalNozzle> Nozzles => this.nozzles;
- public int AmountDecimalDigits => this.amountDecimalDigits;
- public int VolumeDecimalDigits => this.volumeDecimalDigits;
- public int PriceDecimalDigits => this.priceDecimalDigits;
- public int VolumeTotalizerDecimalDigits => this.volumeDecimalDigits;
- public Guid Id => Guid.NewGuid();
- public event EventHandler<FdcPumpControllerOnStateChangeEventArg> OnStateChange;
- public event EventHandler<FdcTransactionDoneEventArg> OnCurrentFuellingStatusChange;
- public async Task<bool> AuthorizeAsync(byte logicalNozzleId)
- {
- if (!this.lastFillRetrievedSinceNozzleDownAtFdcFueling)
- {
- logger.Info("Pump: " + this.pumpId + ", "
- + "Start Authorize pump with logicalNozzle: " + logicalNozzleId + " is denied by internal");
- return false;
- }
- var authorizeSucceed = false;
- logger.Info("Pump: " + this.pumpId + ", " + "Start Authorize pump with logicalNozzle: " + logicalNozzleId + ", first send AllowedNozzleNumbersRequest(allow all)");
- // now always allow all nozzles
- var response = await this.context.Outgoing.WriteAsync(
- new AuthorizeRequest(this.pumpId, this.nozzles.First().LogicalId),
- (request, testResponse) =>
- {
- var parameters = testResponse.Parameters as StringDictionary;
- if (parameters == null || parameters["EventType"] != "AuthorizePump")
- return false;
- string pumpId = parameters["PumpID"];
- string success = parameters["Success"];
- authorizeSucceed = int.Parse(pumpId) == this.PumpId;
- logger.Info("Pump: " + pumpId + ", " + "AuthorizeWithAmountAsync for nozzle: " + logicalNozzleId +
- " , success: " + success);
- return authorizeSucceed;
- },
- 3500);
- return authorizeSucceed;
- }
- public async Task<bool> AuthorizeWithAmountAsync(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId)
- {
- if (!this.lastFillRetrievedSinceNozzleDownAtFdcFueling)
- {
- logger.Info("Pump: " + this.pumpId + ", "
- + "Start AuthorizeWithAmount pump with logicalNozzle: " + logicalNozzleId + " is denied by internal");
- return false;
- }
- moneyAmountWithoutDecimalPoint = (int)(moneyAmountWithoutDecimalPoint / Math.Pow(10, AmountDecimalDigits));
- logger.Info("Pump: " + this.pumpId + ", " + "start AuthorizeWithAmount pump with logicalNozzle: " + logicalNozzleId + ", moneyAmount: " +
- moneyAmountWithoutDecimalPoint + ", first send AllowedNozzleNumbersRequest(allow all)");
- var authorizeSucceed = false;
- // now always allow all nozzles
- var response = await this.context.Outgoing.WriteAsync(
- new PresetAmountRequest(this.pumpId, this.nozzles.First().LogicalId, moneyAmountWithoutDecimalPoint),
- (request, testResponse) =>
- {
- var parameters = testResponse.Parameters as StringDictionary;
- if (parameters == null || parameters["EventType"] != "AuthorizePump")
- return false;
- string pumpId = parameters["PumpID"];
- string success = parameters["Success"];
- authorizeSucceed = int.Parse(pumpId) == this.PumpId;
- logger.Info("Pump: " + pumpId + ", " + "AuthorizeWithAmountAsync for nozzle: " + logicalNozzleId +
- " , success: " + success);
- return authorizeSucceed;
- },
- 3500);
- return authorizeSucceed;
- }
- public async Task<bool> AuthorizeWithVolumeAsync(int volumnWithoutDecimalPoint, byte logicalNozzleId)
- {
- if (!this.lastFillRetrievedSinceNozzleDownAtFdcFueling)
- {
- logger.Info("Pump: " + this.pumpId + ", "
- + "Start AuthorizeWithVolumn pump with logicalNozzle: " + logicalNozzleId + " is denied by internal");
- return false;
- }
- var authorizeSucceed = false;
- logger.Info("Pump: " + this.pumpId + ", " + "start AuthorizeWithVolumn pump with logicalNozzle: " + logicalNozzleId + ", volumn: " + volumnWithoutDecimalPoint + ", first send AllowedNozzleNumbersRequest(allow all)");
- // now always allow all nozzles
- var response = await this.context.Outgoing.WriteAsync(
- new PresetVolumeRequest(this.pumpId, this.nozzles.First().LogicalId, volumnWithoutDecimalPoint),
- (request, testResponse) =>
- {
- var parameters = testResponse.Parameters as StringDictionary;
- if (parameters == null || parameters["EventType"] != "AuthorizePump")
- return false;
- string pumpId = parameters["PumpID"];
- string success = parameters["Success"];
- authorizeSucceed = int.Parse(pumpId) == this.PumpId;
- logger.Info("Pump: " + pumpId + ", " + "AuthorizeWithVolumn for nozzle: " + logicalNozzleId +
- " , success: " + success);
- return authorizeSucceed;
- },
- 3500);
- return authorizeSucceed;
- }
- public Task<bool> SetFuelPrice(int fuelGrade, double newFuelPrice)
- {
- return Task.FromResult(true);
- }
- public Task<bool> ChangeFuelPriceAsync(int newPriceWithoutDecimalPoint, byte logicalNozzleId)
- {
- try
- {
- if (newPriceWithoutDecimalPoint > 10000)
- {
- int fuelGrade = logicalNozzleId;
- double newFuelPrice = (double)(newPriceWithoutDecimalPoint - 10000) / 1000;
- return this.InternalChangeFuelPriceAsync(fuelGrade, newFuelPrice);
- }
- }
- catch (Exception exxx)
- {
- logger.Error("Pump: " + this.pumpId + ", " + "Exceptioned in ChangeFuelPrice: " + exxx);
- return Task.FromResult(false);
- }
- return Task.FromResult(true);
- }
- private async Task<bool> InternalChangeFuelPriceAsync(int fuelGrade, double newFuelPrice)
- {
- if (this.lastLogicalDeviceState == LogicalDeviceState.FDC_CLOSED
- || this.lastLogicalDeviceState == LogicalDeviceState.FDC_OFFLINE)
- {
- logger.Info("Pump: " + this.pumpId + ", " + " Pump is in state FDC_CLOSED or FDC_OFFLINE, InternalChangeFuelPrice will return false");
- return false;
- }
- bool changePriceSucceed = false;
- logger.Info("Pump: " + this.pumpId + ", " + " InternalChangeFuelPrice starting with prices: " + newFuelPrice.ToString());
- var priceChangedResponse = await this.context.Outgoing.WriteAsync(
- new PriceUpdateRequest(fuelGrade, newFuelPrice),
- null,
- 2000);
- // this FC handle WayneDart pump price change one nozzle by nozzle, so change prices on single FuelPoint with multiple
- // nozzle will interpreted as multiple price change request, by testing, too fast send multiple price change request
- // to a FP might be ignored by pump side though message here are all good, so hardcode sleep a while.
- Thread.Sleep(1000);
- return changePriceSucceed;
- }
- public async Task<bool> FuelingRoundUpByAmountAsync(int amount)
- {
- logger.Info("Pump: " + this.pumpId + ", " + "start FuelingRoundUpByAmount with amount: " + amount);
- var isSucceed = false;
- //this.context.Outgoing.WriteAsync(
- // new PresetAmountRequest(this.dartPumpCommAddress, this.parent.GetNewMessageToken(this.pumpId), amount),
- // (request, testResponse) =>
- // testResponse.Adrs == this.dartPumpCommAddress &&
- // testResponse.BlockSeqNumber == request.BlockSeqNumber,
- // (request, response) =>
- // {
- // if (response != null)
- // {
- // if (response.ControlCharacter == ControlCharacter.ACK)
- // {
- // logger.Debug("Pump: " + this.pumpId + ", " + "PresetAmountRequest ACKed");
- // isSucceed = true;
- // }
- // else
- // {
- // logger.Info("Pump: " + this.pumpId + ", " + "PresetAmountRequest NAKed");
- // isSucceed = false;
- // }
- // }
- // else
- // {
- // logger.Info("Pump: " + this.pumpId + ", " + "PresetAmountRequest waiting ACK timed out");
- // isSucceed = false;
- // }
- // }, 1500);
- return isSucceed;
- }
- public async Task<bool> FuelingRoundUpByVolumeAsync(int volume)
- {
- return false;
- }
- public async Task<global::Wayne.FDCPOSLibrary.LogicalDeviceState> QueryStatusAsync()
- {
- return this.lastLogicalDeviceState;
- }
- public async Task<System.Tuple<int, int>> QueryTotalizerAsync(byte logicalNozzleId)
- {
- var result = new System.Tuple<int, int>(-1, -1);
- try
- {
- logger.Info("Pump: " + this.pumpId + ", " + "start QueryTotalizer pump with nozzle: " + logicalNozzleId);
- if (this.lastLogicalDeviceState == LogicalDeviceState.FDC_CLOSED
- || this.lastLogicalDeviceState == LogicalDeviceState.FDC_OFFLINE)
- {
- logger.Info("Pump: " + this.pumpId + ", " + " Pump is in state FDC_CLOSED or FDC_OFFLINE, totalizer will return -1, -1");
- return new System.Tuple<int, int>(-1, -1);
- }
- var nozzlePhysicalId = this.nozzles.FirstOrDefault(n => n.LogicalId == logicalNozzleId)?.PhysicalId;
- if (nozzlePhysicalId == null)
- {
- logger.Info("Pump: " + this.pumpId + ", " + " Nozzle with logicalId: " + logicalNozzleId + " does not exists, totalizer will return -1, -1");
- return new System.Tuple<int, int>(-1, -1);
- }
- var response = await this.context.Outgoing.WriteAsync(
- new QueryTotalizerRequest(this.pumpId, logicalNozzleId),
- (request, testResponse) =>
- {
- var parameters = testResponse.Parameters as StringDictionary;
- if (parameters == null || parameters["EventType"] != "TotalizerReceived")
- return false;
- string volumeTotal = parameters["VolumeTotalizer"];
- string amountTotal = parameters["AmountTotalizer"];
- logger.Info("Pump: " + this.pumpId + ", " + "QueryTotalizer for nozzle: " + logicalNozzleId +
- " succeed, volume total: " + volumeTotal + " , amount total: " + amountTotal);
-
- int amountTotalizer = 0;
- var amountTotalizerOriginal = decimal.Parse(amountTotal) * (decimal)Math.Pow(10, AmountDecimalDigits);
- if (amountTotalizerOriginal > int.MaxValue)
- {
- amountTotalizer = Convert.ToInt32(amountTotalizerOriginal / 10);
- }
- else
- {
- amountTotalizer = (int)(decimal.Parse(amountTotal) * (decimal)Math.Pow(10, AmountDecimalDigits));
- }
- var volumeTotalizer = (int)(decimal.Parse(volumeTotal) * (decimal)Math.Pow(10, volumeTotalizerDecimalDigits));
- result = new System.Tuple<int, int>(amountTotalizer, volumeTotalizer);
- return true;
- },
- 2000);
- }
- catch (Exception ex)
- {
- logger.Error("Pump: " + this.pumpId + ", " + ex);
- }
- //var response = await this.context.Outgoing.WriteAsync(new RequestTotalVolumeCountersRequest(this.dartPumpCommAddress, this.parent.GetNewMessageToken(this.pumpId),
- // nozzlePhysicalId.Value),
- // (request, testResponse) =>
- // testResponse.Adrs == this.dartPumpCommAddress &&
- // testResponse.ControlCharacter == ControlCharacter.DATA, 3000);
- //if (response != null)
- //{
- // this.context.Outgoing.Write(new ACK(this.dartPumpCommAddress, response.BlockSeqNumber));
- // var totalCountersTrx =
- // new TotalCounters_TransactionData(response.TransactionDatas.First(f => f.TransactionNumber == 0x65));
- // logger.Info("Pump: " + this.pumpId + ", " + "QueryTotalizer for nozzle: " + logicalNozzleId + " succeed, volume total: " + totalCountersTrx.TotalValue);
- // result = new System.Tuple<int, int>(-1, totalCountersTrx.TotalValue);
- //}
- //else
- //{
- // logger.Error("Pump: " + this.pumpId + ", " + "QueryTotalizer waiting Data timed out");
- //}
- return result;
- }
- public async Task<bool> ResumeFuellingAsync()
- {
- throw new NotImplementedException();
- }
- public async Task<bool> SuspendFuellingAsync()
- {
- throw new NotImplementedException();
- }
- /// <summary>
- /// unauthorize the authed fueling point, will trigger wayne dart pump switched to `FILLING COMPLETE` state.
- /// </summary>
- /// <param name="logicalNozzleId">wayne dart no need specify nozzle id</param>
- /// <returns></returns>
- public async Task<bool> UnAuthorizeAsync(byte logicalNozzleId)
- {
- var unauthorizeSucceed = false;
- //logger.Info("Pump: " + this.pumpId + ", " + "Start UnAuthorize pump with nozzle: " + logicalNozzleId);
- //var authorizedResponse = await this.context.Outgoing.WriteAsync(
- // new StopRequest(this.dartPumpCommAddress, this.parent.GetNewMessageToken(this.pumpId)),
- // (_, testResponse) =>
- // testResponse.Adrs == this.dartPumpCommAddress &&
- // testResponse.BlockSeqNumber == _.BlockSeqNumber, 3500);
- //if (authorizedResponse != null)
- //{
- // if (authorizedResponse.ControlCharacter == ControlCharacter.ACK)
- // {
- // logger.Info("Pump: " + this.pumpId + ", " + "unAuthorizeRequest ACKed and succeed");
- // unauthorizeSucceed = true;
- // }
- // else
- // logger.Info("Pump: " + this.pumpId + ", " + "unAuthorizeRequest NAKed (rejected?)");
- //}
- //else
- //{
- // logger.Info("Pump: " + this.pumpId + ", " + "unAuthorizeRequest timed out");
- //}
- return unauthorizeSucceed;
- }
- public async Task<bool> LockNozzleAsync(byte logicalNozzleId)
- {
- return false;
- }
- public async Task<bool> UnlockNozzleAsync(byte logicalNozzleId)
- {
- return false;
- }
- #endregion
- public PumpHandler(PumpGroupHandler parent, int pumpId,
- int amountDecimalDigits, int volumeDecimalDigits,
- int priceDecimalDigits, int volumeTotalizerDecimalDigits,
- string pumpXmlConfiguration)
- {
- this.parent = parent;
- this.pumpId = pumpId;
- this.amountDecimalDigits = amountDecimalDigits;
- this.volumeDecimalDigits = volumeDecimalDigits;
- this.priceDecimalDigits = priceDecimalDigits;
- this.volumeTotalizerDecimalDigits = volumeTotalizerDecimalDigits;
- // sample of pumpXmlConfiguration
- // <Pump pumpId='1' physicalId='1'>
- // <Nozzles>
- // <Nozzle logicalId='1' physicalId='1' defaultNoDecimalPointPriceIfNoHistoryPriceReadFromDb='2345'/>
- // <Nozzle logicalId='2' physicalId='2' defaultNoDecimalPointPriceIfNoHistoryPriceReadFromDb='2345'/>
- // <Nozzle logicalId='3' physicalId='3' defaultNoDecimalPointPriceIfNoHistoryPriceReadFromDb='2345'/>
- // </Nozzles>
- // </Pump>
- var xmlDocument = new XmlDocument();
- xmlDocument.LoadXml(pumpXmlConfiguration);
- var physicalPumpAddressConfiguratedInPump = byte.Parse(xmlDocument.SelectSingleNode("/Pump").Attributes["physicalId"].Value);
- if (physicalPumpAddressConfiguratedInPump > 0x20)
- throw new ArgumentOutOfRangeException("Wayne dart pump only accept pump address range from 1 to 32, make sure this value is correctly configurated in physical pump mother board");
- this.dartPumpCommAddress = (byte)(0x4F + physicalPumpAddressConfiguratedInPump);
- foreach (var nozzleElement in xmlDocument.GetElementsByTagName("Nozzle").Cast<XmlNode>())
- {
- var nozzlePhysicalId = byte.Parse(nozzleElement.Attributes["physicalId"].Value);
- var nozzleLogicalId = byte.Parse(nozzleElement.Attributes["logicalId"].Value);
- var nozzleRawDefaultPriceWithoutDecimal = nozzleElement.Attributes["defaultNoDecimalPointPriceIfNoHistoryPriceReadFromDb"].Value;
- if (nozzlePhysicalId < 1 || nozzlePhysicalId > 8)
- throw new ArgumentOutOfRangeException("Wayne dart pump only accept nozzle physical id range in config from 1 to 8");
- this.nozzles.Add(new LogicalNozzle(pumpId, nozzlePhysicalId, nozzleLogicalId, null) { ExpectingPriceOnFcSide = int.Parse(nozzleRawDefaultPriceWithoutDecimal) });
- logger.Info("Pump: " + this.pumpId
- + ", created a nozzle with logicalId: " + nozzleLogicalId + ", physicalId: " + nozzlePhysicalId
- + ", default raw price without decimal points: " + nozzleRawDefaultPriceWithoutDecimal);
- }
- this.deviceOfflineCountdownTimer = new System.Timers.Timer(10000);
- this.deviceOfflineCountdownTimer.Elapsed += (_, __) =>
- {
- if (DateTime.Now.Subtract(this.lastLogicalDeviceStateReceivedTime).TotalMinutes
- >= lastLogicalDeviceStateExpiredTime)
- {
- this.initialPumpStatueEverRetrieved = false;
- if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_OFFLINE)
- {
- this.lastLogicalDeviceState = LogicalDeviceState.FDC_OFFLINE;
- logger.Info("Pump: " + this.pumpId + ", " + " State switched to FDC_OFFLINE due to long time no see pump data incoming");
- var safe0 = this.OnStateChange;
- safe0?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_OFFLINE));
- logger.Trace("Pump: " + this.pumpId + ", " + " OnStateChange event fired and back");
- }
- }
- };
- this.deviceOfflineCountdownTimer.Start();
- }
- public void OnFdcServerInit(Dictionary<string, object> parameters)
- {
- /* Wayne Dart pump will miss last price when disconnected or power off
- * from FC a while(pump state `PUMP NOT PROGRAMMED` indicates this happened), so here
- * is trying to recover the price from Fdc database, and then push to Pump*/
- if (parameters.ContainsKey("LastPriceChange"))
- {
- // nozzle logical id:rawPrice
- var lastPriceChanges = parameters["LastPriceChange"] as Dictionary<byte, int>;
- foreach (var priceChange in lastPriceChanges)
- {
- logger.Info("Pump: " + this.pumpId + ", " + "Pump " + this.pumpId + " OnFdcServerInit, load last price change " +
- "on logical nozzle: " + priceChange.Key + " with price: " + priceChange.Value);
- this.nozzles.First(n => n.LogicalId == priceChange.Key).ExpectingPriceOnFcSide = priceChange.Value;
- }
- }
- /* Load Last sale trx(from db) for void the case of FC accidently disconnect from Pump in fueling,
- and may cause a fueling trx gone from FC control */
- if (parameters.ContainsKey("LastFuelSaleTrx"))
- {
- // nozzle logical id:LastSale
- var lastFuelSaleTrxes = parameters["LastFuelSaleTrx"] as Dictionary<byte, FuelSaleTransaction>;
- foreach (var lastFuelSaleTrx in lastFuelSaleTrxes)
- {
- logger.Info("Pump: " + this.pumpId + ", OnFdcServerInit, load last volume Totalizer " +
- "on logical nozzle: " + lastFuelSaleTrx.Key + " with volume value: " + lastFuelSaleTrx.Value.VolumeTotalizer);
- this.nozzles.First(n => n.LogicalId == lastFuelSaleTrx.Key).VolumeTotalizer = lastFuelSaleTrx.Value.VolumeTotalizer;
- }
- }
- this.isOnFdcServerInitCalled = true;
- }
- public void Init(IContext<object, FccMessageBase> context)
- {
- this.context = context;
- this.context.Communicator.OnConnected += (a, b) =>
- {
- //this.context.Outgoing.Write(new ReturnStatusRequest(this.dartPumpCommAddress, 0));
- };
- //this.context.Communicator.OnDisconnected += (a, b) => this.pumpStatusEverReceived = false;
- }
- public Task Process(IContext<object, FccMessageBase> context)
- {
- var parameters = context.Incoming.Message.Parameters as StringDictionary;
- if (parameters == null)
- {
- logger.Error("Read invalid FCC message" + context.Incoming.Message.Parameters);
- return Task.CompletedTask;
- }
- this.lastLogicalDeviceStateReceivedTime = DateTime.Now;
- string eventType = parameters["EventType"];
- if (eventType == "FuellingStatusChange")
- {
- var logicalHoseId = byte.Parse(parameters["ho"]);
- var barcode = int.Parse(parameters["GR"]);
- var currentQty = double.Parse(parameters["VO"]) * Math.Pow(10, VolumeDecimalDigits);
- var currentAmount = decimal.Parse(parameters["AM"]) * Convert.ToDecimal(Math.Pow(10, AmountDecimalDigits));
- var currentPrice = double.Parse(parameters["PU"]) * Math.Pow(10, PriceDecimalDigits);
- var finished = parameters["Finished"] == "true" ? true : false;
- var paid = parameters["State"] == "PayableTransaction" ? false : true;
- var sequenceNo = int.Parse(parameters["FuelingSequenceNo"]);
- foreach (var item in parameters.Keys)
- {
- logger.Debug($"key: {item}, value: {parameters[(string)item]}");
- }
- logger.Debug(string.Format("currentQty: {0}, currentAmount: {1}, paid: {2}", currentQty, currentAmount, paid));
- if (paid) return Task.CompletedTask;
- var safe = this.OnCurrentFuellingStatusChange;
- safe?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction()
- {
- Nozzle = this.nozzles.First(n => n.LogicalId == logicalHoseId),
- Barcode = barcode,
- Amount = (int)currentAmount,
- Volumn = (int)currentQty,
- Price = Convert.ToInt32(currentPrice),
- Finished = finished,
- SequenceNumberGeneratedOnPhysicalPump = sequenceNo
- }));
- }
- else if (eventType == "PumpStatusChange")
- {
- LogicalNozzle ln = null;
- var pumpStatusStr = parameters["ST"];
- if (pumpStatusStr == "ERROR")
- {
- this.lastLogicalDeviceState = LogicalDeviceState.FDC_OFFLINE;
- }
- else if (pumpStatusStr == "IDLE")
- {
- this.lastLogicalDeviceState = LogicalDeviceState.FDC_READY;
- }
- else if (pumpStatusStr == "CALLING")
- {
- var logicalHoseId = byte.Parse(parameters["ho"]);
- ln = new LogicalNozzle(PumpId, logicalHoseId, logicalHoseId, null);
- this.lastLogicalDeviceState = LogicalDeviceState.FDC_CALLING;
- }
- else if (pumpStatusStr == "AUTHORIZED")
- {
- if (parameters.ContainsKey("ho"))
- {
- var logicalHoseId = byte.Parse(parameters["ho"]);
- ln = new LogicalNozzle(PumpId, logicalHoseId, logicalHoseId, null);
- }
- this.lastLogicalDeviceState = LogicalDeviceState.FDC_AUTHORISED;
- }
- else if (pumpStatusStr == "FUELLING")
- {
- var logicalHoseId = byte.Parse(parameters["ho"]);
- ln = new LogicalNozzle(PumpId, logicalHoseId, logicalHoseId, null);
- this.lastLogicalDeviceState = LogicalDeviceState.FDC_FUELLING;
- }
- else
- {
- this.lastLogicalDeviceState = LogicalDeviceState.FDC_FUELLING;
- }
- var safe = this.OnStateChange;
- var e = ln == null ? new FdcPumpControllerOnStateChangeEventArg(this.lastLogicalDeviceState) : new FdcPumpControllerOnStateChangeEventArg(this.lastLogicalDeviceState, ln);
- safe?.Invoke(this, e);
- }
- else
- { }
- return Task.CompletedTask;
- }
- private void UnAuthorizePumpAndSwithToFdcReady()
- {
- //this.context.Outgoing.WriteAsync(
- // new StopRequest(this.dartPumpCommAddress, this.parent.GetNewMessageToken(this.pumpId)),
- // (request, testResponse) =>
- // testResponse.Adrs == this.dartPumpCommAddress &&
- // testResponse.BlockSeqNumber == request.BlockSeqNumber,
- // (request, response) =>
- // {
- // if (response != null)
- // {
- // if (response.ControlCharacter == ControlCharacter.ACK)
- // {
- // if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_READY)
- // {
- // logger.Debug("Pump: " + this.pumpId + ", " + " State switched to FDC_READY due to StopRequest acked.");
- // this.lastLogicalDeviceState = LogicalDeviceState.FDC_READY;
- // var safe3 = this.OnStateChange;
- // safe3?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_READY));
- // logger.Trace("Pump: " + this.pumpId + ", " + " OnStateChange event fired and back");
- // return;
- // }
- // }
- // else
- // logger.Error("Pump: " + this.pumpId + ", " + "StopRequest NAKed.");
- // }
- // else
- // logger.Error("Pump: " + this.pumpId + ", " + "StopRequest request timed out for ack.");
- // if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_READY)
- // {
- // logger.Debug("Pump: " + this.pumpId + ", " + " State switched to FDC_READY though StopRequest failed");
- // this.lastLogicalDeviceState = LogicalDeviceState.FDC_READY;
- // var safe2 = this.OnStateChange;
- // safe2?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_READY));
- // logger.Trace("Pump: " + this.pumpId + ", " + " OnStateChange event fired and back");
- // }
- // }, 1500);
- }
- private System.Timers.Timer retryReadLastFillTimer;
- private int onReadingLastFill = 0;
- /// <summary>
- /// may get called via 2 route:
- /// 1. received a Filling_Complete from pump.
- /// 2. by a delay timer that fired by nozzle IN from FDC_FUELING state.
- /// </summary>
- /// <param name="nozzlePhysicalIdForTrxDoneOn"></param>
- private void LoopReadLastFill(byte nozzlePhysicalIdForTrxDoneOn)
- {
- if (0 == Interlocked.CompareExchange(ref this.onReadingLastFill, 1, 0))
- {
- this.retryReadLastFillTimer?.Stop();
- this.retryReadLastFillTimer?.Dispose();
- // disable auth since last fill has not been read yet, this happens in real site with bad wire connection condition.
- //this.lastFillRetrieved = false;
- this.ReadLastFillAndUpdateLocalTotalizerAndFireFdcTrxDoneEvent(nozzlePhysicalIdForTrxDoneOn, (t) =>
- {
- if (t != null) { this.lastFillRetrievedSinceNozzleDownAtFdcFueling = true; this.onReadingLastFill = 0; return; }
- logger.Info("Pump: " + this.pumpId + ", " + " Read Last Fill failed on physicalNozzle: " + nozzlePhysicalIdForTrxDoneOn + ", will retry 1st time");
- this.ReadLastFillAndUpdateLocalTotalizerAndFireFdcTrxDoneEvent(nozzlePhysicalIdForTrxDoneOn, (tt) =>
- {
- if (tt != null) { this.lastFillRetrievedSinceNozzleDownAtFdcFueling = true; this.onReadingLastFill = 0; return; }
- logger.Info("Pump: " + this.pumpId + ", " + " Read Last Fill failed on physicalNozzle: " + nozzlePhysicalIdForTrxDoneOn + ", will retry 2nd time");
- this.ReadLastFillAndUpdateLocalTotalizerAndFireFdcTrxDoneEvent(nozzlePhysicalIdForTrxDoneOn, (ttt) =>
- {
- if (ttt != null) { this.lastFillRetrievedSinceNozzleDownAtFdcFueling = true; this.onReadingLastFill = 0; return; }
- logger.Info("Pump: " + this.pumpId + ", " + " Read Last Fill failed on physicalNozzle: " + nozzlePhysicalIdForTrxDoneOn + ", will retry 3rd time");
- this.ReadLastFillAndUpdateLocalTotalizerAndFireFdcTrxDoneEvent(nozzlePhysicalIdForTrxDoneOn, (tttt) =>
- {
- if (tttt != null) { this.lastFillRetrievedSinceNozzleDownAtFdcFueling = true; this.onReadingLastFill = 0; return; }
- logger.Error("Pump: " + this.pumpId + ", " + " Read Last Fill failed on physicalNozzle: " + nozzlePhysicalIdForTrxDoneOn + ", will NOT retry anymore (total read 4 times)");
- // Have no way but release it to allow continue pump auth and fueling, But a fuel sale has been lost!
- this.onReadingLastFill = 0;
- this.lastFillRetrievedSinceNozzleDownAtFdcFueling = true;
- });
- });
- });
- });
- }
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="targetNozzlePhysicalId"></param>
- /// <param name="callback">FdcTransaction is null when retrieved failed, like time out. otherwise, an object will return</param>
- private void ReadLastFillAndUpdateLocalTotalizerAndFireFdcTrxDoneEvent(byte targetNozzlePhysicalId,
- Action<FdcTransaction> callback)
- {
- }
- }
- }
|