StateMachineMessageCutter.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using Edge.Core.Parser.BinaryParser.Util;
  9. using Edge.Core.Processor.Communicator;
  10. namespace LanTian_Sinopec_PumpIcCardReader
  11. {
  12. public class StateMachineMessageCutter : IMessageCutter<byte[]>
  13. {
  14. public byte[] Message { get; private set; }
  15. public event EventHandler OnMessageCut;
  16. public event EventHandler<MessageCutterInvalidMessageReadEventArg> OnInvalidMessageRead;
  17. static NLog.Logger innerLogger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("StateMachineMessageCutter");
  18. private string loggerAppendix = "LanTian_Sinopec_PumpIcCardReader msgCutter ";
  19. private readonly SizableWindow<byte> window;
  20. private State nextState = State.Uninitialized;
  21. /// <summary>
  22. ///
  23. /// </summary>
  24. public StateMachineMessageCutter()
  25. {
  26. this.window = new SizableWindow<byte>(1);
  27. //each 0xFA pair in message body part need extend buffer 1 byte.
  28. int extendBufferTimes = 0;
  29. this.window.OnWindowFull += (data) =>
  30. {
  31. switch (nextState)
  32. {
  33. case State.Uninitialized:
  34. if (data[0] == 0xFA)
  35. {
  36. extendBufferTimes = 0;
  37. this.window.NewSize = 2;
  38. this.nextState = State.HeaderReady;
  39. if (innerLogger.IsDebugEnabled)
  40. innerLogger.Debug(this.loggerAppendix + " state is State.Uninitialized and next is 0xFA, switch to HeaderReady");
  41. }
  42. else
  43. {
  44. this.window.Clear();
  45. return;
  46. }
  47. break;
  48. case State.HeaderReady:
  49. if (this.window.Count(h => h == 0xFA) == 1)
  50. {
  51. if (innerLogger.IsDebugEnabled)
  52. innerLogger.Debug(this.loggerAppendix + " Only 1 time of 0xFA in header window, switch to LengthReady");
  53. this.nextState = State.LengthReady;
  54. this.window.NewSize += 4;
  55. }
  56. else
  57. {
  58. if (innerLogger.IsDebugEnabled)
  59. innerLogger.Debug(this.loggerAppendix + " 2 times of 0xFA in header window, drop all and wait...");
  60. // double 0xFA, not a starter, will drop this 2 0xFA.
  61. this.nextState = State.Uninitialized;
  62. this.window.Clear();
  63. this.window.NewSize = 1;
  64. }
  65. break;
  66. case State.LengthReady:
  67. var bodyLen = this.window[4] * 10 + this.window[5];
  68. if (bodyLen > 9999 || bodyLen < 2)
  69. {
  70. var safe8 = this.OnInvalidMessageRead;
  71. safe8?.Invoke(this, new MessageCutterInvalidMessageReadEventArg()
  72. {
  73. Message = "MessageBody Length is a 2 bytes BCD encoded and expected value here is >=2 and <=9999 while now is " + bodyLen
  74. });
  75. this.nextState = State.Uninitialized;
  76. this.window.Clear();
  77. this.window.NewSize = 1;
  78. return;
  79. }
  80. this.window.NewSize += bodyLen;
  81. this.nextState = State.BodyReady;
  82. if (innerLogger.IsDebugEnabled)
  83. innerLogger.Debug(this.loggerAppendix + " MsgBodyLen caculated with: " + this.window.NewSize + ", and set default windowSize to this value.");
  84. break;
  85. case State.BodyReady:
  86. /* window size not match with MsgBodyLen, indicates there's one or more 0xFA in body.*/
  87. var s = this.Get0xFAPairCountInWindow(this.window.Skip(6));
  88. if (extendBufferTimes < s)
  89. {
  90. this.window.NewSize += s - extendBufferTimes;
  91. extendBufferTimes = s;
  92. return;
  93. }
  94. extendBufferTimes = 0;
  95. this.window.NewSize += 2;
  96. // shrink the NewSize.
  97. this.window.NewSize -= this.Reduce0xFAPair(this.window, 6);
  98. this.nextState = State.CrcReady;
  99. break;
  100. case State.CrcReady:
  101. bodyLen = this.window[4] * 10 + this.window[5];
  102. var p = this.Get0xFAPairCountInWindow(this.window.Skip(6 + bodyLen));
  103. if (extendBufferTimes < p)
  104. {
  105. this.window.NewSize += p - extendBufferTimes;
  106. extendBufferTimes = p;
  107. return;
  108. }
  109. this.Reduce0xFAPair(this.window, 6 + bodyLen);
  110. if (this.window.Count != (6 + bodyLen + 2))
  111. {
  112. var safe8 = this.OnInvalidMessageRead;
  113. safe8?.Invoke(this, new MessageCutterInvalidMessageReadEventArg()
  114. {
  115. Message = "CRC part is invalid"
  116. });
  117. this.nextState = State.Uninitialized;
  118. this.window.Clear();
  119. this.window.NewSize = 1;
  120. return;
  121. }
  122. this.Message = this.window.ToArray();
  123. var safe11 = this.OnMessageCut;
  124. safe11?.Invoke(this, null);
  125. this.nextState = State.Uninitialized;
  126. this.window.Clear();
  127. this.window.NewSize = 1;
  128. return;
  129. default:
  130. throw new ArgumentOutOfRangeException();
  131. }
  132. };
  133. }
  134. /// <summary>
  135. /// if the 0xFA count is odd, will use round up for count/2.
  136. /// e.g.: 0xFA appeared 2 times, return 1.
  137. /// 0xFA appeared 3 times, return 2, this is considered as window is not big enought to include further 0xFA since they're always even.
  138. /// </summary>
  139. /// <returns></returns>
  140. private int Get0xFAPairCountInWindow(IEnumerable<byte> data)
  141. {
  142. return (int)Math.Round(((double)(data.Count(w => w == 0xFA)) / 2), MidpointRounding.AwayFromZero);
  143. }
  144. /// <summary>
  145. /// Iterates the target by searching all pair(one besides one) of 0xFA, and reduce each pair into a single 0xFA.
  146. /// </summary>
  147. /// <param name="target"></param>
  148. /// <param name="startIndex"></param>
  149. /// <returns>how many timed of reduced happened. each reduce will remove one byte</returns>
  150. public int Reduce0xFAPair(IList<byte> target, int startIndex)
  151. {
  152. int reducedCount = 0;
  153. var faAppearedPositions = new List<int>();
  154. for (int i = startIndex; i < target.Count; i++)
  155. {
  156. if (target[i] == 0xFA)
  157. {
  158. if (i <= (target.Count - 2))
  159. {
  160. if (target[i + 1] == 0xFA)
  161. {
  162. faAppearedPositions.Add(i);
  163. i++;
  164. }
  165. }
  166. }
  167. }
  168. for (int i = 0; i < faAppearedPositions.Count; i++)
  169. {
  170. target.RemoveAt(faAppearedPositions[i] - i);
  171. reducedCount++;
  172. }
  173. return reducedCount;
  174. }
  175. private enum State
  176. {
  177. Uninitialized,
  178. //HeaderSeeking,
  179. HeaderReady,
  180. LengthReady,
  181. BodyReady,
  182. CrcReady,
  183. }
  184. public void Feed(byte[] next)
  185. {
  186. //innerLogger.Debug(this.loggerAppendix + " " + next.ToHexLogString() + " is feed in Window in state: " + nextState);
  187. for (int i = 0; i < next.Length; i++)
  188. this.window.Add(next[i]);
  189. }
  190. }
  191. }