123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- using Edge.Core.Processor.Communicator;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace Dfs.WayneChina.GilbarcoDispenserPayTerminal
- {
- public class StateMachineMessageCutter : IMessageCutter<byte[]>
- {
- public byte[] Message { get; private set; }
- public event EventHandler OnMessageCut;
- public event EventHandler<MessageCutterInvalidMessageReadEventArg> OnInvalidMessageRead;
- static NLog.Logger innerLogger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("StateMachineMessageCutter");
- private string loggerAppendix = "GilbarcoPayTerminal msgCutter ";
- private readonly SizableWindow<byte> window;
- private State nextState = State.Uninitialized;
- /// <summary>
- /// Constructor
- /// </summary>
- public StateMachineMessageCutter()
- {
- this.window = new SizableWindow<byte>(1);
-
- //Each 0xFA pair in message body part need extend buffer 1 byte.
- int extendBufferTimes = 0;
- this.window.OnWindowFull += (data) =>
- {
- //if (innerLogger.IsDebugEnabled)
- //{
-
- // innerLogger.Debug(BitConverter.ToString(data.ToArray()));
- //}
- switch (nextState)
- {
- case State.Uninitialized:
- if (data[0] == 0xFA)
- {
- extendBufferTimes = 0;
- this.window.NewSize = 2;
- this.nextState = State.HeaderReady;
- if (innerLogger.IsDebugEnabled)
- innerLogger.Debug(this.loggerAppendix + " state is State.Uninitialized and next is 0xFA, switch to HeaderReady");
- }
- else
- {
- this.window.Clear();
- return;
- }
- break;
- case State.HeaderReady:
- if (this.window.Count(h => h == 0xFA) == 1)
- {
- if (innerLogger.IsDebugEnabled)
- innerLogger.Debug(this.loggerAppendix + " Only 1 time of 0xFA in header window, switch to LengthReady");
- this.nextState = State.LengthReady;
- this.window.NewSize += 4;
- }
- else
- {
- if (innerLogger.IsDebugEnabled)
- innerLogger.Debug(this.loggerAppendix + " 2 times of 0xFA in header window, drop all and wait...");
- // double 0xFA, not a starter, will drop this 2 0xFA.
- this.nextState = State.Uninitialized;
- this.window.Clear();
- this.window.NewSize = 1;
- }
- break;
- case State.LengthReady:
- var bodyLen = BcdBytesToInt(new byte[2] { window[4], window[5] });//this.window[4] * 10 + this.window[5];
- if (bodyLen > 9999 || bodyLen < 2)
- {
- var safe8 = this.OnInvalidMessageRead;
- safe8?.Invoke(this, new MessageCutterInvalidMessageReadEventArg()
- {
- Message = "MessageBody Length is a 2 bytes BCD encoded and expected value here is >=2 and <=9999 while now is " + bodyLen
- });
- this.nextState = State.Uninitialized;
- this.window.Clear();
- this.window.NewSize = 1;
- return;
- }
- this.window.NewSize += bodyLen;
- this.nextState = State.BodyReady;
- if (innerLogger.IsDebugEnabled)
- innerLogger.Debug(this.loggerAppendix + " MsgBodyLen caculated with: " + this.window.NewSize + ", and set default windowSize to this value.");
- break;
- case State.BodyReady:
- /* window size not match with MsgBodyLen, indicates there's one or more 0xFA in body.*/
- var s = this.Get0xFAPairCountInWindow(this.window.Skip(6));
- if (extendBufferTimes < s)
- {
- this.window.NewSize += s - extendBufferTimes;
- extendBufferTimes = s;
- return;
- }
- extendBufferTimes = 0;
- this.window.NewSize += 2;
- // shrink the NewSize.
- this.window.NewSize -= this.Reduce0xFAPair(this.window, 6);
- this.nextState = State.CrcReady;
- break;
- case State.CrcReady:
- bodyLen = BcdBytesToInt(new byte[2] { window[4], window[5] });//this.window[4] * 10 + this.window[5];
- var p = this.Get0xFAPairCountInWindow(this.window.Skip(6 + bodyLen));
- if (extendBufferTimes < p)
- {
- this.window.NewSize += p - extendBufferTimes;
- extendBufferTimes = p;
- return;
- }
- this.Reduce0xFAPair(this.window, 6 + bodyLen);
- if (this.window.Count != (6 + bodyLen + 2))
- {
- var safe8 = this.OnInvalidMessageRead;
- safe8?.Invoke(this, new MessageCutterInvalidMessageReadEventArg()
- {
- Message = "CRC part is invalid"
- });
- this.nextState = State.Uninitialized;
- this.window.Clear();
- this.window.NewSize = 1;
- return;
- }
- this.Message = this.window.ToArray();
- var safe11 = this.OnMessageCut;
- safe11?.Invoke(this, null);
- this.nextState = State.Uninitialized;
- this.window.Clear();
- this.window.NewSize = 1;
- return;
- default:
- throw new ArgumentOutOfRangeException();
- }
- };
- //this.window.OnWindowFull += (data) =>
- //{
- // switch (nextState)
- // {
- // case State.Uninitialized:
- // if (data.First() == 0xFA)
- // {
- // extendBufferTimes = 0;
- // window.NewSize = 2;
- // 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.HeaderReady:
- // if (this.window.Count(h => h == 0xFA) == 1)
- // {
- // if (innerLogger.IsDebugEnabled)
- // innerLogger.Debug(this.loggerAppendix + " Only 1 time of 0xFA in header window, switch to LengthReady");
- // this.nextState = State.LengthReady;
- // this.window.NewSize += 4;
- // }
- // else
- // {
- // if (innerLogger.IsDebugEnabled)
- // innerLogger.Debug(this.loggerAppendix + " 2 times of 0xFA in header window, drop all and wait...");
- // // double 0xFA, not a starter, will drop this 2 0xFA.
- // this.nextState = State.Uninitialized;
- // this.window.Clear();
- // this.window.NewSize = 1;
- // }
- // break;
- // case State.LengthReady:
- // var lengthBytes = window.Skip(4).Take(2);
- // window.NewSize = lengthBytes.ToArray().ToInt32() + 6;
- // nextState = State.CrcReady;
- // break;
- // case State.CrcReady:
- // window.NewSize = window.Skip(4).Take(2).ToArray().ToInt32() + 6 + 2;
- // nextState = State.BodyReady;
- // break;
- // case State.BodyReady:
- // Message = window.ToArray();
- // var safe = OnMessageCut;
- // safe?.Invoke(this, null);
- // nextState = State.Uninitialized;
- // window.Clear();
- // break;
- // default:
- // throw new ArgumentOutOfRangeException();
- // }
- //};
- }
- private int BcdBytesToInt(byte[] bcds)
- {
- int result = 0;
- foreach (byte bcd in bcds)
- {
- result *= 100;
- result += (10 * (bcd >> 4));
- result += bcd & 0xf;
- }
- return result;
- }
- /// <summary>
- /// if the 0xFA count is odd, will use round up for count/2.
- /// e.g.: 0xFA appeared 2 times, return 1.
- /// 0xFA appeared 3 times, return 2, this is considered as window is not big enought to include further 0xFA since they're always even.
- /// </summary>
- /// <returns></returns>
- private int Get0xFAPairCountInWindow(IEnumerable<byte> data)
- {
- return (int)Math.Round(((double)(data.Count(w => w == 0xFA)) / 2), MidpointRounding.AwayFromZero);
- }
- /// <summary>
- /// Iterates the target by searching all pair(one besides one) of 0xFA, and reduce each pair into a single 0xFA.
- /// </summary>
- /// <param name="target"></param>
- /// <param name="startIndex"></param>
- /// <returns>how many timed of reduced happened. each reduce will remove one byte</returns>
- public int Reduce0xFAPair(IList<byte> target, int startIndex)
- {
- int reducedCount = 0;
- var faAppearedPositions = new List<int>();
- 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;
- }
- private enum State
- {
- Uninitialized,
- //HeaderSeeking,
- HeaderReady,
- LengthReady,
- BodyReady,
- CrcReady,
- }
- public void Feed(byte[] next)
- {
- //innerLogger.Debug(this.loggerAppendix + " " + next.ToHexLogString() + " is feed in Window in state: " + nextState);
- for (int i = 0; i < next.Length; i++)
- this.window.Add(next[i]);
- }
- }
- }
|