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; namespace HengShan_Pump_TQC_IFSF { /// /// silent means this pump group handler never actively send data to TQC, this is a solution for /// a special TQC pump only passthrough sales data to FC, because it's controlled by other party FC. /// [MetaPartsRequired(typeof(GenericDeviceProcessor<,>))] [MetaPartsRequired(typeof(HengShan_TQC_IFsfMessageTcpIpCommunicator_Silent<>))] [MetaPartsDescriptor( "lang-zh-cn:HS TQC只读模式加油机lang-en-us:HS TQC readonly mode pump", "lang-zh-cn:用于中石化站中驱动只读模式下的基于TCP连接的IFSF协议的 Wayne-WT30 或者 恒山-TH22 协议的加油机lang-en-us:Used for SinoPec site to driven pumps set in readonly mode, and the model are Wayne-WT30 or HengShan-TH22, which using IFSF protocol on TCP", new[] { "lang-zh-cn:加油机lang-en-us:Pump" })] public class SilentPumpGroupHandler : PumpGroupHandler { private System.Timers.Timer timelyCheckConnection; private DateTime lastTimeRecieveHearbeat; [ParamsJsonSchemas("PumpGroupHandlerCtorParamsJsonSchemas")] public SilentPumpGroupHandler(PumpGroupConfiguration pumpGroupConfiguration, IServiceProvider services) : base(pumpGroupConfiguration, services) { } /// /// /// /// public SilentPumpGroupHandler(int fccIfsfSubnetValue, int fccIfsfNodeValue, string pumpsXmlConfiguration) : base(fccIfsfSubnetValue, fccIfsfNodeValue, pumpsXmlConfiguration) { // sample // // // // // // // // // // // // // // // // // // // // // // // // // // } public override void Init(IContext context) { base.context = context; this.context.Communicator.OnConnected += (e, f) => { // each time communicator get disconnect, will trigger a re-init. logger.Info("node " + this.tqcPumpGroupInitializer.ifsfRecipientNode + ", OnCommunicator Connected, will hard set all pumps to Idle state anyway"); this.pumpHandlers.ForEach(p => p.HandleFpStatusChange(FuellingPointStatus.Idle, null)); }; this.context.Communicator.OnDisconnected += (e, f) => { logger.Info("node " + this.tqcPumpGroupInitializer.ifsfRecipientNode + ", OnCommunicator Disconnected, will hard set all pumps to Closed state anyway"); this.pumpHandlers.ForEach(p => p.HandleFpStatusChange(FuellingPointStatus.Closed, null)); }; base.pumpHandlers.ForEach(p => p.Init(this.context)); this.timelyCheckConnection = new Timer(6000); this.timelyCheckConnection.Elapsed += (_, __) => { if (DateTime.Now.Subtract(this.lastTimeRecieveHearbeat).TotalSeconds >= 6) { //logger.Debug("Heartbeat timed out, will turn all pumps(" // + this.pumpHandlers.Select(p => p.QueryStatus().ToString()).Aggregate((acc, n) => acc + "," + n) // + ") to Closed state"); this.pumpHandlers.ForEach(p => p.HandleFpStatusChange(FuellingPointStatus.Closed, null)); } }; this.timelyCheckConnection.Start(); } protected override void CreatePumpHandlers(IEnumerable pumpElements) { foreach (XmlNode pumpElement in pumpElements) { byte pumpPhysicalIdValue; var rawPumpPhysicalId = pumpElement.Attributes["physicalId"].Value; if (rawPumpPhysicalId.StartsWith("0x") || rawPumpPhysicalId.StartsWith("0X")) pumpPhysicalIdValue = byte.Parse(pumpElement.Attributes["physicalId"].Value.Substring(2), NumberStyles.HexNumber); else pumpPhysicalIdValue = byte.Parse(pumpElement.Attributes["physicalId"].Value); if (pumpPhysicalIdValue < 0x21 || pumpPhysicalIdValue > 0x24) throw new ArgumentException("ifsf fuel point id must be range from 0x21 to 0x24, while the configuration passed in " + rawPumpPhysicalId); SilentPumpHandler pumpHandler = new SilentPumpHandler(this, int.Parse(pumpElement.Attributes["pumpId"].Value), pumpPhysicalIdValue, base.recipientSubnet, base.recipientNode, pumpElement.InnerXml, GetNewMessageToken); base.pumpHandlers.Add(pumpHandler); } } public override Task Process(IContext context) { base.context = context; switch (context.Incoming.Message) { case CommunicationDb_Event_ACK usedAsHeartBeat: this.lastTimeRecieveHearbeat = DateTime.Now; context.Outgoing.Write(new AcknowledgeMessage( usedAsHeartBeat.OriginatorSubnet, usedAsHeartBeat.OriginatorNode, PumpGroupHandler.originatorSubnet, PumpGroupHandler.originatorNode, usedAsHeartBeat.MessageToken, usedAsHeartBeat.DatabaseId, MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived )); break; //case LogicalNozzleDb_Nozzle_PhyId_Event_ACK nozzlePhyIdEvent: // this.pumpHandlers.Where(h => h.ifsfFuelPointId == nozzlePhyIdEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context)); // break; //case FuellingPointDb_FpStatus_Event fpStatusEvent: // this.pumpHandlers.Where(h => h.ifsfFuelPointId == fpStatusEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context)); // break; //case FuellingPointDb_FpRunningTransaction_Event fpRunningTrxEvent: // this.pumpHandlers.Where(h => h.ifsfFuelPointId == fpRunningTrxEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context)); // break; default: base.RouteMessageToHandlers(context); break; } return Task.CompletedTask; } } }