|
@@ -32,7 +32,7 @@ namespace HengshanPaymentTerminal
|
|
|
private string loggerAppendix = "HengshanPay Terminal";
|
|
|
private readonly SizableWindow<byte> window;
|
|
|
|
|
|
- private State nextState = State.Uninitialized;
|
|
|
+ //private State nextState = State.Uninitialized;
|
|
|
|
|
|
private const int STX = 0xFA;
|
|
|
|
|
@@ -50,132 +50,67 @@ namespace HengshanPaymentTerminal
|
|
|
|
|
|
window.OnWindowFull += (data) =>
|
|
|
{
|
|
|
- Message = window.ToArray();
|
|
|
+ byte[] tempBytes = window.ToArray();
|
|
|
+
|
|
|
+ //查找包头位置
|
|
|
+ int findIndex = -1;
|
|
|
+ for (int index = 0;index < tempBytes.Length - 1;index++)
|
|
|
+ {
|
|
|
+ if (tempBytes[index] == STX && tempBytes[index + 1] != STX)
|
|
|
+ {
|
|
|
+ findIndex = index;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //若没有找到包头,证明目前缓存中都是垃圾数据,清掉
|
|
|
+ if (findIndex == -1)
|
|
|
+ {
|
|
|
+ window.RemoveRange(0, tempBytes.Length);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //长度不够表示有粘包,部分数据还未接收到,等待下一轮接收数据再处理
|
|
|
+ //1byte FA + 1 byte 目标地址 + 1 byte 源地址 + 1 byte 帧号 + 2 byte 有效数据长度 + 2 byte 数据校验 = 8byte
|
|
|
+ if(findIndex + 8 > tempBytes.Length)
|
|
|
+ {
|
|
|
+ innerLogger.Info($"Insufficient data length,at least ,need {findIndex + 8},but length is {tempBytes.Length}");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //获取有效数据长度
|
|
|
+ int dataLen = Bcd2Int(tempBytes[4], tempBytes[5]);
|
|
|
+ byte[] reduceFAData = Reduce0xFAPair(tempBytes);
|
|
|
+ //去除多余FA后,实际数据不足有效数据长度,表示有粘包,仍非完整数据,等待下一轮接收数据再处理
|
|
|
+ if(reduceFAData.Length < dataLen + 2)
|
|
|
+ {
|
|
|
+ innerLogger.Info("Insufficient data length");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //计算crc,若值对应不上,表示数据错误
|
|
|
+ ushort calculatorValue = HengshanCRC16.ComputeChecksum(reduceFAData.AsSpan(1,reduceFAData.Length - 3).ToArray());
|
|
|
+ byte highByte = reduceFAData[reduceFAData.Length - 2];
|
|
|
+ byte lowByte = reduceFAData[reduceFAData.Length - 1];
|
|
|
+ int crcValue = (highByte << 8) | lowByte;
|
|
|
+ if (calculatorValue != crcValue)
|
|
|
+ {
|
|
|
+ innerLogger.Info($"crc value error,get value is {crcValue},calculator value is {calculatorValue}");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //获取到实际数据包,并将缓存数据删除
|
|
|
+ Message = reduceFAData;
|
|
|
var safe = OnMessageCut;
|
|
|
safe?.Invoke(this, null);
|
|
|
- nextState = State.Uninitialized;
|
|
|
- window.Clear();
|
|
|
- //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();
|
|
|
- //}
|
|
|
+ window.RemoveRange(0, tempBytes.Length);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
public void Feed(byte[] data)
|
|
|
{
|
|
|
- for (int i = 0; i < data.Length; i++)
|
|
|
- {
|
|
|
- window.Add(data[i]);
|
|
|
- }
|
|
|
+ List<byte> list = new(data);
|
|
|
+ window.Add(list);
|
|
|
+ //for (int i = 0; i < data.Length; i++)
|
|
|
+ //{
|
|
|
+ // window.Add(data[i]);
|
|
|
+ //}
|
|
|
}
|
|
|
|
|
|
private int Get0xFAPairCountInWindow(IEnumerable<byte> data)
|
|
@@ -183,32 +118,43 @@ namespace HengshanPaymentTerminal
|
|
|
return (int)Math.Round(((double)(data.Count(w => w == 0xFA)) / 2), MidpointRounding.AwayFromZero);
|
|
|
}
|
|
|
|
|
|
- public int Reduce0xFAPair(IList<byte> target, int startIndex)
|
|
|
+ /// <summary>
|
|
|
+ /// 删除多余的FA,在接收到的一个完整数据包中,数据包头为FA,其余数据中每一个FA会替换为两个FA,这里要删除多余的fa来获取实际数据包
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="data">原数据包</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public byte[] Reduce0xFAPair(byte[] data)
|
|
|
{
|
|
|
- int reducedCount = 0;
|
|
|
- var faAppearedPositions = new List<int>();
|
|
|
- for (int i = startIndex; i < target.Count; i++)
|
|
|
+ List<byte> list = new();
|
|
|
+ for (int index = 0; index < data.Length; index++)
|
|
|
{
|
|
|
- if (target[i] == 0xFA)
|
|
|
+ if (index < data.Length -1 && data[index] == STX && data[index + 1] == STX)
|
|
|
{
|
|
|
- if (i <= (target.Count - 2))
|
|
|
- {
|
|
|
- if (target[i + 1] == 0xFA)
|
|
|
- {
|
|
|
- faAppearedPositions.Add(i);
|
|
|
- i++;
|
|
|
- }
|
|
|
- }
|
|
|
+ list.Add(STX);
|
|
|
+ index++;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ list.Add(data[index]);
|
|
|
}
|
|
|
}
|
|
|
+ return list.ToArray();
|
|
|
+ }
|
|
|
|
|
|
- for (int i = 0; i < faAppearedPositions.Count; i++)
|
|
|
- {
|
|
|
- target.RemoveAt(faAppearedPositions[i] - i);
|
|
|
- reducedCount++;
|
|
|
- }
|
|
|
+ public int Bcd2Int(byte byte1,byte byte2)
|
|
|
+ {
|
|
|
+ // 提取第一个字节的高四位和低四位
|
|
|
+ int digit1 = (byte1 >> 4) & 0x0F; // 高四位
|
|
|
+ int digit2 = byte1 & 0x0F; // 低四位
|
|
|
+
|
|
|
+ // 提取第二个字节的高四位和低四位
|
|
|
+ int digit3 = (byte2 >> 4) & 0x0F; // 高四位
|
|
|
+ int digit4 = byte2 & 0x0F; // 低四位
|
|
|
+
|
|
|
+ // 组合成一个整数
|
|
|
+ int result = digit1 * 1000 + digit2 * 100 + digit3 * 10 + digit4;
|
|
|
|
|
|
- return reducedCount;
|
|
|
+ return result;
|
|
|
}
|
|
|
}
|
|
|
}
|