SilentPumpGroupHandler.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. using Edge.Core.Processor;
  2. using Edge.Core.IndustryStandardInterface.Pump;
  3. using HengShan_Pump_TQC_IFSF.MessageEntity;
  4. using HengShan_Pump_TQC_IFSF.MessageEntity.Incoming;
  5. using HengShan_Pump_TQC_IFSF.MessageEntity.Outgoing;
  6. using Edge.Core.Parser.BinaryParser.Util;
  7. using System;
  8. using System.Collections;
  9. using System.Collections.Generic;
  10. using System.Globalization;
  11. using System.Linq;
  12. using System.Text;
  13. using System.Threading;
  14. using System.Threading.Tasks;
  15. using System.Xml;
  16. using Wayne.FDCPOSLibrary;
  17. using Timer = System.Timers.Timer;
  18. using Edge.Core.Processor.Dispatcher.Attributes;
  19. using Edge.Core.Processor.Communicator;
  20. namespace HengShan_Pump_TQC_IFSF
  21. {
  22. /// <summary>
  23. /// silent means this pump group handler never actively send data to TQC, this is a solution for
  24. /// a special TQC pump only passthrough sales data to FC, because it's controlled by other party FC.
  25. /// </summary>
  26. [MetaPartsRequired(typeof(GenericDeviceProcessor<,>))]
  27. [MetaPartsRequired(typeof(HengShan_TQC_IFsfMessageTcpIpCommunicator_Silent<>))]
  28. [MetaPartsDescriptor(
  29. "lang-zh-cn:HS TQC只读模式加油机lang-en-us:HS TQC readonly mode pump",
  30. "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",
  31. new[] { "lang-zh-cn:加油机lang-en-us:Pump" })]
  32. public class SilentPumpGroupHandler : PumpGroupHandler
  33. {
  34. private System.Timers.Timer timelyCheckConnection;
  35. private DateTime lastTimeRecieveHearbeat;
  36. [ParamsJsonSchemas("PumpGroupHandlerCtorParamsJsonSchemas")]
  37. public SilentPumpGroupHandler(PumpGroupConfiguration pumpGroupConfiguration, IServiceProvider services)
  38. : base(pumpGroupConfiguration, services)
  39. {
  40. }
  41. /// <summary>
  42. ///
  43. /// </summary>
  44. /// <param name="pumpsXmlConfiguration"></param>
  45. public SilentPumpGroupHandler(int fccIfsfSubnetValue, int fccIfsfNodeValue, string pumpsXmlConfiguration)
  46. : base(fccIfsfSubnetValue, fccIfsfNodeValue, pumpsXmlConfiguration)
  47. {
  48. // sample
  49. //<PumpGroup ifsfSubNet='2' ifsfNode='1'>
  50. // <Pump pumpId='1' physicalId='0x21'>
  51. // <Nozzles>
  52. // <Nozzle logicalId='1' physicalId='0x11' />
  53. // <Nozzle logicalId='2' physicalId='0x12' />
  54. // </Nozzles>
  55. // </Pump>
  56. // <Pump pumpId='2' physicalId='0x22'>
  57. // <Nozzles>
  58. // <Nozzle logicalId='1' physicalId='0x11' />
  59. // <Nozzle logicalId='2' physicalId='0x12' "/>
  60. // </Nozzles>
  61. // </Pump>
  62. // <Pump pumpId='3' physicalId='0x23'>
  63. // <Nozzles>
  64. // <Nozzle logicalId='1' physicalId='0x13' />
  65. // <Nozzle logicalId='2' physicalId='0x14' />
  66. // </Nozzles>
  67. // </Pump>
  68. // <Pump pumpId='4' physicalId='0x24'>
  69. // <Nozzles>
  70. // <Nozzle logicalId='1' physicalId='0x15' />
  71. // <Nozzle logicalId='2' physicalId='0x16' />
  72. // </Nozzles>
  73. // </Pump>
  74. //</PumpGroup>
  75. }
  76. public override void Init(IContext<byte[], IfsfMessageBase> context)
  77. {
  78. base.context = context;
  79. this.context.Communicator.OnConnected += (e, f) =>
  80. {
  81. // each time communicator get disconnect, will trigger a re-init.
  82. logger.Info("node " + this.tqcPumpGroupInitializer.ifsfRecipientNode + ", OnCommunicator Connected, will hard set all pumps to Idle state anyway");
  83. this.pumpHandlers.ForEach(p => p.HandleFpStatusChange(FuellingPointStatus.Idle, null));
  84. };
  85. this.context.Communicator.OnDisconnected += (e, f) =>
  86. {
  87. logger.Info("node " + this.tqcPumpGroupInitializer.ifsfRecipientNode + ", OnCommunicator Disconnected, will hard set all pumps to Closed state anyway");
  88. this.pumpHandlers.ForEach(p => p.HandleFpStatusChange(FuellingPointStatus.Closed, null));
  89. };
  90. base.pumpHandlers.ForEach(p => p.Init(this.context));
  91. this.timelyCheckConnection = new Timer(6000);
  92. this.timelyCheckConnection.Elapsed += (_, __) =>
  93. {
  94. if (DateTime.Now.Subtract(this.lastTimeRecieveHearbeat).TotalSeconds >= 6)
  95. {
  96. //logger.Debug("Heartbeat timed out, will turn all pumps("
  97. // + this.pumpHandlers.Select(p => p.QueryStatus().ToString()).Aggregate((acc, n) => acc + "," + n)
  98. // + ") to Closed state");
  99. this.pumpHandlers.ForEach(p => p.HandleFpStatusChange(FuellingPointStatus.Closed, null));
  100. }
  101. };
  102. this.timelyCheckConnection.Start();
  103. }
  104. protected override void CreatePumpHandlers(IEnumerable<XmlNode> pumpElements)
  105. {
  106. foreach (XmlNode pumpElement in pumpElements)
  107. {
  108. byte pumpPhysicalIdValue;
  109. var rawPumpPhysicalId = pumpElement.Attributes["physicalId"].Value;
  110. if (rawPumpPhysicalId.StartsWith("0x") || rawPumpPhysicalId.StartsWith("0X"))
  111. pumpPhysicalIdValue = byte.Parse(pumpElement.Attributes["physicalId"].Value.Substring(2), NumberStyles.HexNumber);
  112. else
  113. pumpPhysicalIdValue = byte.Parse(pumpElement.Attributes["physicalId"].Value);
  114. if (pumpPhysicalIdValue < 0x21 || pumpPhysicalIdValue > 0x24)
  115. throw new ArgumentException("ifsf fuel point id must be range from 0x21 to 0x24, while the configuration passed in " + rawPumpPhysicalId);
  116. SilentPumpHandler pumpHandler = new SilentPumpHandler(this, int.Parse(pumpElement.Attributes["pumpId"].Value),
  117. pumpPhysicalIdValue,
  118. base.recipientSubnet, base.recipientNode,
  119. pumpElement.InnerXml, GetNewMessageToken);
  120. base.pumpHandlers.Add(pumpHandler);
  121. }
  122. }
  123. public override Task Process(IContext<byte[], IfsfMessageBase> context)
  124. {
  125. base.context = context;
  126. switch (context.Incoming.Message)
  127. {
  128. case CommunicationDb_Event_ACK usedAsHeartBeat:
  129. this.lastTimeRecieveHearbeat = DateTime.Now;
  130. context.Outgoing.Write(new AcknowledgeMessage(
  131. usedAsHeartBeat.OriginatorSubnet,
  132. usedAsHeartBeat.OriginatorNode,
  133. PumpGroupHandler.originatorSubnet, PumpGroupHandler.originatorNode,
  134. usedAsHeartBeat.MessageToken,
  135. usedAsHeartBeat.DatabaseId,
  136. MessageAcknowledgeStatus.ACK_PositiveAcknowledgeDataReceived
  137. ));
  138. break;
  139. //case LogicalNozzleDb_Nozzle_PhyId_Event_ACK nozzlePhyIdEvent:
  140. // this.pumpHandlers.Where(h => h.ifsfFuelPointId == nozzlePhyIdEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
  141. // break;
  142. //case FuellingPointDb_FpStatus_Event fpStatusEvent:
  143. // this.pumpHandlers.Where(h => h.ifsfFuelPointId == fpStatusEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
  144. // break;
  145. //case FuellingPointDb_FpRunningTransaction_Event fpRunningTrxEvent:
  146. // this.pumpHandlers.Where(h => h.ifsfFuelPointId == fpRunningTrxEvent.TargetFuelPointId).ToList().ForEach(p => p.Process(context));
  147. // break;
  148. default: base.RouteMessageToHandlers(context); break;
  149. }
  150. return Task.CompletedTask;
  151. }
  152. }
  153. }