123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- using Edge.Core.Processor;
- using Edge.Core.IndustryStandardInterface.Pump;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Xml;
- using Wayne_Pump_Dart.MessageEntity;
- using Wayne_Pump_Dart.MessageEntity.Outgoing;
- using Edge.Core.Processor.Dispatcher.Attributes;
- using Edge.Core.Processor.Communicator;
- using System.Xml.Serialization;
- using System.IO;
- namespace Wayne_Pump_Dart
- {
- [MetaPartsRequired(typeof(HalfDuplexActivePollingDeviceProcessor<,>))]
- [MetaPartsRequired(typeof(ComPortCommunicator<>))]
- [MetaPartsRequired(typeof(TcpClientCommunicator<>))]
- [MetaPartsRequired(typeof(TcpServerCommunicator<>))]
- [MetaPartsDescriptor(
- "lang-zh-cn:稳Dart加油机lang-en-us:WayneDartPump",
- "lang-zh-cn:用于驱动 Wayne Dart 协议的加油机lang-en-us:Used for driven Wayne Pump which use Dart Protocol",
- new[] { "lang-zh-cn:加油机lang-en-us:Pump" })]
- public class PumpGroupHandler : TestableActivePollingDeviceHandler<byte[], MessageBase>, IEnumerable<IFdcPumpController>, IDisposable
- {
- //protected static ILog logger = log4net.LogManager.GetLogger("PumpHandler");
- static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("PumpHandler");
- protected IContext<byte[], MessageBase> context;
- protected int pollingInterval;
- protected List<PumpHandler> pumpHandlers = new List<PumpHandler>();
- /// <summary>
- /// if the underlying communicator connected to remote device.
- /// </summary>
- private bool isCommConnected = false;
- private int amountDecimalDigits;
- private int volumeDecimalDigits;
- private int priceDecimalDigits;
- private int volumeTotalizerDecimalDigits;
- #region Ctor parameters
- public class PumpGroupConfiguration
- {
- public byte AmountDecimalDigits { get; set; }
- public byte VolumeDecimalDigits { get; set; }
- public byte PriceDecimalDigits { get; set; }
- public byte VolumeTotalizerDecimalDigits { get; set; }
- public List<PumpConfiguration> PumpConfigurations { get; set; }
- }
- public class PumpConfiguration
- {
- public byte PumpId { get; set; }
- public byte PhysicalId { get; set; }
- public List<NozzleConfiguration> NozzleConfigurations { get; set; }
- }
- public class NozzleConfiguration
- {
- public byte LogicalId { get; set; }
- public byte PhysicalId { get; set; }
- public int DefaultNoDecimalPointPriceIfNoHistoryPriceReadFromDb { get; set; }
- }
- #endregion
- [ParamsJsonSchemas("PumpGroupHandlerCtorParamsJsonSchemas")]
- public PumpGroupHandler(PumpGroupConfiguration pumpGroupConfiguration, IServiceProvider services)
- {
- this.amountDecimalDigits = pumpGroupConfiguration.AmountDecimalDigits;
- this.volumeDecimalDigits = pumpGroupConfiguration.VolumeDecimalDigits;
- this.priceDecimalDigits = pumpGroupConfiguration.PriceDecimalDigits;
- this.volumeTotalizerDecimalDigits = pumpGroupConfiguration.VolumeTotalizerDecimalDigits;
- logger.Info("Wayne Dart pump group, Will create " + pumpGroupConfiguration.PumpConfigurations.Count + " pump handlers for this Wayne Dart Group from config");
- this.CreatePumpHandlers(pumpGroupConfiguration.PumpConfigurations.Select(pc =>
- {
- XmlDocument doc = new XmlDocument();
- var xml =
- "<Pump pumpId='" + pc.PumpId + "' physicalId='" + pc.PhysicalId + "'>" +
- "<Nozzles>" +
- pc.NozzleConfigurations.Select(nc =>
- "<Nozzle logicalId='" + nc.LogicalId + "' physicalId='" + nc.PhysicalId +
- "' defaultNoDecimalPointPriceIfNoHistoryPriceReadFromDb='" +
- nc.DefaultNoDecimalPointPriceIfNoHistoryPriceReadFromDb + "' />").Aggregate((acc, n) => acc + n) +
- "</Nozzles>" +
- "</Pump>";
- doc.LoadXml(xml);
- return doc.FirstChild;
- }));
- }
- //[MetaApiParamsJsonSchemaDescriptor("PumpGroupHandlerCtorSchema")]
- public PumpGroupHandler(int amountDecimalDigits, int volumeDecimalDigits,
- int priceDecimalDigits, int volumeTotalizerDecimalDigits,
- string pumpGroupXmlConfiguration)
- {
- this.amountDecimalDigits = amountDecimalDigits;
- this.volumeDecimalDigits = volumeDecimalDigits;
- this.priceDecimalDigits = priceDecimalDigits;
- this.volumeTotalizerDecimalDigits = volumeTotalizerDecimalDigits;
- //sample of pumpGroupXmlConfiguration, the address is a config value in physical wayne dart
- //pump mother board, 1 - 32 is the acceptable values.
- //the reason introduce PumpGroupHandler, because wayne dart pump may run multiple pumps in single
- //rs485 com port.
- //<PumpGroup>
- // <Pump pumpId='1' physicalId='1'>
- // <Nozzles>
- // <Nozzle logicalId='1' physicalId='1' />
- // <Nozzle logicalId='2' physicalId='2' />
- // <Nozzle logicalId='3' physicalId='3' />
- // </Nozzles>
- // </Pump>
- // <Pump pumpId='2' physicalId='2'>
- // <Nozzles>
- // <Nozzle logicalId='1' physicalId='1' />
- // <Nozzle logicalId='2' physicalId='2' />
- // </Nozzles>
- // </Pump>
- //</PumpGroup>
- XmlDocument xmlDocument = new XmlDocument();
- xmlDocument.LoadXml(pumpGroupXmlConfiguration);
- //var rootElement = xmlDocument.GetElementsByTagName("PumpGroup").Cast<XmlElement>().First();
- var pumpElements = xmlDocument.GetElementsByTagName("Pump").Cast<XmlNode>();
- logger.Info("Wayne Dart pump group, Will create " + pumpElements.Count() + " pump handlers for this Wayne Dart Group from local config");
- this.CreatePumpHandlers(pumpElements);
- }
- protected virtual void CreatePumpHandlers(IEnumerable<XmlNode> pumpElements)
- {
- foreach (XmlNode pumpElement in pumpElements)
- {
- var pumpId =
- int.Parse(pumpElement.Attributes["pumpId"].Value);
- var pumpHandler = new PumpHandler(this, pumpId,
- amountDecimalDigits, volumeDecimalDigits,
- priceDecimalDigits, volumeTotalizerDecimalDigits,
- pumpElement.OuterXml);
- this.pumpHandlers.Add(pumpHandler);
- }
- this.rotateMsgTokens.AddRange(Enumerable.Repeat(0, this.pumpHandlers.Select(s => s.PumpId).Max()));
- }
- public IEnumerator<IFdcPumpController> GetEnumerator()
- {
- return this.pumpHandlers.GetEnumerator();
- }
- IEnumerator IEnumerable.GetEnumerator()
- {
- return this.pumpHandlers.GetEnumerator();
- }
- /// <summary>
- /// key:value = pumpId:MsgToken
- /// </summary>
- private List<int> rotateMsgTokens = new List<int>();
- /// <summary>
- /// get a valid token which from >=1 and lessOrEqual F.
- /// NOTE, if ResetMessageTokenToAlign(pumpId) was called, the next token generated here will be 0, which is
- /// a special value used to align the comm with real pump side, and then if the comm eastablished, we
- /// should use 1 to F.
- /// </summary>
- /// <param name="pumpId"></param>
- /// <returns></returns>
- public byte GetNewMessageToken(int pumpId)
- {
- lock (rotateMsgTokens)
- {
- //return 0;
- if (rotateMsgTokens[pumpId - 1] > 0x0E)
- this.rotateMsgTokens[pumpId - 1] = 0;
- //var returnValue = this.rotateMsgTokens[pumpId - 1];
- //this.rotateMsgTokens[pumpId - 1] += 1;
- return (byte)(++this.rotateMsgTokens[pumpId - 1]);
- }
- }
- /// <summary>
- /// typically called after a data request was NAKed by pump side, 0 token used for
- /// re-align the comm from FC to real pump.
- /// </summary>
- /// <param name="pumpId"></param>
- public void ResetMessageTokenToAlign(int pumpId)
- {
- lock (rotateMsgTokens)
- this.rotateMsgTokens[pumpId - 1] = -1;
- }
- public override void Init(IContext<byte[], MessageBase> context)
- {
- base.Init(context);
- this.context = context;
- this.context.Communicator.OnConnected += (object sender, EventArgs e) => { this.isCommConnected = true; };
- this.context.Communicator.OnDisconnected += (object sender, EventArgs e) => { this.isCommConnected = false; };
- this.pumpHandlers.ForEach(p => p.Init(this.context));
- var timeWindowWithActivePollingOutgoing =
- this.context.Outgoing as TimeWindowWithActivePollingOutgoing<byte[], MessageBase>;
- this.pollingInterval = timeWindowWithActivePollingOutgoing.PollingInterval;
- int previousPolledHandlerIndex = 0;
- timeWindowWithActivePollingOutgoing.PollingMsgProducer =
- () =>
- {
- try
- {
- //if (!this.isCommConnected) return null;
- if (this.pumpHandlers.Count <= previousPolledHandlerIndex)
- previousPolledHandlerIndex = 0;
- var target = this.pumpHandlers[previousPolledHandlerIndex++];
- return new Poll((byte)(target.PumpPhysicalId), 0);// this.GetNewMessageToken(target.PumpId));
- }
- catch (Exception exxx)
- {
- logger.Error("Exceptioned in WayneDartPumpGroupHandler(previousPolledHandlerIndex: " + previousPolledHandlerIndex + "): " + exxx);
- return null;
- }
- };
- }
- public override Task Process(IContext<byte[], MessageBase> context)
- {
- this.context = context;
- var ph = this.pumpHandlers.FirstOrDefault(p => (byte)(p.PumpPhysicalId) == context.Incoming.Message.Adrs);
- if (ph == null)
- {
- logger.Error("PumpGroupHandler does not contain pumpHandler with physcialId: "
- + context.Incoming.Message.Adrs.ToString("X2"));
- return Task.CompletedTask;
- }
- return ph.Process(context);
- //return Task.CompletedTask;
- }
- public void Dispose()
- {
- this.pumpHandlers.ForEach(ph => ph.Dispose());
- }
- }
- }
|