using Edge.Core.Database;
using Edge.Core.Processor;
using Edge.Core.IndustryStandardInterface.Pump;
using HengShan_Pump_TQC_IFSF.MessageEntity;
using HengShan_Pump_TQC_IFSF.MessageEntity.Incoming;
using HengShan_Pump_TQC_IFSF.MessageEntity.Outgoing;
using Edge.Core.Parser.BinaryParser.Util;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Wayne.FDCPOSLibrary;
using Timer = System.Timers.Timer;
using Edge.Core.Processor.Dispatcher.Attributes;
using Edge.Core.Processor.Communicator;
using Edge.Core.Configuration;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
namespace HengShan_Pump_TQC_IFSF
{
///
/// one TQC connection controls a full physical pump, typically 2 sides.
///
[MetaPartsRequired(typeof(GenericDeviceProcessor<,>))]
[MetaPartsRequired(typeof(TcpClientCommunicator<>))]
[MetaPartsRequired(typeof(ComPortCommunicator<>))]
[MetaPartsRequired(typeof(IfsfMessageBase))]
[MetaPartsDescriptor(
"lang-zh-cn:LonWorks HS TQC加油机lang-en-us:LonWorks HS TQC pump",
"lang-zh-cn:用于驱动基于 LonWorks Card 连接的IFSF协议的 稳牌-WT30 或 恒山-TH22 加油机lang-en-us:Used for driven Wayne-WT30 or HengShan-TH22 dispensers which using IFSF protocol on LonWorks Card",
new[] { "lang-zh-cn:加油机lang-en-us:Pump" })]
public class LonWorksPumpGroupHandler : IEnumerable, IDeviceHandler, IDisposable
{
#region Ctor parameters
public class PumpIfsfNodeGroupConfig
{
//public byte AmountDecimalDigits { get; set; }
//public byte VolumeDecimalDigits { get; set; }
//public byte PriceDecimalDigits { get; set; }
//public byte VolumeTotalizerDecimalDigits { get; set; }
public byte FccIfsfSubnetValue { get; set; }
public byte FccIfsfNodeValue { get; set; }
public List IfsfNodeConfigs { get; set; }
}
public class IfsfNodeConfig
{
///
///
public byte IfsfSubnetValue { get; set; }
public byte IfsfNodeValue { get; set; }
public List IfsfFpConfigs { get; set; }
}
public class IfsfFpConfig
{
public byte PumpId { get; set; }
///
/// 0x21 - 0x24
///
public byte PhysicalId { get; set; }
public List IfsfNozzleConfigs { get; set; }
}
public class IfsfNozzleConfig
{
public byte LogicalId { get; set; }
///
/// 0x11 - 0x18
///
public byte PhysicalId { get; set; }
}
#endregion
private IServiceProvider services;
private static ILogger logger = NullLogger.Instance;
private Timer fccHeartbeatSendingTimer;
private Timer pumpIfsfNodeOfflineCheckTimer;
///
/// treat pump ifsf node to offline if this time period have not recieve any of its message.
///
private int pumpIfsfNodeOfflineTimeThresholdByMs = 25000;
public PumpIfsfNodeGroupConfig deviceConfig;
private object syncObject = new object();
protected IEnumerable nozzleExtraInfos;
private Guid uniqueId = Guid.NewGuid();
internal List tqcPumpGroupInitializers;
//protected static ILog logger = log4net.LogManager.GetLogger("PumpHandler");
protected IContext context;
protected List pumpHandlers = new List();
//// for test, the tqc configured as subnet 1, node 1.
//protected byte recipientSubnet = 1;
//protected byte recipientNode = 1;
//// originator, it's the FC self.
public static byte originatorSubnet = 2;
public static byte originatorNode = 1;
public LonWorksPumpGroupHandler(PumpIfsfNodeGroupConfig pumpIfsfNodeGroupConfig, IServiceProvider services)
{
this.services = services;
var loggerFactory = services.GetRequiredService();
logger = loggerFactory.CreateLogger("PumpHandler");
this.deviceConfig = pumpIfsfNodeGroupConfig;
originatorSubnet = pumpIfsfNodeGroupConfig.FccIfsfSubnetValue;
originatorNode = pumpIfsfNodeGroupConfig.FccIfsfNodeValue;
//this.recipientSubnet = pumpIfsfNodeGroupConfig.IfsfSubnetValue;
//this.recipientNodes.AddRange(pumpIfsfNodeGroupConfig.IfsfNodeConfigs.Select(pc => pc.PumpIfsfNodeValue));
//logger.LogInformation("node " + this.recipientNodes.Select(n => n.ToString()).Aggregate((acc, n) => acc + ", " + n) + ", Will create " + pumpIfsfNodeGroupConfig.IfsfNodeConfigs.Count
// + " pump handlers for this TQC connection according from pumpGroupConfiguration");
if (pumpIfsfNodeGroupConfig.IfsfNodeConfigs.SelectMany(nc => nc.IfsfFpConfigs).Any(fpConfig => fpConfig.PhysicalId < 0x21 || fpConfig.PhysicalId > 0x24))
throw new ArgumentException("Ifsf fuel point id must be range from 0x21 to 0x24");
foreach (var nodeConfig in pumpIfsfNodeGroupConfig.IfsfNodeConfigs)
{
var initor = new TqcPumpGroupInitializer(originatorSubnet, originatorNode,
nodeConfig.IfsfSubnetValue, nodeConfig.IfsfNodeValue, this.pumpHandlers, this.GetNewMessageToken);
initor.OnInitTimeout += (cc, dd) =>
{
logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + ", init timed out, will start it over...");
initor.Reset();
initor.FeedIn(this.context);
};
initor.OnInitDone += (ee, ff) =>
{
logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + ", init done, will query all FP status and routed following msg to PumpHandlers");
// query all FP status, answer will be routed to each PumpHandler to handle.
this.context.Outgoing.Write(
new FuellingPointDbRequest_Read_FuelPointState(nodeConfig.IfsfSubnetValue,
nodeConfig.IfsfNodeValue,
originatorSubnet,
originatorNode,
this.GetNewMessageToken(),
0x20));
initor.pumpHandlers.SelectMany(p => p.Nozzles).ToList().ForEach(n =>
{
var boundProductNo = this.nozzleExtraInfos.First(c => c.PumpId == n.PumpId && c.NozzleLogicalId == n.LogicalId).ProductBarcode;
logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode
+ ", will write back price to nozzle on pump " + n.PumpId + ", logicalNozzleId: " + n.LogicalId + ", boundProductNo: " + boundProductNo
+ " to new Init Price(without decimal points): " + initor.PriceBook[boundProductNo]);
n.RealPriceOnPhysicalPump = initor.PriceBook[boundProductNo];
});
};
this.tqcPumpGroupInitializers.Add(initor);
foreach (var fpConfig in nodeConfig.IfsfFpConfigs)
{
var pumpHandler = new LonWorksPumpHandler(initor,
fpConfig.PumpId, fpConfig.PhysicalId,
nodeConfig.IfsfSubnetValue, nodeConfig.IfsfNodeValue,
fpConfig.IfsfNozzleConfigs, GetNewMessageToken);
this.pumpHandlers.Add(pumpHandler);
}
}
}
///
/// will be called at the Init stage of FdcServerApp, that means before the calling the Start() for all the Processors.
///
///
public void OnFdcServerInit(Dictionary parameters)
{
if (parameters != null && parameters.TryGetValue("NozzleProductMapping", out object param))
{
this.nozzleExtraInfos = param as IEnumerable;
}
this.pumpHandlers.ForEach(ph => ph.OnFdcServerInit(parameters));
}
public virtual void Init(IContext context)
{
this.context = context;
bool isCommConnected = false;
this.context.Communicator.OnConnected += async (e, f) =>
{
logger.LogInformation("TqcPumpGroupInitializer, Communicator Connected, will start init each Initializer...");
isCommConnected = true;
foreach (var initor in this.tqcPumpGroupInitializers)
{
while (isCommConnected)
{
initor.Reset();
if (initor.CurrentStatus == TqcPumpGroupInitializer.Status.UnInit)
initor.FeedIn(this.context);
else
logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + " is already in a initing process, just wait for finish...");
// may refine in future for wait the init Done event.
var initResult = await initor.InitDoneTaskCompletionSource.Task;
if (initResult)
{
logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + ", init done, will query all FP status and routed following msg to PumpHandlers");
// query all FP status, answer will be routed to each PumpHandler to handle.
this.context.Outgoing.Write(
new FuellingPointDbRequest_Read_FuelPointState(
initor.ifsfRecipientSubnet,
initor.ifsfRecipientNode,
originatorSubnet,
originatorNode,
this.GetNewMessageToken(),
0x20));
//var nozzleProductConfig
// = Configurator.Default.NozzleExtraInfoConfiguration.Mapping;
initor.pumpHandlers.SelectMany(p => p.Nozzles).ToList().ForEach(n =>
{
var boundProductNo = this.nozzleExtraInfos.First(c => c.PumpId == n.PumpId && c.NozzleLogicalId == n.LogicalId).ProductBarcode;
logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode
+ ", will write back price to nozzle on pump " + n.PumpId + ", logicalNozzleId: " + n.LogicalId + ", boundProductNo: " + boundProductNo
+ " to new Init Price(without decimal points): " + initor.PriceBook[boundProductNo]);
n.RealPriceOnPhysicalPump = initor.PriceBook[boundProductNo];
});
break;
}
else
{
logger.LogInformation("TqcPumpGroupInitializer, node " + initor.ifsfRecipientNode + ", init timed out, will start it over...");
//initor.Reset();
//initor.FeedIn(this.context);
}
}
}
};
this.pumpHandlers.ForEach(p => p.Init(this.context));
//accuracy is 1000ms
this.pumpIfsfNodeOfflineCheckTimer = new Timer(1000);
this.pumpIfsfNodeOfflineCheckTimer.Elapsed += async (a, b) =>
{
var offlinePumps = this.pumpHandlers.Where(b => DateTime.Now.Subtract(b.LastIncomingMsgReceivedTime ?? DateTime.MinValue).TotalMilliseconds >= pumpIfsfNodeOfflineTimeThresholdByMs);
if (offlinePumps.Any())
{
logger.LogError($"Long time no see any msg incoming for {offlinePumps.Select(p => $"Pump {p.PumpId}").Aggregate((acc, n) => acc + ", " + n)}, will trigger them to Fdc_Offline state.");
}
foreach (var p in offlinePumps)
{
p.HandleFpStatusChange(FuellingPointStatus.Inoperative, null);
}
};
this.pumpIfsfNodeOfflineCheckTimer.Start();
this.fccHeartbeatSendingTimer = new Timer(10000);
this.fccHeartbeatSendingTimer.Elapsed += async (a, b) =>
{
context.Outgoing.Write(new LonWorksHeartbeat(1, originatorSubnet, originatorNode));
};
this.fccHeartbeatSendingTimer.Start();
}
private byte rotateMsgToken = 0;
public byte GetNewMessageToken()
{
if (rotateMsgToken == (0xFF & 0x01F))
rotateMsgToken = 0;
else
rotateMsgToken++;
return rotateMsgToken;
}
public virtual Task Process(IContext context)
{
this.context = context;
var initor = this.tqcPumpGroupInitializers?.FirstOrDefault(i => i.ifsfRecipientNode == context.Incoming.Message.OriginatorNode);
if (initor == null)
{
// must has initor created first
return Task.CompletedTask;
}
if (!initor.IsInitDone)
{
initor.FeedIn(context);
return Task.CompletedTask;
}
this.RouteMessageToHandlers(context);
return Task.CompletedTask;
}
protected virtual void RouteMessageToHandlers(IContext context)
{
switch (context.Incoming.Message)
{
case FuellingPointDb_FpStatus_Event fpStatusEvent:
this.pumpHandlers.Where(h => h.PumpPhysicalId == fpStatusEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
break;
case FuellingPointDb_FpStatus_Answer fpStatusAnswer:
this.pumpHandlers.Where(h => h.PumpPhysicalId == fpStatusAnswer.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
break;
case FuellingPointDb_FpRunningTransaction_Event fpRunningTrxEvent:
this.pumpHandlers.Where(h => h.PumpPhysicalId == fpRunningTrxEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
break;
case FuellingPointDb_FpRunningTransaction_Event_ACK fpRunningTrxEvent:
this.pumpHandlers.Where(h => h.PumpPhysicalId == fpRunningTrxEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
break;
case FuellingTrxDb_TransactionBufferStatus_Event trxBufferStatusEvent:
this.pumpHandlers.Where(h => h.PumpPhysicalId == trxBufferStatusEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
break;
case FuellingTrxDb_TransactionBufferStatus_Event_ACK trxBufferStatusEvent:
this.pumpHandlers.Where(h => h.PumpPhysicalId == trxBufferStatusEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
break;
case FuellingTrxDb_TransactionBufferStatus_Answer trxBufferStatusAnswer:
this.pumpHandlers.Where(h => h.PumpPhysicalId == trxBufferStatusAnswer.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
break;
//default: this.pumpHandlers.ForEach(p => p.Process(context)); break;
}
}
public IEnumerator GetEnumerator()
{
return this.pumpHandlers.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.pumpHandlers.GetEnumerator();
}
public Guid Id => this.uniqueId;
///
/// Initalizer for init default values into TQC pump.
/// especially for default product price: it will read pre-config values for each product in pump first,
///
public class TqcPumpGroupInitializer
{
System.Timers.Timer initTimeoutTimer = new Timer(initTimeout);
const int initTimeout = 10000;
///
/// will fire when reached timed out time, and state is still not Done.
///
public event EventHandler OnInitTimeout;
///
/// false indicates init timed out, true indicates init done successfully.
///
public TaskCompletionSource InitDoneTaskCompletionSource = new TaskCompletionSource();
public event EventHandler OnInitDone;
public event EventHandler OnInitError;
///
/// init process will firstly read all necessary config values in TQC, and then execute the write operations.
/// this event will be fired once those config values were read, and write operation is still not perform.
///
public event EventHandler OnTqcExistedConfigRead;
private List fpStatus_Answers
= new List();
private List logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers
= new List();
///
/// Gets pre-config value in TQC pump.
///
public IEnumerable LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers => this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers;
private List productDb_ProductNo_Answers
= new List();
///
/// Gets pre-config value in TQC pump.
///
public IEnumerable ProductDb_ProductNo_Answers => this.productDb_ProductNo_Answers;
private List productPerFuellingModeDb_ProductPrice_Answers
= new List();
///
/// Gets pre-config value in TQC pump.
///
public IEnumerable ProductPerFuellingModeDb_ProductPrice_Answers => this.productPerFuellingModeDb_ProductPrice_Answers;
private Dictionary priceBook = new Dictionary();
///
/// Gets or set the init price for each product.
/// default price init policy is read from pricebook, if found then apply, otherwise use pre-config value read from pump.
/// should follow-> barcode:rawFormatPriceWithoutDecimalPoints
///
public Dictionary PriceBook => this.priceBook;
private IEnumerable nozzleProductConfig;
public IEnumerable NozzleProductConfig
{
get
{
return this.nozzleProductConfig;
}
set
{
this.nozzleProductConfig = value;
this.thisTqcProductBarcodes = this.nozzleProductConfig.Where(n => this.pumpHandlers.Select(p => p.PumpId).Contains(n.PumpId)).Select(s => s.ProductBarcode).Distinct().OrderBy(o => o).ToList();
//this.thisTqcProductBarcodes = nozzleProductConfig.Select(s => s.ProductBarcode).Distinct().OrderBy(o => o).ToList();
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " current TQC local configuration indicates total have "
+ this.thisTqcProductBarcodes.Count + " product barcodes: " + this.thisTqcProductBarcodes.Select(s => s.ToString()).Aggregate((acc, n) => acc + ", " + n));
}
}
//= Configurator.Default.NozzleProductConfiguration.Mapping;
private byte ifsfSelfSubnet;
private byte ifsfSelfNode;
public byte ifsfRecipientSubnet;
public byte ifsfRecipientNode;
public IEnumerable pumpHandlers;
Func msgTokenGenerator;
private List thisTqcProductBarcodes;
private Status currentStatus;
public Status CurrentStatus
{
get { return this.currentStatus; }
set
{
//logger.Debug("PumpInitializer, node " + ifsfRecipientNode + ", Status switched from " + this.currentStatus + " to " + value);
this.currentStatus = value;
}
}
//private DateTime previousRequestingTime;
private byte? pendingForAckMsgToken;
public enum Status
{
UnInit = 0,
Wait_Answer_Query_All_FuelPointState,
Wait_Answer_Close_All_FuelPoint,
Wait_Answer_Query_Caculator_Overall_Info,
Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId,
Wait_Answer_Read_All_ProductNumber,
Wait_Answer_Read_All_ProductPrice,
//Sending_Add_Recipient_Addr,
Wait_ACK_Add_Recipient_Addr,
//Sending_Clear_ProductNumber_In_Caculator,
Wait_ACK_Clear_ProductNumber_In_ProductDb,
//Sending_Set_ProductNumber_In_Caculator,
Wait_ACK_Set_ProductNumber_In_ProductDb,
//Sending_Link_Meter_To_CaculatorSlot,
Wait_ACK_Link_Meter_To_ProductDb_ProductNoSlot,
//Sending_Set_AUTH_MODE,
Wait_ACK_Set_AUTH_MODE,
//Sending_Set_Price,
Wait_ACK_Set_Price,
//Sending_Set_Nozzle_To_CacalatorSlot,
Wait_ACK_Set_Nozzle_To_ProductDb_ProductNoSlot,
//Sending_Set_FP_Default_FuellingMode,
Wait_ACK_Set_FP_Default_FuellingMode,
//Send_Open_FP,
//Wait_ACK_Open_FP,
Done
}
///
///
///
/// fc ifsf subnet value
/// fc ifsf node value
/// remote ifsf recipient subnet value
/// remote ifsf recipient node value
/// pump handlers created from the pump Group handler
///
public TqcPumpGroupInitializer(byte selfSubnet, byte selfNode, byte ifsfRecipientSubnet, byte ifsfRecipientNode,
IEnumerable pumpHandlers, Func msgTokenGenerator)
{
this.ifsfSelfSubnet = selfSubnet;
this.ifsfSelfNode = selfNode;
this.ifsfRecipientSubnet = ifsfRecipientSubnet;
this.ifsfRecipientNode = ifsfRecipientNode;
this.pumpHandlers = pumpHandlers;
this.msgTokenGenerator = msgTokenGenerator;
this.initTimeoutTimer.Elapsed += (a, b) =>
{
this.initTimeoutTimer.Stop();
if (!this.IsInitDone)
{
this.InitDoneTaskCompletionSource.SetResult(false);
var safe = this.OnInitTimeout;
safe(this, null);
}
};
}
private byte next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
private byte next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
//private byte next_Link_Meter_to_ProductNumber_Index = 1;
private byte next_site_nozzle_Index = 1;
private byte next_Fp_Index = 1;
///
/// if it was Done, return false. otherwise, the init is kicking off, return true.
///
///
///
public bool FeedIn(IContext context)
{
if (!this.initTimeoutTimer.Enabled) this.initTimeoutTimer.Start();
if (IsInitDone) return false;
switch (this.CurrentStatus)
{
case Status.UnInit:
{
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " start Init, firstly Query&Close all FuelPoints...");
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Send QueryAllFp state(first time)");
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new FuellingPointDbRequest_Read_FuelPointState(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value, 0x20
));
this.CurrentStatus = Status.Wait_Answer_Query_All_FuelPointState;
break;
}
case Status.Wait_Answer_Query_All_FuelPointState:
{
if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
&& ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
{
var historyIncoming = context.Incoming as HistoryKeepIncoming;
this.fpStatus_Answers = historyIncoming.History.Where(h => (h.Item1 is FuellingPointDb_FpStatus_Answer im)
&& im.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
&& im.MessageToken == this.pendingForAckMsgToken)
.Select(s => (FuellingPointDb_FpStatus_Answer)s.Item1).ToList();
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + ", read total " + fpStatus_Answers.Count
+ " Fp status, they're " + this.fpStatus_Answers.Select(s => "Fp 0x" + s.TargetFuelPointId.ToHexLogString() + " is " + s.FuelPointState)
.Aggregate((n, acc) => n + ", " + acc));
this.pendingForAckMsgToken = this.msgTokenGenerator();
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Send CloseFuelPoint(first time) for FP: 0x"
+ this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString());
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new FuellingPointDbRequest_Write_CloseFuelPoint(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId)
));
this.CurrentStatus = Status.Wait_Answer_Close_All_FuelPoint;
}
break;
}
case Status.Wait_Answer_Close_All_FuelPoint:
{
if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
&& ifsfMsg.MessageToken == this.pendingForAckMsgToken)
{
if (++next_Fp_Index <= this.pumpHandlers.Count())
{
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " CloseFuelPoint Acked");
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Send CloseFuelPoint for FP: 0x"
+ this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString());
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new FuellingPointDbRequest_Write_CloseFuelPoint(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId)
));
this.CurrentStatus = Status.Wait_Answer_Close_All_FuelPoint;
}
else
{
this.next_Fp_Index = 1;
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending CaculatorDbRequest_Read_FuelPoint_Product_FuelMode_Meter_Info...");
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new CaculatorDbRequest_Read_FuelPoint_Product_FuelMode_Meter_Info(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value
));
this.CurrentStatus = Status.Wait_Answer_Query_Caculator_Overall_Info;
}
}
break;
}
case Status.Wait_Answer_Query_Caculator_Overall_Info:
{
if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
&& ifsfMsg.MessageToken == this.pendingForAckMsgToken)
{
var dataParser = DatabaseDataParser.New().Convert(ifsfMsg.RawDatabaseData.ToArray());
var numberOfProducts = dataParser.DataIds.First(i => i.Item1 == 0x02).Item2.ToInt32();
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " CaculatorDbRequest_Read_FuelPoint_Product_FuelMode_Meter_Info Acked,\r\n" +
"Read caculatorDbOverallInfo, No_Products: " + numberOfProducts
+ ", No_Fuelling_Modes: " + dataParser.DataIds.First(i => i.Item1 == 0x03).Item2.ToInt32()
+ ", No_Meters: " + dataParser.DataIds.First(i => i.Item1 == 0x04).Item2.ToInt32()
+ ", No_FP: " + dataParser.DataIds.First(i => i.Item1 == 0x05).Item2.ToInt32()
+ ", Auth_State_Mode: " + dataParser.DataIds.First(i => i.Item1 == 0x0B).Item2.ToInt32());
if (numberOfProducts != this.thisTqcProductBarcodes.Count)
{
logger.LogInformation("!!!!!!PumpInitializer, node " + ifsfRecipientNode + " This TQC MUST config " + numberOfProducts + " products, but now trying to load local config with " + this.thisTqcProductBarcodes.Count + " products");
}
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending CommunicationServiceDbRequest_Write_RecipientAddressTable...");
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new CommunicationServiceDbRequest_Write_RecipientAddressTable(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value,
this.ifsfSelfSubnet,
this.ifsfSelfNode));
this.CurrentStatus = Status.Wait_ACK_Add_Recipient_Addr;
}
break;
}
case Status.Wait_ACK_Add_Recipient_Addr:
{
if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
&& ifsfMsg.MessageToken == this.pendingForAckMsgToken)
{
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " CommunicationServiceDbRequest_Write_RecipientAddressTable Acked.");
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Send LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId for FP: 0x"
+ this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString());
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 0x10));
this.CurrentStatus = Status.Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId;
}
break;
}
case Status.Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId:
{
if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
&& ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
{
var historyIncoming = context.Incoming as HistoryKeepIncoming;
this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers.AddRange(
historyIncoming.History.Where(h => (h.Item1 is LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer im)
&& im.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
&& im.MessageToken == this.pendingForAckMsgToken)
.Select(s => s.Item1 as LogicalNozzleDb_Nozzle_ProductInfo_PhyId_Answer).OrderBy(a => a.LogicalNozzleId));
if (++next_Fp_Index <= this.pumpHandlers.Count())
{
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId Acked");
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Send LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId for FP: 0x"
+ this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString());
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new LogicalNozzleDb_Read_Nozzle_ProductInfo_PhyId(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 0x10));
this.CurrentStatus = Status.Wait_Answer_Read_All_Nozzle_ProductInfo_PhyId;
}
else
{
foreach (var answer in this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers)
logger.LogInformation(" node " + ifsfRecipientNode + " Read nozzles info for FP: 0x" + answer.TargetFuelPointId.ToHexLogString()
+ ", with logicalId: 0x" + answer.LogicalNozzleId.ToHexLogString()
+ ", with physicalId: 0x" + answer.PhyscialNozzleId.ToHexLogString()
+ ", with product slotId: 0x" + answer.LinkedProductSlotId_InProductDbProdcutNoSlot.ToHexLogString());
next_Fp_Index = 1;
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending ProductDbRequest_Read_ProductNo");
this.pendingForAckMsgToken = this.msgTokenGenerator();
// will receive several answers and an ACK messages since this is a query all request.
context.Outgoing.Write(new ProductDbRequest_Read_ProductNo(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value, 0));
this.CurrentStatus = Status.Wait_Answer_Read_All_ProductNumber;
}
}
break;
}
case Status.Wait_Answer_Read_All_ProductNumber:
{
if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
&& ifsfMsg.MessageToken == this.pendingForAckMsgToken)
{
var historyIncoming = context.Incoming as HistoryKeepIncoming;
//logger.Debug("************" + historyIncoming.History.Select(a => a.Item1.GetType().Name).Aggregate((acc, n) => acc + ", " + n));
//logger.Debug("************historyIncoming.History.Count: " + historyIncoming.History.Count);
this.productDb_ProductNo_Answers.AddRange(
historyIncoming.History.Where(h => (h.Item1 is ProductDb_ProductNo_Answer im)
&& im.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
&& im.MessageToken == this.pendingForAckMsgToken).Select(s => s.Item1 as ProductDb_ProductNo_Answer).OrderBy(a => a.ProductSlotId));
if (this.productDb_ProductNo_Answers.Any())
{
logger.LogInformation(" node " + ifsfRecipientNode + " Read all existed product number in a TQC which are: " + this.productDb_ProductNo_Answers.Select(s => s.ProductNumber + "(in 0x" + s.ProductSlotId.ToHexLogString() + ")").Aggregate((acc, n) => acc + ", " + n));
string actualProductNumber = this.productDb_ProductNo_Answers.ElementAt(this.next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1).ProductNumber;//this.siteProductBarcodes[next_Clear_ProductNumber_In_Caculator_SlotIndex - 1].ToString();
this.pendingForAckMsgToken = this.msgTokenGenerator();
// each Product will receive a serial of answers for its differnt FuelMode since we input 0x10 as query all FM.
context.Outgoing.Write(new ProductPerFuellingModeDbRequest_Read_ProductPrice(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value, actualProductNumber, 0x10));
this.CurrentStatus = Status.Wait_Answer_Read_All_ProductPrice;
}
else
{
/* TQC without any product configed, happened for fresh new TQC pump (get hardware reset or just shipped from factory??), handle here */
logger.LogInformation(" node " + ifsfRecipientNode + " has no ProductDb_ProductNo_Answer received, seems there're no product configurated in this TQC yet, will directly Clear Set_ProductNumber_In_ProductDb");
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending Clear ProductNumber in ProductDb slot index: " + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index);
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value,
next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index, null));
this.CurrentStatus = Status.Wait_ACK_Set_ProductNumber_In_ProductDb;
}
}
break;
}
case Status.Wait_Answer_Read_All_ProductPrice:
{
if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
&& ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
{
var historyIncoming = context.Incoming as HistoryKeepIncoming;
var resultSetsForOneProduct = historyIncoming.History.Where(h => (h.Item1 is ProductPerFuellingModeDb_ProductPrice_Answer im)
&& im.MessageType == MessageType.IFSF_MESSAGE_TYPE_ANSWER
&& im.MessageToken == this.pendingForAckMsgToken).Select(s => s.Item1 as ProductPerFuellingModeDb_ProductPrice_Answer);
logger.LogInformation(" node " + ifsfRecipientNode + ", Read one existed product price with product number: "
+ resultSetsForOneProduct.Last().ProductNumber
+ ", price are: " + resultSetsForOneProduct.OrderBy(a => a.FuelModeId).Select(s => s.Price + " in FM:0x" + s.FuelModeId.ToString("X")).Aggregate((acc, n) => acc + ", " + n));
this.productPerFuellingModeDb_ProductPrice_Answers.AddRange(resultSetsForOneProduct);
if (this.productDb_ProductNo_Answers.Count() >= ++next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index)
{
string actualProductNumber = this.productDb_ProductNo_Answers.ElementAt(this.next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1).ProductNumber;//this.siteProductBarcodes[next_Clear_ProductNumber_In_Caculator_SlotIndex - 1].ToString();
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(new ProductPerFuellingModeDbRequest_Read_ProductPrice(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value, actualProductNumber, 0x10));
this.CurrentStatus = Status.Wait_Answer_Read_All_ProductPrice;
}
else
{
next_ActualInPump_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Read_All_ProductPrice done!");
var safe = this.OnTqcExistedConfigRead;
safe?.Invoke(this, null);
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending Clear productNo(1st time) in ProductDb with slot index: " + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index);
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value,
next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index, null));
this.CurrentStatus = Status.Wait_ACK_Clear_ProductNumber_In_ProductDb;
}
}
break;
}
case Status.Wait_ACK_Clear_ProductNumber_In_ProductDb:
{
if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
&& ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
{
if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived)
logger.LogError(" node " + ifsfRecipientNode + ", clear product from ProductDb failed!");
logger.LogInformation(" PumpInitializer, node " + ifsfRecipientNode + ", clear product no. from ProductDb with msgToken:" + this.pendingForAckMsgToken.Value + " Acked.");
if (++next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index <= 8)
{
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending Clear productNo in ProductDb with slot index: " + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index);
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value,
next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index, null));
this.CurrentStatus = Status.Wait_ACK_Clear_ProductNumber_In_ProductDb;
}
else
{
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " clear product number on all productDb productNo slots(total:" + (next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1) + ") done!");
next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
var next_site_nozzle = this.pumpHandlers.SelectMany(p => p.Nozzles)
.OrderBy(n => n.PumpId).OrderBy(n => n.LogicalId).ElementAt(next_site_nozzle_Index - 1);
var nozzleProductConfig = this.nozzleProductConfig
.First(c => c.PumpId == next_site_nozzle.PumpId && c.NozzleLogicalId == next_site_nozzle.LogicalId);
var ifsfNozzle
= this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers
.First(ifsfNzl => ifsfNzl.TargetFuelPointId == this.pumpHandlers.First(p => p.PumpId == next_site_nozzle.PumpId).PumpPhysicalId
&& ifsfNzl.LogicalNozzleId == next_site_nozzle.PhysicalId);
var nozzleBoundToProductDbProductSlot = ifsfNozzle.LinkedProductSlotId_InProductDbProdcutNoSlot;
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode
+ " pump " + next_site_nozzle.PumpId + ", nozzle logicalId " + next_site_nozzle.LogicalId
+ "(ifsf FpId 0x" + ifsfNozzle.TargetFuelPointId.ToHexLogString() + ", ifsf nozzle logicalId 0x" + ifsfNozzle.LogicalNozzleId.ToHexLogString() + ")"
+ " is hardware-bound to productDb product No. slot 0x" + nozzleBoundToProductDbProductSlot.ToHexLogString()
+ ", will(1st time) set 0x" + nozzleBoundToProductDbProductSlot.ToHexLogString() + " to product No.: " + nozzleProductConfig.ProductBarcode);
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value,
(byte)(nozzleBoundToProductDbProductSlot - 0x40), nozzleProductConfig.ProductBarcode.ToString()));
this.CurrentStatus = Status.Wait_ACK_Set_ProductNumber_In_ProductDb;
}
}
break;
}
case Status.Wait_ACK_Set_ProductNumber_In_ProductDb:
{
if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
&& ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
{
if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived)
logger.LogError(" node " + ifsfRecipientNode + ", set product to ProductDb failed!");
logger.LogInformation(" PumpInitializer, node " + ifsfRecipientNode + " ProductDbRequest_Write_SetProductNumber with msgToken:" + this.pendingForAckMsgToken.Value + " Acked.");
if (++next_site_nozzle_Index <= this.pumpHandlers.SelectMany(p => p.Nozzles).Count())
{
var next_site_nozzle = this.pumpHandlers.SelectMany(p => p.Nozzles)
.OrderBy(n => n.PumpId).OrderBy(n => n.LogicalId).ElementAt(next_site_nozzle_Index - 1);
var nozzleProductConfig = this.nozzleProductConfig
.First(c => c.PumpId == next_site_nozzle.PumpId && c.NozzleLogicalId == next_site_nozzle.LogicalId);
var ifsfNozzle
= this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers
.First(ifsfNzl => ifsfNzl.TargetFuelPointId == this.pumpHandlers.First(p => p.PumpId == next_site_nozzle.PumpId).PumpPhysicalId
&& ifsfNzl.LogicalNozzleId == next_site_nozzle.PhysicalId);
var nozzleBoundToProductDbProductSlot = ifsfNozzle.LinkedProductSlotId_InProductDbProdcutNoSlot;
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode
+ " pump " + next_site_nozzle.PumpId + ", nozzle logicalId " + next_site_nozzle.LogicalId
+ "(ifsf FpId 0x" + ifsfNozzle.TargetFuelPointId.ToHexLogString() + ", ifsf nozzle logicalId 0x" + ifsfNozzle.LogicalNozzleId.ToHexLogString() + ")"
+ " is hardware-bound to productDb product No. slot 0x" + nozzleBoundToProductDbProductSlot.ToHexLogString()
+ ", will set 0x" + nozzleBoundToProductDbProductSlot.ToHexLogString() + " to product No.: " + nozzleProductConfig.ProductBarcode);
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value,
(byte)(nozzleBoundToProductDbProductSlot - 0x40), nozzleProductConfig.ProductBarcode.ToString()));
this.CurrentStatus = Status.Wait_ACK_Set_ProductNumber_In_ProductDb;
}
else
{
next_site_nozzle_Index = 1;
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol");
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value,
CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol.Caculator_Auth_State_Mode.AUTH_State_Allowed));
this.CurrentStatus = Status.Wait_ACK_Set_AUTH_MODE;
}
//if (++next_Clear_ProductNumber_In_Caculator_SlotIndex <= 8)
//this.CurrentStatus = Status.Sending_Link_Meter_To_CaculatorSlot;
//logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending MeterDbRequest_Write_SetMesureToProduct(first), meterId: "
// + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index + ", 2nd arg: " + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index);
//this.pendingForAckMsgToken = this.msgTokenGenerator();
//context.Outgoing.Write(
// new MeterDbRequest_Write_SetMesureToProduct(this.ifsfRecipientSubnet,
// this.ifsfRecipientNode,
// this.ifsfSelfSubnet,
// this.ifsfSelfNode,
// this.pendingForAckMsgToken.Value,
// next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index,
// next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index));
//this.CurrentStatus = Status.Wait_ACK_Link_Meter_To_ProductDb_ProductNoSlot;
}
break;
}
//case Status.Wait_ACK_Link_Meter_To_ProductDb_ProductNoSlot:
// {
// if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
// && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
// {
// if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived)
// logger.Error(" node " + ifsfRecipientNode + ", link meter to ProductDb failed!");
// logger.Info(" PumpInitializer, MeterDbRequest_Write_SetMesureToProduct with msgToken:" + this.pendingForAckMsgToken.Value + " Acked.");
// if (this.thisTqcProductBarcodes.Count >= ++next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index)
// {
// //this.CurrentStatus = Status.Sending_Set_ProductNumber_In_Caculator;
// this.pendingForAckMsgToken = this.msgTokenGenerator();
// // put 91 to 98 to caculator 8 slots.
// string operatingProductNumber = this.thisTqcProductBarcodes[next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1].ToString();
// logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending ProductDbRequest_Write_SetProductNumber, ProductDb ProductNoSlot: "
// + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index + ", productNo: " + operatingProductNumber);
// context.Outgoing.Write(
// new ProductDbRequest_Write_SetProductNumber(this.ifsfRecipientSubnet,
// this.ifsfRecipientNode,
// this.ifsfSelfSubnet,
// this.ifsfSelfNode,
// this.pendingForAckMsgToken.Value,
// next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index, operatingProductNumber));
// this.CurrentStatus = Status.Wait_ACK_Set_ProductNumber_In_ProductDb;
// }
// else
// {
// next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
// logger.Info("PumpInitializer, node " + ifsfRecipientNode + " Link_Meter_To_ProductDb_ProductNoSlot done!");
// //this.CurrentStatus = Status.Sending_Set_AUTH_MODE;
// }
// }
// break;
// }
case Status.Wait_ACK_Set_AUTH_MODE:
{
if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
&& ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
{
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " CaculatorDbRequest_Write_Set_AuthStateMode_MinFuelingVol_MinDisplayVol Acked.");
var targetBarcode = this.thisTqcProductBarcodes[next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1];
int newInitPriceWithoutDecimalPoints;
if (this.PriceBook.ContainsKey(targetBarcode))
newInitPriceWithoutDecimalPoints = this.PriceBook[targetBarcode];
else
{
// try load the existed pre-configed price value which just read from TQC pump.
var preConfigedPriceOnPump = this.productPerFuellingModeDb_ProductPrice_Answers.FirstOrDefault(p => p.FuelModeId == 0x11 && p.ProductNumber == targetBarcode.ToString().PadLeft(8, '0'))?.Price;
if (string.IsNullOrEmpty(preConfigedPriceOnPump))
newInitPriceWithoutDecimalPoints = 1883 + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index;
else
newInitPriceWithoutDecimalPoints = int.Parse(preConfigedPriceOnPump);
this.PriceBook.Add(targetBarcode, newInitPriceWithoutDecimalPoints);
}
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending ProductPerFuellingModeDbRequest_Write_SetPrice, productNo: "
+ targetBarcode.ToString()
+ ", new price(without decimalPoints): " + newInitPriceWithoutDecimalPoints.ToString());
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new ProductPerFuellingModeDbRequest_Write_SetPrice(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value,
targetBarcode.ToString(), newInitPriceWithoutDecimalPoints.ToString()
, 0x11)
);
this.CurrentStatus = Status.Wait_ACK_Set_Price;
}
break;
}
case Status.Wait_ACK_Set_Price:
{
if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
&& ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
{
if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived)
logger.LogError(" node " + ifsfRecipientNode + ", set price for product failed!");
if (this.thisTqcProductBarcodes.Count >= ++next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index)
{
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " ProductPerFuellingModeDbRequest_Write_SetPrice Acked.");
var targetBarcode = this.thisTqcProductBarcodes[next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index - 1];
int newInitPriceWithoutDecimalPoints;
if (this.PriceBook != null && this.PriceBook.ContainsKey(targetBarcode))
newInitPriceWithoutDecimalPoints = this.PriceBook[targetBarcode];
else
{
// try load the existed pre-configed price value which just read from TQC pump.
var preConfigedPriceOnPump = this.productPerFuellingModeDb_ProductPrice_Answers.FirstOrDefault(p => p.FuelModeId == 0x11 && p.ProductNumber == targetBarcode.ToString().PadLeft(8, '0'))?.Price;
if (string.IsNullOrEmpty(preConfigedPriceOnPump))
newInitPriceWithoutDecimalPoints = 1883 + next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index;
else
newInitPriceWithoutDecimalPoints = int.Parse(preConfigedPriceOnPump);
this.PriceBook.Add(targetBarcode, newInitPriceWithoutDecimalPoints);
}
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " sending ProductPerFuellingModeDbRequest_Write_SetPrice, productNo: "
+ targetBarcode.ToString()
+ ", new price(without decimalPoints): " + newInitPriceWithoutDecimalPoints.ToString());
this.pendingForAckMsgToken = this.msgTokenGenerator();
//logger.Info("PumpInitializer, node "+ifsfRecipientNode+" will set product: " + this.siteProductBarcodes[next_Clear_ProductNumber_In_Caculator_SlotIndex - 1].ToString() + " with price " + (1883 + next_Clear_ProductNumber_In_Caculator_SlotIndex).ToString());
context.Outgoing.Write(
new ProductPerFuellingModeDbRequest_Write_SetPrice(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value, targetBarcode.ToString(), newInitPriceWithoutDecimalPoints.ToString(), 0x11)
);
this.CurrentStatus = Status.Wait_ACK_Set_Price;
}
else
{
next_Clear_ProductNumber_In_ProductDb_ProductNoSlot_Index = 1;
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " set product Price(8 products) in fuel mode 0x11 done!");
//this.CurrentStatus = Status.Sending_Set_Nozzle_To_CacalatorSlot;
// now we only have fuelling mode 0x11.
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Send FuellingPointDbRequest_Write_SetDefaultFuellingMode,\r\n" +
"FP: 0x" + this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString() + " set to fuelling mode 0x11");
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new FuellingPointDbRequest_Write_SetDefaultFuellingMode(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 1));
this.CurrentStatus = Status.Wait_ACK_Set_FP_Default_FuellingMode;
}
}
break;
}
//case Status.Wait_ACK_Set_Nozzle_To_ProductDb_ProductNoSlot:
// {
// if (context.Incoming.Message is AcknowledgeMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
// && ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
// {
// if (ifsfMsg.OverallStatus != MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived)
// logger.Error(" node " + ifsfRecipientNode + ", set nozzle to ProductDb failed:\r\n " + ifsfMsg.ToLogString());
// if (++next_site_nozzle_Index <= this.pumpHandlers.SelectMany(p => p.Nozzles).Count())
// {
// logger.Info("PumpInitializer, node " + ifsfRecipientNode + " LogicalNozzleDb_Write_SetNozzleProductAndMeter Acked.");
// var next_site_nozzle = this.pumpHandlers.SelectMany(p => p.Nozzles).ElementAt(next_site_nozzle_Index - 1);
// var productOnThisNozzleSitsInWhichCaculatorSlot =
// thisTqcProductBarcodes.IndexOf(nozzleProductConfig.First(p => p.NozzleLogicalId == next_site_nozzle.LogicalId && p.PumpId == next_site_nozzle.PumpId).ProductBarcode) + 1;
// this.pendingForAckMsgToken = this.msgTokenGenerator();
// byte targetIfsfFpId = this.pumpHandlers.First(p => p.PumpId == next_site_nozzle.PumpId).ifsfFuelPointId;
// byte targetIfsfNozzleId = ((byte)(0x11 + next_site_nozzle.LogicalId - 1));
// logger.Info("PumpInitializer, node " + ifsfRecipientNode + " sending LogicalNozzleDb_Write_SetNozzleProductAndMeter,\r\n" +
// "set product to nozzle, FP: 0x" + targetIfsfFpId.ToHexLogString()
// + ", nozzle: 0x" + targetIfsfNozzleId.ToHexLogString()
// + ", PumpId: " + next_site_nozzle.PumpId + " map to ProductDb ProductNo Slot: " + productOnThisNozzleSitsInWhichCaculatorSlot);
// context.Outgoing.Write(
// new LogicalNozzleDb_Write_SetNozzleProductAndMeter(this.ifsfRecipientSubnet,
// this.ifsfRecipientNode,
// this.ifsfSelfSubnet,
// this.ifsfSelfNode,
// this.pendingForAckMsgToken.Value,
// targetIfsfFpId
// //assume logical id is 1 based.
// , targetIfsfNozzleId,
// (byte)productOnThisNozzleSitsInWhichCaculatorSlot, 1)
// );
// this.CurrentStatus = Status.Wait_ACK_Set_Nozzle_To_ProductDb_ProductNoSlot;
// }
// else
// {
// next_site_nozzle_Index = 1;
// logger.Info("PumpInitializer, node " + ifsfRecipientNode + " set fp nozzles to ProductDb ProductNo Slot done!");
// }
// }
// break;
// }
case Status.Wait_ACK_Set_FP_Default_FuellingMode:
{
if (context.Incoming.Message is IfsfMessage ifsfMsg && ifsfMsg.MessageType == MessageType.IFSF_MESSAGE_TYPE_ACK
&& ifsfMsg.MessageToken == this.pendingForAckMsgToken.Value)
{
if (++next_Fp_Index <= this.pumpHandlers.Count())
{
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " FuellingPointDbRequest_Write_SetDefaultFuellingMode Acked");
// now we only have fuelling mode 0x11.
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " Send FuellingPointDbRequest_Write_SetDefaultFuellingMode,\r\n" +
"FP Default fuelling mode, FP: 0x" + this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId.ToHexLogString());
this.pendingForAckMsgToken = this.msgTokenGenerator();
context.Outgoing.Write(
new FuellingPointDbRequest_Write_SetDefaultFuellingMode(this.ifsfRecipientSubnet,
this.ifsfRecipientNode,
this.ifsfSelfSubnet,
this.ifsfSelfNode,
this.pendingForAckMsgToken.Value, (byte)(this.pumpHandlers.ElementAt(next_Fp_Index - 1).PumpPhysicalId), 1));
this.CurrentStatus = Status.Wait_ACK_Set_FP_Default_FuellingMode;
}
else
{
next_Fp_Index = 1;
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " set fp default fuelling mode all done!");
logger.LogInformation("PumpInitializer, node " + ifsfRecipientNode + " DONE!!!");
this.InitDoneTaskCompletionSource.SetResult(true);
this.CurrentStatus = Status.Done;
var safe = this.OnInitDone;
safe?.Invoke(this, null);
}
}
break;
}
}
return true;
}
public bool IsInitDone => this.CurrentStatus == Status.Done;
public void Reset()
{
this.CurrentStatus = Status.UnInit;
this.initTimeoutTimer.Stop();
this.InitDoneTaskCompletionSource = new TaskCompletionSource();
this.logicalNozzleDb_Nozzle_ProductInfo_PhyId_Answers.Clear();
this.productDb_ProductNo_Answers.Clear();
this.productPerFuellingModeDb_ProductPrice_Answers.Clear();
}
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
this.pumpIfsfNodeOfflineCheckTimer?.Stop();
this.fccHeartbeatSendingTimer?.Stop();
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
disposedValue = true;
}
}
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
// ~PumpHandler() {
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
// }
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
// TODO: uncomment the following line if the finalizer is overridden above.
// GC.SuppressFinalize(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.pumpHandlers.GetEnumerator();
}
#endregion
}
}