PumpGroupHandler.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. using Edge.Core.Processor;
  2. using Edge.Core.IndustryStandardInterface.Pump;
  3. using System;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using System.Xml;
  10. using Wayne_Pump_Dart.MessageEntity;
  11. using Wayne_Pump_Dart.MessageEntity.Outgoing;
  12. using Edge.Core.Processor.Dispatcher.Attributes;
  13. using Edge.Core.Processor.Communicator;
  14. using System.Xml.Serialization;
  15. using System.IO;
  16. namespace Wayne_Pump_Dart
  17. {
  18. [MetaPartsRequired(typeof(HalfDuplexActivePollingDeviceProcessor<,>))]
  19. [MetaPartsRequired(typeof(ComPortCommunicator<>))]
  20. [MetaPartsRequired(typeof(TcpClientCommunicator<>))]
  21. [MetaPartsRequired(typeof(TcpServerCommunicator<>))]
  22. [MetaPartsDescriptor(
  23. "lang-zh-cn:稳Dart加油机lang-en-us:WayneDartPump",
  24. "lang-zh-cn:用于驱动 Wayne Dart 协议的加油机lang-en-us:Used for driven Wayne Pump which use Dart Protocol",
  25. new[] { "lang-zh-cn:加油机lang-en-us:Pump" })]
  26. public class PumpGroupHandler : TestableActivePollingDeviceHandler<byte[], MessageBase>, IEnumerable<IFdcPumpController>, IDisposable
  27. {
  28. //protected static ILog logger = log4net.LogManager.GetLogger("PumpHandler");
  29. static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("PumpHandler");
  30. protected IContext<byte[], MessageBase> context;
  31. protected int pollingInterval;
  32. protected List<PumpHandler> pumpHandlers = new List<PumpHandler>();
  33. /// <summary>
  34. /// if the underlying communicator connected to remote device.
  35. /// </summary>
  36. private bool isCommConnected = false;
  37. private int amountDecimalDigits;
  38. private int volumeDecimalDigits;
  39. private int priceDecimalDigits;
  40. private int volumeTotalizerDecimalDigits;
  41. #region Ctor parameters
  42. public class PumpGroupConfiguration
  43. {
  44. public byte AmountDecimalDigits { get; set; }
  45. public byte VolumeDecimalDigits { get; set; }
  46. public byte PriceDecimalDigits { get; set; }
  47. public byte VolumeTotalizerDecimalDigits { get; set; }
  48. public List<PumpConfiguration> PumpConfigurations { get; set; }
  49. }
  50. public class PumpConfiguration
  51. {
  52. public byte PumpId { get; set; }
  53. public byte PhysicalId { get; set; }
  54. public List<NozzleConfiguration> NozzleConfigurations { get; set; }
  55. }
  56. public class NozzleConfiguration
  57. {
  58. public byte LogicalId { get; set; }
  59. public byte PhysicalId { get; set; }
  60. public int DefaultNoDecimalPointPriceIfNoHistoryPriceReadFromDb { get; set; }
  61. }
  62. #endregion
  63. [ParamsJsonSchemas("PumpGroupHandlerCtorParamsJsonSchemas")]
  64. public PumpGroupHandler(PumpGroupConfiguration pumpGroupConfiguration, IServiceProvider services)
  65. {
  66. this.amountDecimalDigits = pumpGroupConfiguration.AmountDecimalDigits;
  67. this.volumeDecimalDigits = pumpGroupConfiguration.VolumeDecimalDigits;
  68. this.priceDecimalDigits = pumpGroupConfiguration.PriceDecimalDigits;
  69. this.volumeTotalizerDecimalDigits = pumpGroupConfiguration.VolumeTotalizerDecimalDigits;
  70. logger.Info("Wayne Dart pump group, Will create " + pumpGroupConfiguration.PumpConfigurations.Count + " pump handlers for this Wayne Dart Group from config");
  71. this.CreatePumpHandlers(pumpGroupConfiguration.PumpConfigurations.Select(pc =>
  72. {
  73. XmlDocument doc = new XmlDocument();
  74. var xml =
  75. "<Pump pumpId='" + pc.PumpId + "' physicalId='" + pc.PhysicalId + "'>" +
  76. "<Nozzles>" +
  77. pc.NozzleConfigurations.Select(nc =>
  78. "<Nozzle logicalId='" + nc.LogicalId + "' physicalId='" + nc.PhysicalId +
  79. "' defaultNoDecimalPointPriceIfNoHistoryPriceReadFromDb='" +
  80. nc.DefaultNoDecimalPointPriceIfNoHistoryPriceReadFromDb + "' />").Aggregate((acc, n) => acc + n) +
  81. "</Nozzles>" +
  82. "</Pump>";
  83. doc.LoadXml(xml);
  84. return doc.FirstChild;
  85. }));
  86. }
  87. //[MetaApiParamsJsonSchemaDescriptor("PumpGroupHandlerCtorSchema")]
  88. public PumpGroupHandler(int amountDecimalDigits, int volumeDecimalDigits,
  89. int priceDecimalDigits, int volumeTotalizerDecimalDigits,
  90. string pumpGroupXmlConfiguration)
  91. {
  92. this.amountDecimalDigits = amountDecimalDigits;
  93. this.volumeDecimalDigits = volumeDecimalDigits;
  94. this.priceDecimalDigits = priceDecimalDigits;
  95. this.volumeTotalizerDecimalDigits = volumeTotalizerDecimalDigits;
  96. //sample of pumpGroupXmlConfiguration, the address is a config value in physical wayne dart
  97. //pump mother board, 1 - 32 is the acceptable values.
  98. //the reason introduce PumpGroupHandler, because wayne dart pump may run multiple pumps in single
  99. //rs485 com port.
  100. //<PumpGroup>
  101. // <Pump pumpId='1' physicalId='1'>
  102. // <Nozzles>
  103. // <Nozzle logicalId='1' physicalId='1' />
  104. // <Nozzle logicalId='2' physicalId='2' />
  105. // <Nozzle logicalId='3' physicalId='3' />
  106. // </Nozzles>
  107. // </Pump>
  108. // <Pump pumpId='2' physicalId='2'>
  109. // <Nozzles>
  110. // <Nozzle logicalId='1' physicalId='1' />
  111. // <Nozzle logicalId='2' physicalId='2' />
  112. // </Nozzles>
  113. // </Pump>
  114. //</PumpGroup>
  115. XmlDocument xmlDocument = new XmlDocument();
  116. xmlDocument.LoadXml(pumpGroupXmlConfiguration);
  117. //var rootElement = xmlDocument.GetElementsByTagName("PumpGroup").Cast<XmlElement>().First();
  118. var pumpElements = xmlDocument.GetElementsByTagName("Pump").Cast<XmlNode>();
  119. logger.Info("Wayne Dart pump group, Will create " + pumpElements.Count() + " pump handlers for this Wayne Dart Group from local config");
  120. this.CreatePumpHandlers(pumpElements);
  121. }
  122. protected virtual void CreatePumpHandlers(IEnumerable<XmlNode> pumpElements)
  123. {
  124. foreach (XmlNode pumpElement in pumpElements)
  125. {
  126. var pumpId =
  127. int.Parse(pumpElement.Attributes["pumpId"].Value);
  128. var pumpHandler = new PumpHandler(this, pumpId,
  129. amountDecimalDigits, volumeDecimalDigits,
  130. priceDecimalDigits, volumeTotalizerDecimalDigits,
  131. pumpElement.OuterXml);
  132. this.pumpHandlers.Add(pumpHandler);
  133. }
  134. this.rotateMsgTokens.AddRange(Enumerable.Repeat(0, this.pumpHandlers.Select(s => s.PumpId).Max()));
  135. }
  136. public IEnumerator<IFdcPumpController> GetEnumerator()
  137. {
  138. return this.pumpHandlers.GetEnumerator();
  139. }
  140. IEnumerator IEnumerable.GetEnumerator()
  141. {
  142. return this.pumpHandlers.GetEnumerator();
  143. }
  144. /// <summary>
  145. /// key:value = pumpId:MsgToken
  146. /// </summary>
  147. private List<int> rotateMsgTokens = new List<int>();
  148. /// <summary>
  149. /// get a valid token which from >=1 and lessOrEqual F.
  150. /// NOTE, if ResetMessageTokenToAlign(pumpId) was called, the next token generated here will be 0, which is
  151. /// a special value used to align the comm with real pump side, and then if the comm eastablished, we
  152. /// should use 1 to F.
  153. /// </summary>
  154. /// <param name="pumpId"></param>
  155. /// <returns></returns>
  156. public byte GetNewMessageToken(int pumpId)
  157. {
  158. lock (rotateMsgTokens)
  159. {
  160. //return 0;
  161. if (rotateMsgTokens[pumpId - 1] > 0x0E)
  162. this.rotateMsgTokens[pumpId - 1] = 0;
  163. //var returnValue = this.rotateMsgTokens[pumpId - 1];
  164. //this.rotateMsgTokens[pumpId - 1] += 1;
  165. return (byte)(++this.rotateMsgTokens[pumpId - 1]);
  166. }
  167. }
  168. /// <summary>
  169. /// typically called after a data request was NAKed by pump side, 0 token used for
  170. /// re-align the comm from FC to real pump.
  171. /// </summary>
  172. /// <param name="pumpId"></param>
  173. public void ResetMessageTokenToAlign(int pumpId)
  174. {
  175. lock (rotateMsgTokens)
  176. this.rotateMsgTokens[pumpId - 1] = -1;
  177. }
  178. public override void Init(IContext<byte[], MessageBase> context)
  179. {
  180. base.Init(context);
  181. this.context = context;
  182. this.context.Communicator.OnConnected += (object sender, EventArgs e) => { this.isCommConnected = true; };
  183. this.context.Communicator.OnDisconnected += (object sender, EventArgs e) => { this.isCommConnected = false; };
  184. this.pumpHandlers.ForEach(p => p.Init(this.context));
  185. var timeWindowWithActivePollingOutgoing =
  186. this.context.Outgoing as TimeWindowWithActivePollingOutgoing<byte[], MessageBase>;
  187. this.pollingInterval = timeWindowWithActivePollingOutgoing.PollingInterval;
  188. int previousPolledHandlerIndex = 0;
  189. timeWindowWithActivePollingOutgoing.PollingMsgProducer =
  190. () =>
  191. {
  192. try
  193. {
  194. //if (!this.isCommConnected) return null;
  195. if (this.pumpHandlers.Count <= previousPolledHandlerIndex)
  196. previousPolledHandlerIndex = 0;
  197. var target = this.pumpHandlers[previousPolledHandlerIndex++];
  198. return new Poll((byte)(target.PumpPhysicalId), 0);// this.GetNewMessageToken(target.PumpId));
  199. }
  200. catch (Exception exxx)
  201. {
  202. logger.Error("Exceptioned in WayneDartPumpGroupHandler(previousPolledHandlerIndex: " + previousPolledHandlerIndex + "): " + exxx);
  203. return null;
  204. }
  205. };
  206. }
  207. public override Task Process(IContext<byte[], MessageBase> context)
  208. {
  209. this.context = context;
  210. var ph = this.pumpHandlers.FirstOrDefault(p => (byte)(p.PumpPhysicalId) == context.Incoming.Message.Adrs);
  211. if (ph == null)
  212. {
  213. logger.Error("PumpGroupHandler does not contain pumpHandler with physcialId: "
  214. + context.Incoming.Message.Adrs.ToString("X2"));
  215. return Task.CompletedTask;
  216. }
  217. return ph.Process(context);
  218. //return Task.CompletedTask;
  219. }
  220. public void Dispose()
  221. {
  222. this.pumpHandlers.ForEach(ph => ph.Dispose());
  223. }
  224. }
  225. }