@@ -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(); | |||
// 转义还原——>验证校验码——>解析消息 | |||
// 1. 解码(转义还原) | |||
ReadOnlySpan<byte> buffer = JT809DeEscape(bytes); | |||
ReadOnlySpan<byte> buffer = JT809Util.JT809DeEscape(bytes); | |||
// 2. 验证校验码 | |||
// 2.1. 获取校验位索引 | |||
int checkIndex = buffer.Length - 3; | |||
@@ -132,87 +132,7 @@ namespace JT809.Protocol.Formatters | |||
// 5.终止符 | |||
offset += JT809BinaryExtensions.WriteByteLittle(bytes, offset, value.EndFlag); | |||
// 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; | |||
} | |||
} | |||
} |