StateMachineMessageCutter.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;
  2. using Microsoft.Extensions.Logging;
  3. using Edge.Core.Parser.BinaryParser.Util;
  4. using System;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using Edge.Core.Processor.Communicator;
  11. namespace WayneChina_IcCardReader_SinoChem
  12. {
  13. public class StateMachineMessageCutter : IMessageCutter<byte[]>
  14. {
  15. public byte[] Message { get; private set; }
  16. public event EventHandler OnMessageCut;
  17. public event EventHandler<MessageCutterInvalidMessageReadEventArg> OnInvalidMessageRead;
  18. static NLog.Logger innerLogger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("Communicator");
  19. private string loggerAppendix = "WayneChina_IcCardReader_SinoChem msgCutter ";
  20. private readonly SizableWindow<byte> window;
  21. private State nextState = State.Uninitialized;
  22. /// <summary>
  23. /// if the 0xFA count is odd, will use round up for count/2.
  24. /// e.g.: 0xFA appeared 2 times, return 1.
  25. /// 0xFA appeared 3 times, return 2, this is considered as window is not big enough to include further 0xFA since they're always even.
  26. /// </summary>
  27. /// <returns></returns>
  28. private int Get0xFAPairCountInWindow(IList<byte> target)
  29. {
  30. return (int)Math.Round(((double)(target.Count(w => w == 0xFA)) / 2), MidpointRounding.AwayFromZero);
  31. }
  32. /// <summary>
  33. /// found all pair(one besides one) of 0xFA in a list, and reduce the pair to a single 0xFA.
  34. /// </summary>
  35. /// <param name="target"></param>
  36. private int Reduce0xFAPair(IList<byte> target, int from)
  37. {
  38. var faAppearedPositions = new List<int>();
  39. for (int i = from; i < target.Count; i++)
  40. {
  41. if (target[i] == 0xFA)
  42. {
  43. faAppearedPositions.Add(i);
  44. i++;
  45. }
  46. }
  47. for (int i = 0; i < faAppearedPositions.Count; i++)
  48. {
  49. target.RemoveAt(faAppearedPositions[i] - i);
  50. }
  51. return faAppearedPositions.Count;
  52. }
  53. /// <summary>
  54. ///
  55. /// </summary>
  56. public StateMachineMessageCutter()
  57. {
  58. //通讯数据包格式为:数据包头(0xFA)+源地址(1byte)+有效数据长度(2 bytes)+有效数据+数据校验(2 bytes)
  59. this.window = new SizableWindow<byte>();
  60. this.window.OnWindowFull += (data) =>
  61. {
  62. switch (nextState)
  63. {
  64. case State.Uninitialized:
  65. if (data.First() == 0xFA)
  66. {
  67. // extend to 2 to see if exists extra 0xFA
  68. this.window.NewSize = 2;
  69. this.nextState = State.PrefixReady;
  70. if (innerLogger.IsTraceEnabled)
  71. innerLogger.Trace(this.loggerAppendix + " state is State.Uninitialized and next is 0xFF, switch to LengthReady");
  72. }
  73. else
  74. this.window.Clear();
  75. break;
  76. case State.PrefixReady:
  77. if (this.window.Count(h => h == 0xFA) == 1)
  78. {
  79. if (innerLogger.IsTraceEnabled)
  80. innerLogger.Trace(this.loggerAppendix + " 1 time of 0xFA in header window, valid starter, switch to LengthReady");
  81. this.nextState = State.LengthReady;
  82. this.window.NewSize = 4;
  83. }
  84. else
  85. {
  86. if (innerLogger.IsDebugEnabled)
  87. innerLogger.Debug(this.loggerAppendix + " 2 times of 0xFA in potential header window, drop all and wait...");
  88. // double 0xFA, not a starter.
  89. this.nextState = State.Uninitialized;
  90. this.window.Clear();
  91. }
  92. break;
  93. case State.LengthReady:
  94. //this.DumpWindowToQueue();
  95. try
  96. {
  97. this.window.NewSize += this.window.Skip(2).Take(2).GetBCD();
  98. this.nextState = State.BodyReady;
  99. if (innerLogger.IsTraceEnabled)
  100. innerLogger.Trace(this.loggerAppendix + " MsgBodyLen caculated with: " + this.window.NewSize);
  101. }
  102. catch (Exception ex)
  103. {
  104. innerLogger.Debug($"LengthReady: Exception in parsing message length bytes : {ex.ToString()}");
  105. this.nextState = State.Uninitialized;
  106. this.window.Clear();
  107. }
  108. break;
  109. case State.BodyReady:
  110. //innerLogger.Debug(this.loggerAppendix + " Fire OnMessageConstructed with innerQueue: " + this.buffer.ToHexLogString());
  111. if (this.window.All(w => w != 0xFA))
  112. {
  113. if (innerLogger.IsTraceEnabled)
  114. innerLogger.Trace(this.loggerAppendix + " MsgBody have NO 0xFA, fastly switch to CrcReady");
  115. /* window size exactly match with MsgBodyLen, indicats there's no 0xFA in body.*/
  116. this.window.NewSize += 2;
  117. this.nextState = State.CrcReady;
  118. }
  119. else
  120. {
  121. try
  122. {
  123. /* window size not match with MsgBodyLen, indicates there's one or more 0xFA in body.*/
  124. var __msgBodyLen = this.window.Skip(2).Take(2).GetBCD();
  125. var faPairCount = Get0xFAPairCountInWindow(this.window.Skip(1).ToList());
  126. if ((this.window.Count - 4) == (__msgBodyLen + faPairCount))
  127. {
  128. //innerLogger.Debug(this.loggerAppendix + " Reduce0xFAPair from raw window: " + this.window.ToHexLogString());
  129. var reducedCount = this.Reduce0xFAPair(this.window, 1);
  130. this.window.NewSize += (2 - reducedCount);
  131. this.nextState = State.CrcReady;
  132. }
  133. else
  134. {
  135. /*extend the window based on 0xFA count in `current` window. NOTE, every extend may include new 0xFA pair.*/
  136. this.window.NewSize += 1;
  137. //innerLogger.Debug(this.loggerAppendix + " Re-extend window size to: " + this.windowSize);
  138. }
  139. }
  140. catch (Exception ex)
  141. {
  142. innerLogger.Debug($"BodyReady: Exception in parsing message length bytes : {ex.ToString()}");
  143. this.nextState = State.Uninitialized;
  144. this.window.Clear();
  145. }
  146. }
  147. break;
  148. case State.CrcReady:
  149. try
  150. {
  151. var _msgBodyLen = this.window.Skip(2).Take(2).GetBCD();
  152. var crcPart = this.window.Skip(4 + _msgBodyLen).ToList();
  153. if (crcPart.Count == 2 && crcPart[0] == 0xFA)
  154. {
  155. if (innerLogger.IsDebugEnabled)
  156. innerLogger.Debug(this.loggerAppendix + " Crc[0] is 0xFA, extend windowSize to 3");
  157. this.window.NewSize += 1; return;
  158. }
  159. if (crcPart.Count == 3 && crcPart[2] == 0xFA)
  160. {
  161. if (innerLogger.IsDebugEnabled)
  162. innerLogger.Debug(this.loggerAppendix + " Crc[2] is 0xFA, extend windowSize to 4");
  163. this.window.NewSize += 1; return;
  164. }
  165. Enumerable.Repeat("_", (int)Math.Round(((double)(crcPart.Count(w => w == 0xFA)) / 2))).ToList().ForEach(
  166. s =>
  167. {
  168. if (innerLogger.IsDebugEnabled)
  169. innerLogger.Debug(this.loggerAppendix + " Removing one 0xFA from Crc part.");
  170. crcPart.Remove(0xFA);
  171. });
  172. this.Message = this.window.Take(4 + _msgBodyLen).ToList().Concat(crcPart).ToArray();
  173. var safe = this.OnMessageCut;
  174. safe?.Invoke(this, null);
  175. this.nextState = State.Uninitialized;
  176. this.window.Clear();
  177. //this.window.NewSize = this.initWindowSize;
  178. }
  179. catch (Exception ex)
  180. {
  181. innerLogger.Debug($"CrcReady: BodyReady: Exception in parsing message length bytes : {ex.ToString()}");
  182. this.nextState = State.Uninitialized;
  183. this.window.Clear();
  184. }
  185. break;
  186. default:
  187. throw new ArgumentOutOfRangeException();
  188. }
  189. };
  190. }
  191. private enum State
  192. {
  193. Uninitialized,
  194. // single 0xFA
  195. PrefixReady,
  196. // 0xFA + 1 byte source address
  197. HeaderReady,
  198. // 0xFA + 1 byte source address + 2 bytes real length
  199. LengthReady,
  200. // 0xFA + 1 byte source address + 2 bytes real length + real data
  201. BodyReady,
  202. // 0xFA + 1 byte source address + 2 bytes real length + real data + 2 bytes CRC
  203. CrcReady,
  204. }
  205. public void Feed(byte[] next)
  206. {
  207. //innerLogger.Debug(this.loggerAppendix + " " + next.ToHexLogString() + " is feed in Window in state: " + nextState);
  208. for (int i = 0; i < next.Length; i++)
  209. this.window.Add(next[i]);
  210. }
  211. }
  212. }