using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump; using Edge.Core.Processor.Communicator; using System; using System.Collections.Generic; using System.Linq; namespace Dfs.WayneChina.HengshanPayTerminal { public class StateMachineMessageCutter : IMessageCutter { #region Internal State enum private enum State { Uninitialized, LengthReady, BodyReady, EtxReady, CrcReady } #endregion #region Fields public byte[] Message { get; private set; } public event EventHandler OnMessageCut; public event EventHandler OnInvalidMessageRead; private string loggerAppendix = "HengshanPay Terminal"; private readonly SizableWindow window; private State nextState = State.Uninitialized; private const int STX = 0xFA; #endregion #region Logger static NLog.Logger innerLogger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("Communicator"); #endregion public StateMachineMessageCutter() { window = new SizableWindow(); window.OnWindowFull += (data) => { switch (nextState) { case State.Uninitialized: if (data.First() == STX) { window.NewSize = 6; nextState = State.LengthReady; } else { OnInvalidMessageRead?.Invoke(this, new MessageCutterInvalidMessageReadEventArg() { Message = "invalid byte[0]: 0x" + data.First().ToString("x2") + ", will skip" }); window.Clear(); } break; case State.LengthReady: var countSTX = window.Count(b => b == STX); if (countSTX > 1) { //Console.WriteLine($"0xFA count: {countSTX}"); innerLogger.Info($"0xFA count: {countSTX}"); int index = window.ToList().LastIndexOf(STX); var tempArray = window.Skip(index).ToArray(); window.Clear(); nextState = State.Uninitialized; Feed(tempArray); return; } int bodyLen = window[4] * 10 + window[5]; if (bodyLen > 256 || bodyLen < 2) { var safe8 = this.OnInvalidMessageRead; safe8?.Invoke(this, new MessageCutterInvalidMessageReadEventArg() { Message = "Message body length is not valid, len is: " + bodyLen }); nextState = State.Uninitialized; window.Clear(); window.NewSize = 1; return; } window.NewSize += bodyLen; nextState = State.BodyReady; break; case State.BodyReady: window.NewSize = window.Skip(4).Take(2).ToArray().ToInt32() + 6 + 3; nextState = State.CrcReady; break; case State.CrcReady: var stxCount = window.Count(b => b == STX); if (stxCount > 1) { //Console.WriteLine($"0xFA count: {stxCount}"); innerLogger.Info($"0xFA count: {stxCount}"); int length = window.Count; if (window[length - 3] == 0xFF) { //Console.WriteLine("ETX exists, consider it a complete message"); innerLogger.Info("ETX exists, consider it a complete message"); } else { int index = window.ToList().LastIndexOf(STX); if (index + 2 <= window.Count - 1) { byte trailingByte1 = window[index + 1]; byte trailingByte2 = window[index + 2]; if (trailingByte1 == trailingByte2 && trailingByte1 <= 6 && trailingByte1 > 0) { //Console.WriteLine("Possible mix of incompleted messages"); innerLogger.Info("Possible mix of incompleted messages"); var tempArray = window.Skip(index).ToArray(); window.Clear(); nextState = State.Uninitialized; Feed(tempArray); return; } } } } Message = window.ToArray(); var safe = OnMessageCut; safe?.Invoke(this, null); nextState = State.Uninitialized; window.Clear(); break; default: throw new ArgumentOutOfRangeException(); } }; } public void Feed(byte[] data) { for (int i = 0; i < data.Length; i++) { window.Add(data[i]); } } private int Get0xFAPairCountInWindow(IEnumerable data) { return (int)Math.Round(((double)(data.Count(w => w == 0xFA)) / 2), MidpointRounding.AwayFromZero); } public int Reduce0xFAPair(IList target, int startIndex) { int reducedCount = 0; var faAppearedPositions = new List(); for (int i = startIndex; i < target.Count; i++) { if (target[i] == 0xFA) { if (i <= (target.Count - 2)) { if (target[i + 1] == 0xFA) { faAppearedPositions.Add(i); i++; } } } } for (int i = 0; i < faAppearedPositions.Count; i++) { target.RemoveAt(faAppearedPositions[i] - i); reducedCount++; } return reducedCount; } } }