@@ -0,0 +1,32 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using JT809.Protocol; | |||||
using JT809.Protocol.Extensions; | |||||
using JT809.Protocol.MessageBody; | |||||
using JT809.Protocol.Enums; | |||||
namespace JT809.Protocol.Test.JT809Packages | |||||
{ | |||||
public class JT809HeaderPackageTest | |||||
{ | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
var bytes = "5B 00 00 00 48 00 00 00 85 10 01 01 33 EF B8 01 00 00 00 00 00 27 0F 01 33 EF B8 32 30 31 38 30 39 32 30 31 32 37 2E 30 2E 30 2E 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 29 6A 91 5D".ToHexBytes(); | |||||
JT809HeaderPackage jT809HeaderPackage = JT809Serializer.Deserialize<JT809HeaderPackage>(bytes); | |||||
Assert.Equal((uint)72, jT809HeaderPackage.Header.MsgLength); | |||||
Assert.Equal((uint)133, jT809HeaderPackage.Header.MsgSN); | |||||
Assert.Equal((uint)9999, jT809HeaderPackage.Header.EncryptKey); | |||||
Assert.Equal((uint)20180920, jT809HeaderPackage.Header.MsgGNSSCENTERID); | |||||
Assert.Equal(JT809BusinessType.主链路登录请求消息, jT809HeaderPackage.Header.BusinessType); | |||||
Assert.Equal(new JT809Header_Version().ToString(), jT809HeaderPackage.Header.Version.ToString()); | |||||
JT809_0x1001 jT809_0X1001 = JT809Serializer.Deserialize<JT809_0x1001>(jT809HeaderPackage.Bodies); | |||||
Assert.Equal((uint)20180920, jT809_0X1001.UserId); | |||||
Assert.Equal("20180920", jT809_0X1001.Password); | |||||
Assert.Equal("127.0.0.1", jT809_0X1001.DownLinkIP); | |||||
Assert.Equal((ushort)809, jT809_0X1001.DownLinkPort); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,84 @@ | |||||
using JT809.Protocol.Attributes; | |||||
using JT809.Protocol.Enums; | |||||
using JT809.Protocol.Exceptions; | |||||
using JT809.Protocol.Extensions; | |||||
using System; | |||||
namespace JT809.Protocol.Formatters | |||||
{ | |||||
public class JT809HeaderPackageFromatter : IJT809Formatter<JT809HeaderPackage> | |||||
{ | |||||
public JT809HeaderPackage Deserialize(ReadOnlySpan<byte> bytes, out int readSize) | |||||
{ | |||||
int offset = 0; | |||||
JT809HeaderPackage jT809HeaderPackage = new JT809HeaderPackage(); | |||||
// 转义还原——>验证校验码——>解析消息 | |||||
// 1. 解码(转义还原) | |||||
ReadOnlySpan<byte> buffer = JT809Util.JT809DeEscape(bytes); | |||||
// 2. 验证校验码 | |||||
// 2.1. 获取校验位索引 | |||||
int checkIndex = buffer.Length - 3; | |||||
// 2.2. 获取校验码 | |||||
int crcCodeOffset = 0; | |||||
jT809HeaderPackage.CRCCode = JT809BinaryExtensions.ReadUInt16Little(buffer.Slice(checkIndex, 2), ref crcCodeOffset); | |||||
if (!JT809GlobalConfig.Instance.SkipCRCCode) | |||||
{ | |||||
// 2.3. 从消息头到校验码前一个字节 | |||||
ushort checkCode = buffer.ToCRC16_CCITT(1, checkIndex); | |||||
// 2.4. 验证校验码 | |||||
if (jT809HeaderPackage.CRCCode != checkCode) | |||||
{ | |||||
throw new JT809Exception(JT809ErrorCode.CRC16CheckInvalid, $"{jT809HeaderPackage.CRCCode.ToString()}!={checkCode.ToString()}"); | |||||
} | |||||
} | |||||
jT809HeaderPackage.BeginFlag = JT809BinaryExtensions.ReadByteLittle(buffer, ref offset); | |||||
// 3.初始化消息头 | |||||
try | |||||
{ | |||||
jT809HeaderPackage.Header = JT809FormatterExtensions.GetFormatter<JT809Header>().Deserialize(buffer.Slice(offset, JT809Header.FixedByteLength), out readSize); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
throw new JT809Exception(JT809ErrorCode.HeaderParseError, $"offset>{offset.ToString()}", ex); | |||||
} | |||||
offset += readSize; | |||||
// 5.数据体处理 | |||||
// 5.1 判断是否有数据体(总长度-固定长度)> 0 | |||||
if ((jT809HeaderPackage.Header.MsgLength - JT809Package.FixedByteLength) > 0) | |||||
{ | |||||
//JT809.Protocol.Enums.JT809BusinessType 映射对应消息特性 | |||||
JT809BodiesTypeAttribute jT809BodiesTypeAttribute = jT809HeaderPackage.Header.BusinessType.GetAttribute<JT809BodiesTypeAttribute>(); | |||||
if (jT809BodiesTypeAttribute != null) | |||||
{ | |||||
try | |||||
{ | |||||
// 5.2 是否加密 | |||||
switch (jT809HeaderPackage.Header.EncryptFlag) | |||||
{ | |||||
case JT809Header_Encrypt.None: | |||||
//5.3 处理消息体 | |||||
jT809HeaderPackage.Bodies = buffer.Slice(offset, checkIndex - offset).ToArray(); | |||||
break; | |||||
case JT809Header_Encrypt.Common: | |||||
byte[] bodiesData = JT809GlobalConfig.Instance.Encrypt.Decrypt(buffer.Slice(offset, checkIndex - offset).ToArray(), jT809HeaderPackage.Header.EncryptKey); | |||||
jT809HeaderPackage.Bodies = bodiesData; | |||||
break; | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
throw new JT809Exception(JT809ErrorCode.BodiesParseError, $"offset>{offset.ToString()}", ex); | |||||
} | |||||
} | |||||
} | |||||
jT809HeaderPackage.EndFlag = buffer[buffer.Length - 1]; | |||||
readSize = buffer.Length; | |||||
return jT809HeaderPackage; | |||||
} | |||||
public int Serialize(ref byte[] bytes, int offset, JT809HeaderPackage value) | |||||
{ | |||||
throw new NotImplementedException("只适用反序列化"); | |||||
} | |||||
} | |||||
} |
@@ -17,7 +17,7 @@ namespace JT809.Protocol.Formatters | |||||
JT809Package jT809Package = new JT809Package(); | JT809Package jT809Package = new JT809Package(); | ||||
// 转义还原——>验证校验码——>解析消息 | // 转义还原——>验证校验码——>解析消息 | ||||
// 1. 解码(转义还原) | // 1. 解码(转义还原) | ||||
ReadOnlySpan<byte> buffer = JT809DeEscape(bytes); | |||||
ReadOnlySpan<byte> buffer = JT809Util.JT809DeEscape(bytes); | |||||
// 2. 验证校验码 | // 2. 验证校验码 | ||||
// 2.1. 获取校验位索引 | // 2.1. 获取校验位索引 | ||||
int checkIndex = buffer.Length - 3; | int checkIndex = buffer.Length - 3; | ||||
@@ -132,87 +132,7 @@ namespace JT809.Protocol.Formatters | |||||
// 5.终止符 | // 5.终止符 | ||||
offset += JT809BinaryExtensions.WriteByteLittle(bytes, offset, value.EndFlag); | offset += JT809BinaryExtensions.WriteByteLittle(bytes, offset, value.EndFlag); | ||||
// 6.转义 | // 6.转义 | ||||
return JT809Escape(ref bytes, offset); | |||||
} | |||||
internal static ReadOnlySpan<byte> JT809DeEscape(ReadOnlySpan<byte> buffer) | |||||
{ | |||||
byte[] tmpBuffer = JT809ArrayPool.Rent(buffer.Length - 1); | |||||
try | |||||
{ | |||||
int offset = 0; | |||||
tmpBuffer[offset++] = buffer[0]; | |||||
for (int i = 1; i < buffer.Length - 1; i++) | |||||
{ | |||||
byte first = buffer[i]; | |||||
byte second = buffer[i + 1]; | |||||
if (first == 0x5a && second == 0x01) | |||||
{ | |||||
tmpBuffer[offset++] = 0x5b; | |||||
i++; | |||||
} | |||||
else if (first == 0x5a && second == 0x02) | |||||
{ | |||||
tmpBuffer[offset++] = 0x5a; | |||||
i++; | |||||
} | |||||
else if (first == 0x5e && second == 0x01) | |||||
{ | |||||
tmpBuffer[offset++] = 0x5d; | |||||
i++; | |||||
} | |||||
else if (first == 0x5e && second == 0x02) | |||||
{ | |||||
tmpBuffer[offset++] = 0x5e; | |||||
i++; | |||||
} | |||||
else | |||||
{ | |||||
tmpBuffer[offset++] = first; | |||||
} | |||||
} | |||||
tmpBuffer[offset++] = buffer[buffer.Length - 1]; | |||||
return tmpBuffer.AsSpan(0, offset).ToArray(); | |||||
} | |||||
finally | |||||
{ | |||||
JT809ArrayPool.Return(tmpBuffer); | |||||
} | |||||
} | |||||
internal static int JT809Escape(ref byte[] buffer, int offset) | |||||
{ | |||||
byte[] tmpBuffer = buffer.AsSpan(0, offset).ToArray(); | |||||
int tmpOffset = 0; | |||||
buffer[tmpOffset++] = tmpBuffer[0]; | |||||
for (int i = 1; i < offset - 1; i++) | |||||
{ | |||||
var item = tmpBuffer[i]; | |||||
switch (item) | |||||
{ | |||||
case 0x5b: | |||||
buffer[tmpOffset++] = 0x5a; | |||||
buffer[tmpOffset++] = 0x01; | |||||
break; | |||||
case 0x5a: | |||||
buffer[tmpOffset++] = 0x5a; | |||||
buffer[tmpOffset++] = 0x02; | |||||
break; | |||||
case 0x5d: | |||||
buffer[tmpOffset++] = 0x5e; | |||||
buffer[tmpOffset++] = 0x01; | |||||
break; | |||||
case 0x5e: | |||||
buffer[tmpOffset++] = 0x5e; | |||||
buffer[tmpOffset++] = 0x02; | |||||
break; | |||||
default: | |||||
buffer[tmpOffset++] = item; | |||||
break; | |||||
} | |||||
} | |||||
buffer[tmpOffset++]= tmpBuffer[tmpBuffer.Length - 1]; | |||||
return tmpOffset; | |||||
return JT809Util.JT809Escape(ref bytes, offset); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -0,0 +1,25 @@ | |||||
using JT809.Protocol.Attributes; | |||||
using JT809.Protocol.Formatters; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.Protocol | |||||
{ | |||||
/// <summary> | |||||
/// JT809头部数据包 | |||||
/// </summary> | |||||
[JT809Formatter(typeof(JT809HeaderPackageFromatter))] | |||||
public class JT809HeaderPackage | |||||
{ | |||||
public byte BeginFlag { get; set; } = JT809Package.BEGINFLAG; | |||||
public JT809Header Header { get; set; } | |||||
public byte[] Bodies { get; set; } | |||||
public ushort CRCCode { get; set; } | |||||
public byte EndFlag { get; set; } = JT809Package.ENDFLAG; | |||||
} | |||||
} |
@@ -0,0 +1,89 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.Protocol | |||||
{ | |||||
public static class JT809Util | |||||
{ | |||||
internal static ReadOnlySpan<byte> JT809DeEscape(ReadOnlySpan<byte> buffer) | |||||
{ | |||||
byte[] tmpBuffer = JT809ArrayPool.Rent(buffer.Length - 1); | |||||
try | |||||
{ | |||||
int offset = 0; | |||||
tmpBuffer[offset++] = buffer[0]; | |||||
for (int i = 1; i < buffer.Length - 1; i++) | |||||
{ | |||||
byte first = buffer[i]; | |||||
byte second = buffer[i + 1]; | |||||
if (first == 0x5a && second == 0x01) | |||||
{ | |||||
tmpBuffer[offset++] = 0x5b; | |||||
i++; | |||||
} | |||||
else if (first == 0x5a && second == 0x02) | |||||
{ | |||||
tmpBuffer[offset++] = 0x5a; | |||||
i++; | |||||
} | |||||
else if (first == 0x5e && second == 0x01) | |||||
{ | |||||
tmpBuffer[offset++] = 0x5d; | |||||
i++; | |||||
} | |||||
else if (first == 0x5e && second == 0x02) | |||||
{ | |||||
tmpBuffer[offset++] = 0x5e; | |||||
i++; | |||||
} | |||||
else | |||||
{ | |||||
tmpBuffer[offset++] = first; | |||||
} | |||||
} | |||||
tmpBuffer[offset++] = buffer[buffer.Length - 1]; | |||||
return tmpBuffer.AsSpan(0, offset).ToArray(); | |||||
} | |||||
finally | |||||
{ | |||||
JT809ArrayPool.Return(tmpBuffer); | |||||
} | |||||
} | |||||
internal static int JT809Escape(ref byte[] buffer, int offset) | |||||
{ | |||||
byte[] tmpBuffer = buffer.AsSpan(0, offset).ToArray(); | |||||
int tmpOffset = 0; | |||||
buffer[tmpOffset++] = tmpBuffer[0]; | |||||
for (int i = 1; i < offset - 1; i++) | |||||
{ | |||||
var item = tmpBuffer[i]; | |||||
switch (item) | |||||
{ | |||||
case 0x5b: | |||||
buffer[tmpOffset++] = 0x5a; | |||||
buffer[tmpOffset++] = 0x01; | |||||
break; | |||||
case 0x5a: | |||||
buffer[tmpOffset++] = 0x5a; | |||||
buffer[tmpOffset++] = 0x02; | |||||
break; | |||||
case 0x5d: | |||||
buffer[tmpOffset++] = 0x5e; | |||||
buffer[tmpOffset++] = 0x01; | |||||
break; | |||||
case 0x5e: | |||||
buffer[tmpOffset++] = 0x5e; | |||||
buffer[tmpOffset++] = 0x02; | |||||
break; | |||||
default: | |||||
buffer[tmpOffset++] = item; | |||||
break; | |||||
} | |||||
} | |||||
buffer[tmpOffset++] = tmpBuffer[tmpBuffer.Length - 1]; | |||||
return tmpOffset; | |||||
} | |||||
} | |||||
} |