StateMachineMessageCutter.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. using Edge.Core.Processor.Communicator;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace HengshanPaymentTerminal
  8. {
  9. public class StateMachineMessageCutter : IMessageCutter<byte[]>
  10. {
  11. #region Internal State enum
  12. private enum State
  13. {
  14. Uninitialized,
  15. LengthReady,
  16. BodyReady,
  17. EtxReady,
  18. CrcReady
  19. }
  20. #endregion
  21. #region Fields
  22. public byte[] Message { get; private set; }
  23. public event EventHandler OnMessageCut;
  24. public event EventHandler<MessageCutterInvalidMessageReadEventArg> OnInvalidMessageRead;
  25. private string loggerAppendix = "HengshanPay Terminal";
  26. private readonly SizableWindow<byte> window;
  27. private State nextState = State.Uninitialized;
  28. private const int STX = 0xFA;
  29. #endregion
  30. #region Logger
  31. static NLog.Logger innerLogger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("Communicator");
  32. #endregion
  33. public StateMachineMessageCutter()
  34. {
  35. window = new SizableWindow<byte>();
  36. window.OnWindowFull += (data) =>
  37. {
  38. switch (nextState)
  39. {
  40. case State.Uninitialized:
  41. if (data.First() == STX)
  42. {
  43. window.NewSize = 6;
  44. nextState = State.LengthReady;
  45. }
  46. else
  47. {
  48. OnInvalidMessageRead?.Invoke(this, new MessageCutterInvalidMessageReadEventArg()
  49. {
  50. Message = "invalid byte[0]: 0x" + data.First().ToString("x2") + ", will skip"
  51. });
  52. window.Clear();
  53. }
  54. break;
  55. case State.LengthReady:
  56. var countSTX = window.Count(b => b == STX);
  57. if (countSTX > 1)
  58. {
  59. //Console.WriteLine($"0xFA count: {countSTX}");
  60. innerLogger.Info($"0xFA count: {countSTX}");
  61. int index = window.ToList().LastIndexOf(STX);
  62. var tempArray = window.Skip(index).ToArray();
  63. window.Clear();
  64. nextState = State.Uninitialized;
  65. Feed(tempArray);
  66. return;
  67. }
  68. int bodyLen = window[4] * 10 + window[5];
  69. if (bodyLen > 256 || bodyLen < 2)
  70. {
  71. var safe8 = this.OnInvalidMessageRead;
  72. safe8?.Invoke(this, new MessageCutterInvalidMessageReadEventArg()
  73. {
  74. Message = "Message body length is not valid, len is: " + bodyLen
  75. });
  76. nextState = State.Uninitialized;
  77. window.Clear();
  78. window.NewSize = 1;
  79. return;
  80. }
  81. window.NewSize += bodyLen;
  82. nextState = State.BodyReady;
  83. break;
  84. case State.BodyReady:
  85. window.NewSize = window.Skip(4).Take(2).ToArray().ToInt32() + 6 + 3;
  86. nextState = State.CrcReady;
  87. break;
  88. case State.CrcReady:
  89. var stxCount = window.Count(b => b == STX);
  90. if (stxCount > 1)
  91. {
  92. //Console.WriteLine($"0xFA count: {stxCount}");
  93. innerLogger.Info($"0xFA count: {stxCount}");
  94. int length = window.Count;
  95. if (window[length - 3] == 0xFF)
  96. {
  97. //Console.WriteLine("ETX exists, consider it a complete message");
  98. innerLogger.Info("ETX exists, consider it a complete message");
  99. }
  100. else
  101. {
  102. int index = window.ToList().LastIndexOf(STX);
  103. if (index + 2 <= window.Count - 1)
  104. {
  105. byte trailingByte1 = window[index + 1];
  106. byte trailingByte2 = window[index + 2];
  107. if (trailingByte1 == trailingByte2 && trailingByte1 <= 6 && trailingByte1 > 0)
  108. {
  109. //Console.WriteLine("Possible mix of incompleted messages");
  110. innerLogger.Info("Possible mix of incompleted messages");
  111. var tempArray = window.Skip(index).ToArray();
  112. window.Clear();
  113. nextState = State.Uninitialized;
  114. Feed(tempArray);
  115. return;
  116. }
  117. }
  118. }
  119. }
  120. Message = window.ToArray();
  121. var safe = OnMessageCut;
  122. safe?.Invoke(this, null);
  123. nextState = State.Uninitialized;
  124. window.Clear();
  125. break;
  126. default:
  127. throw new ArgumentOutOfRangeException();
  128. }
  129. };
  130. }
  131. public void Feed(byte[] data)
  132. {
  133. for (int i = 0; i < data.Length; i++)
  134. {
  135. window.Add(data[i]);
  136. }
  137. }
  138. private int Get0xFAPairCountInWindow(IEnumerable<byte> data)
  139. {
  140. return (int)Math.Round(((double)(data.Count(w => w == 0xFA)) / 2), MidpointRounding.AwayFromZero);
  141. }
  142. public int Reduce0xFAPair(IList<byte> target, int startIndex)
  143. {
  144. int reducedCount = 0;
  145. var faAppearedPositions = new List<int>();
  146. for (int i = startIndex; i < target.Count; i++)
  147. {
  148. if (target[i] == 0xFA)
  149. {
  150. if (i <= (target.Count - 2))
  151. {
  152. if (target[i + 1] == 0xFA)
  153. {
  154. faAppearedPositions.Add(i);
  155. i++;
  156. }
  157. }
  158. }
  159. }
  160. for (int i = 0; i < faAppearedPositions.Count; i++)
  161. {
  162. target.RemoveAt(faAppearedPositions[i] - i);
  163. reducedCount++;
  164. }
  165. return reducedCount;
  166. }
  167. }
  168. }