@@ -1,35 +1,47 @@ | |||||
# JT809协议开发 | # JT809协议开发 | ||||
### 瞎逼逼: | |||||
## 瞎逼逼: | |||||
  现在有了[JT808](https://github.com/SmallChi/GPSPlatform/blob/master/JT808.md)的基础,对JT809就只剩搬砖了。 |   现在有了[JT808](https://github.com/SmallChi/GPSPlatform/blob/master/JT808.md)的基础,对JT809就只剩搬砖了。 | ||||
> 该JT809协议是参考[MessagePack-CSharp](https://github.com/neuecc/MessagePack-CSharp)一款二进制序列化器,站在巨人的肩膀上搬砖就是爽歪歪。 | > 该JT809协议是参考[MessagePack-CSharp](https://github.com/neuecc/MessagePack-CSharp)一款二进制序列化器,站在巨人的肩膀上搬砖就是爽歪歪。 | ||||
> 这GB的文档不得不说,太坑了。。。 | |||||
## JT809协议消息对照表 | ## JT809协议消息对照表 | ||||
### 链路管理类 | |||||
|序号|消息ID|完成情况|消息体名称| | |序号|消息ID|完成情况|消息体名称| | ||||
|:------:|:------:|:------:|:------:| | |:------:|:------:|:------:|:------:| | ||||
| 1 | 0x1001 | √ | 主链路登录请求消息 | | |||||
| 2 | 0x1002 | √ | 主链路登录应答消息 | | |||||
| 3 | 0x1003 | √ | 主链路注销请求消息 | | |||||
| 4 | 0x1004 | √ | 主链路注销应答消息 | | |||||
| 5 | 0x1005 | √ | 主链路连接保持请求消息 | | |||||
| 6 | 0x1006 | √ | 主链路连接保持应答消息 | | |||||
| 7 | 0x1007 | √ | 主链路断开通知消息 | | |||||
| 8 | 0x1008 | √ | 下级平台主动关闭链路通知消息 | | |||||
| 9 | 0x9001 | √ | 从链路连接请求消息 | | |||||
| 10 | 0x9002 | √ | 从链路连接应答消息 | | |||||
| 11 | 0x9003 | √ | 从链路注销请求消息 | | |||||
| 12 | 0x9004 | √ | 从链路注销应答消息 | | |||||
| 13 | 0x9005 | √ | 从链路连接保持请求消息 | | |||||
| 14 | 0x9006 | √ | 从链路连接保持应答消息 | | |||||
| 15 | 0x9007 | √ | 从链路断开通知消息 | | |||||
| 16 | 0x9008 | √ | 上级平台主动关闭链路通知消息 | | |||||
| 17 | 0x9101 | √ | 接收定位信息数量通知消息 | | |||||
| 1 | 0x1001 | √ | 主链路登录请求消息 | | |||||
| 2 | 0x1002 | √ | 主链路登录应答消息 | | |||||
| 3 | 0x1003 | √ | 主链路注销请求消息 | | |||||
| 4 | 0x1004 | √ | 主链路注销应答消息 | | |||||
| 5 | 0x1005 | √ | 主链路连接保持请求消息 | | |||||
| 6 | 0x1006 | √ | 主链路连接保持应答消息 | | |||||
| 7 | 0x1007 | √ | 主链路断开通知消息 | | |||||
| 8 | 0x1008 | √ | 下级平台主动关闭链路通知消息 | | |||||
| 9 | 0x9001 | √ | 从链路连接请求消息 | | |||||
| 10 | 0x9002 | √ | 从链路连接应答消息 | | |||||
| 11 | 0x9003 | √ | 从链路注销请求消息 | | |||||
| 12 | 0x9004 | √ | 从链路注销应答消息 | | |||||
| 13 | 0x9005 | √ | 从链路连接保持请求消息 | | |||||
| 14 | 0x9006 | √ | 从链路连接保持应答消息 | | |||||
| 15 | 0x9007 | √ | 从链路断开通知消息 | | |||||
| 16 | 0x9008 | √ | 上级平台主动关闭链路通知消息 | | |||||
### 信息统计类 | |||||
|序号|消息ID|完成情况|消息体名称| | |||||
|:------:|:------:|:------:|:------:| | |||||
| 1 | 0x9101 | √ |接收定位信息数量通知消息 | | |||||
### 车辆动态信息交换 | |||||
#### 主链路动态信息交换消息 | |||||
|序号|消息ID|完成情况|消息体名称| | |||||
|:------:|:------:|:------:|:------:| | |||||
| 1 | 0x1200 | √ | 主链路动态信息交换消息 | | |||||
| 2 | 0x1201 | √ | 上传车辆注册信息 | |
@@ -0,0 +1,54 @@ | |||||
using JT809.Protocol; | |||||
using JT809.Protocol.JT809Extensions; | |||||
using JT809.Protocol.JT809MessageBody; | |||||
using JT809.Protocol.JT809SubMessageBody; | |||||
using JT809.Protocol.JT809Exceptions; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
namespace JT809.Protocol.Test.JT809MessageBody | |||||
{ | |||||
public class JT809_0x1200Test | |||||
{ | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT809_0x1200 jT809_0X1200 = new JT809_0x1200(); | |||||
jT809_0X1200.VehicleNo= "粤A12345"; | |||||
jT809_0X1200.VehicleColor = JT809Enums.JT809VehicleColorType.蓝色; | |||||
jT809_0X1200.SubBusinessType = JT809Enums.JT809SubBusinessType.UP_EXG_MSG_REGISTER; | |||||
jT809_0X1200.DataLength = 61; | |||||
JT809_0x1200_0x1201 jT809_0X1200_0X1201 = new JT809_0x1200_0x1201 | |||||
{ | |||||
PlateformId = "1111111111", | |||||
ProducerId = "1111111111", | |||||
TerminalId = "11111AA", | |||||
TerminalModelType = "11111111", | |||||
TerminalSimCode = "222222222222" | |||||
}; | |||||
jT809_0X1200.JT809SubBodies = jT809_0X1200_0X1201; | |||||
var hex = JT809Serializer.Serialize(jT809_0X1200).ToHexString(); | |||||
//"D4 C1 41 31 32 33 34 35 00 00 00 00 00 00 00 00 00 00 00 00 00 01 12 01 00 00 00 3D 00 00 00 00 00 00 00 42 3A 35 C7 00 00 00 00 00 00 00 42 3A 35 C7 31 31 31 31 31 31 31 31 00 00 00 00 00 00 00 00 00 00 00 00 31 31 31 31 31 41 41 32 32 32 32 32 32 32 32 32 32 32 32" | |||||
//"D4 C1 41 31 32 33 34 35 00 00 00 00 00 00 00 00 00 00 00 00 00 01 12 01 00 00 00 3D 00 00 00 00 00 00 00 42 3A 35 C7 00 00 00 00 00 00 00 42 3A 35 C7 31 31 31 31 31 31 31 31 00 00 00 00 00 00 00 00 00 00 00 00 31 31 31 31 31 61 61 32 32 32 32 32 32 32 32 32 32 32 32" | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var bytes = "D4 C1 41 31 32 33 34 35 00 00 00 00 00 00 00 00 00 00 00 00 00 01 12 01 00 00 00 3D 00 00 00 00 00 00 00 42 3A 35 C7 00 00 00 00 00 00 00 42 3A 35 C7 31 31 31 31 31 31 31 31 00 00 00 00 00 00 00 00 00 00 00 00 31 31 31 31 31 41 41 32 32 32 32 32 32 32 32 32 32 32 32".ToHexBytes(); | |||||
JT809_0x1200 jT809_0X9001 = JT809Serializer.Deserialize<JT809_0x1200>(bytes); | |||||
Assert.Equal("粤A12345", jT809_0X9001.VehicleNo); | |||||
Assert.Equal(JT809Enums.JT809VehicleColorType.蓝色, jT809_0X9001.VehicleColor); | |||||
Assert.Equal(JT809Enums.JT809SubBusinessType.UP_EXG_MSG_REGISTER, jT809_0X9001.SubBusinessType); | |||||
Assert.Equal((ushort)61, jT809_0X9001.DataLength); | |||||
JT809_0x1200_0x1201 jT809_0X1200_0X1201 = jT809_0X9001.JT809SubBodies as JT809_0x1200_0x1201; | |||||
Assert.Equal("1111111111", jT809_0X1200_0X1201.PlateformId); | |||||
Assert.Equal("1111111111", jT809_0X1200_0X1201.ProducerId); | |||||
Assert.Equal("11111AA", jT809_0X1200_0X1201.TerminalId); | |||||
Assert.Equal("11111111", jT809_0X1200_0X1201.TerminalModelType); | |||||
Assert.Equal("222222222222", jT809_0X1200_0X1201.TerminalSimCode); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,42 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using JT809.Protocol; | |||||
using JT809.Protocol.JT809Extensions; | |||||
using JT809.Protocol.JT809MessageBody; | |||||
using JT809.Protocol.JT809Exceptions; | |||||
using JT809.Protocol.JT809SubMessageBody; | |||||
namespace JT809.Protocol.Test.JT809SubMessageBody | |||||
{ | |||||
public class JT809_0x1200_0x1201Test | |||||
{ | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT809_0x1200_0x1201 jT809_0X1200_0X1201 = new JT809_0x1200_0x1201 | |||||
{ | |||||
PlateformId= "1111111111", | |||||
ProducerId= "1111111111", | |||||
TerminalId= "11111AA", | |||||
TerminalModelType= "11111111", | |||||
TerminalSimCode= "222222222222" | |||||
}; | |||||
var hex = JT809Serializer.Serialize(jT809_0X1200_0X1201).ToHexString(); | |||||
//"00 00 00 00 00 00 00 42 3A 35 C7 00 00 00 00 00 00 00 42 3A 35 C7 31 31 31 31 31 31 31 31 00 00 00 00 00 00 00 00 00 00 00 00 31 31 31 31 31 41 41 32 32 32 32 32 32 32 32 32 32 32 32" | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var bytes = "00 00 00 00 00 00 00 42 3A 35 C7 00 00 00 00 00 00 00 42 3A 35 C7 31 31 31 31 31 31 31 31 00 00 00 00 00 00 00 00 00 00 00 00 31 31 31 31 31 41 41 32 32 32 32 32 32 32 32 32 32 32 32".ToHexBytes(); | |||||
JT809_0x1200_0x1201 jT809_0X1200_0X1201 = JT809Serializer.Deserialize<JT809_0x1200_0x1201>(bytes); | |||||
Assert.Equal("1111111111", jT809_0X1200_0X1201.PlateformId); | |||||
Assert.Equal("1111111111", jT809_0X1200_0X1201.ProducerId); | |||||
Assert.Equal("11111AA", jT809_0X1200_0X1201.TerminalId); | |||||
Assert.Equal("11111111", jT809_0X1200_0X1201.TerminalModelType); | |||||
Assert.Equal("222222222222", jT809_0X1200_0X1201.TerminalSimCode); | |||||
} | |||||
} | |||||
} |
@@ -37,6 +37,7 @@ | |||||
<Compile Include="JT809Enums\JT809ErrorCode.cs" /> | <Compile Include="JT809Enums\JT809ErrorCode.cs" /> | ||||
<Compile Include="JT809Enums\JT809BusinessType.cs" /> | <Compile Include="JT809Enums\JT809BusinessType.cs" /> | ||||
<Compile Include="JT809Enums\JT809SubBusinessType.cs" /> | <Compile Include="JT809Enums\JT809SubBusinessType.cs" /> | ||||
<Compile Include="JT809Enums\JT809VehicleColorType.cs" /> | |||||
<Compile Include="JT809Enums\JT809_0x1002_Result.cs" /> | <Compile Include="JT809Enums\JT809_0x1002_Result.cs" /> | ||||
<Compile Include="JT809Enums\JT809_0x1007_ErrorCode.cs" /> | <Compile Include="JT809Enums\JT809_0x1007_ErrorCode.cs" /> | ||||
<Compile Include="JT809Enums\JT809_0x9007_ReasonCode.cs" /> | <Compile Include="JT809Enums\JT809_0x9007_ReasonCode.cs" /> | ||||
@@ -1,40 +0,0 @@ | |||||
using JT809.Protocol.ProtocolPacket; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.Protocol.Configs | |||||
{ | |||||
public class JT809Config | |||||
{ | |||||
public JT809Config() { } | |||||
public JT809Config(uint sessionId, JT809Version jt809Version) | |||||
{ | |||||
SessionId = sessionId; | |||||
JT809Version = jt809Version; | |||||
} | |||||
public JT809Config(uint sessionId, JT809Version jt809Version, JT809EncryptConfig encryptConfig) | |||||
{ | |||||
SessionId = sessionId; | |||||
JT809Version = jt809Version; | |||||
JT809EncryptConfig = encryptConfig; | |||||
} | |||||
/// <summary> | |||||
/// 下级平台接入码,上级平台给下级平台分配唯一标识码。 | |||||
/// </summary> | |||||
public uint SessionId { get; set; } | |||||
/// <summary> | |||||
/// 加密配置 | |||||
/// </summary> | |||||
public JT809EncryptConfig JT809EncryptConfig { get; set; } | |||||
/// <summary> | |||||
/// 版本号 | |||||
/// </summary> | |||||
public JT809Version JT809Version { get; set; } = new JT809Version(); | |||||
} | |||||
} |
@@ -123,6 +123,7 @@ namespace JT809.Protocol.JT809Enums | |||||
///主链路动态信息交换消息 | ///主链路动态信息交换消息 | ||||
///</summary> | ///</summary> | ||||
[Description("主链路动态信息交换消息")] | [Description("主链路动态信息交换消息")] | ||||
[JT809BodiesType(typeof(JT809_0x1200))] | |||||
UP_EXG_MSG = 0x1200, | UP_EXG_MSG = 0x1200, | ||||
///<summary> | ///<summary> | ||||
///从链路动态信息交换消息 | ///从链路动态信息交换消息 | ||||
@@ -12,6 +12,8 @@ namespace JT809.Protocol.JT809Enums | |||||
SerializeError=1004, | SerializeError=1004, | ||||
DeserializeError=1005, | DeserializeError=1005, | ||||
HeaderParseError=1006, | HeaderParseError=1006, | ||||
BodiesParseError=1007 | |||||
BodiesParseError=1007, | |||||
SubBodiesParseError = 1008, | |||||
GetAttributeError=1009 | |||||
} | } | ||||
} | } |
@@ -1,4 +1,6 @@ | |||||
using System.ComponentModel; | |||||
using JT809.Protocol.JT809Attributes; | |||||
using JT809.Protocol.JT809SubMessageBody; | |||||
using System.ComponentModel; | |||||
namespace JT809.Protocol.JT809Enums | namespace JT809.Protocol.JT809Enums | ||||
{ | { | ||||
@@ -12,6 +14,7 @@ namespace JT809.Protocol.JT809Enums | |||||
/// 上传车辆注册信息 | /// 上传车辆注册信息 | ||||
/// </summary> | /// </summary> | ||||
[Description("上传车辆注册信息")] | [Description("上传车辆注册信息")] | ||||
[JT809BodiesType(typeof(JT809_0x1200_0x1201))] | |||||
UP_EXG_MSG_REGISTER = 0x1201, | UP_EXG_MSG_REGISTER = 0x1201, | ||||
///<summary> | ///<summary> | ||||
///实时上传车辆定位信息 | ///实时上传车辆定位信息 | ||||
@@ -0,0 +1,18 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.Protocol.JT809Enums | |||||
{ | |||||
/// <summary> | |||||
/// 车辆颜色,按照 JT/T415-2006 中5.4.12 的规定 | |||||
/// </summary> | |||||
public enum JT809VehicleColorType:byte | |||||
{ | |||||
蓝色=0x01, | |||||
黄色=0x02, | |||||
黑色 = 0x03, | |||||
白色 = 0x04, | |||||
其他 =0x09, | |||||
} | |||||
} |
@@ -58,7 +58,7 @@ namespace JT809.Protocol.JT809Extensions | |||||
public static string ReadStringLittle(ReadOnlySpan<byte> read, ref int offset, int len) | public static string ReadStringLittle(ReadOnlySpan<byte> read, ref int offset, int len) | ||||
{ | { | ||||
string value = encoding.GetString(read.Slice(offset, len).ToArray()); | string value = encoding.GetString(read.Slice(offset, len).ToArray()); | ||||
offset += value.Length; | |||||
offset += len; | |||||
return value.Trim('\0'); | return value.Trim('\0'); | ||||
} | } | ||||
@@ -69,11 +69,6 @@ namespace JT809.Protocol.JT809Extensions | |||||
return value.Trim('\0'); | return value.Trim('\0'); | ||||
} | } | ||||
//public static void WriteLatLng(byte[] write, int offset,double latlng) | |||||
//{ | |||||
// WriteLittle(write, (int)(Math.Pow(10, 6) * latlng), offset, 4); | |||||
//} | |||||
public static long ReadBCD(ReadOnlySpan<byte> buf, ref int offset, int len) | public static long ReadBCD(ReadOnlySpan<byte> buf, ref int offset, int len) | ||||
{ | { | ||||
long result = 0; | long result = 0; | ||||
@@ -164,15 +159,42 @@ namespace JT809.Protocol.JT809Extensions | |||||
return temp.ToArray(); | return temp.ToArray(); | ||||
} | } | ||||
public static int WriteLittle(ref byte[] write, int offset, DateTime date) | |||||
/// <summary> | |||||
/// 数字编码 大端模式、高位在前 | |||||
/// </summary> | |||||
/// <param name="read"></param> | |||||
/// <param name="offset"></param> | |||||
/// <param name="len"></param> | |||||
/// <returns></returns> | |||||
public static string ReadBigNumberLittle(ReadOnlySpan<byte> read, ref int offset, int len) | |||||
{ | { | ||||
write[offset] = ((byte)(date.Year - DateLimitYear)).ToBcdByte(); | |||||
write[offset + 1] = ((byte)(date.Month)).ToBcdByte(); | |||||
write[offset + 2] = ((byte)(date.Day)).ToBcdByte(); | |||||
write[offset + 3] = ((byte)(date.Hour)).ToBcdByte(); | |||||
write[offset + 4] = ((byte)(date.Minute)).ToBcdByte(); | |||||
write[offset + 5] = ((byte)(date.Second)).ToBcdByte(); | |||||
return 6; | |||||
ulong result = 0; | |||||
for (int i = 0; i < len; i++) | |||||
{ | |||||
ulong currentData = (ulong)read[offset+i] << (8 * (len - i - 1)); | |||||
result += currentData; | |||||
} | |||||
offset += len; | |||||
return result.ToString(); | |||||
} | |||||
/// <summary> | |||||
/// 数字编码 小端模式、低位在前 | |||||
/// </summary> | |||||
/// <param name="read"></param> | |||||
/// <param name="offset"></param> | |||||
/// <param name="len"></param> | |||||
/// <returns></returns> | |||||
public static string ReadLowNumberLittle(ReadOnlySpan<byte> read, ref int offset, int len) | |||||
{ | |||||
ulong result = 0; | |||||
for (int i = 0; i < len; i++) | |||||
{ | |||||
ulong currentData = (ulong)read[offset+i] << (8 * i); | |||||
result += currentData; | |||||
} | |||||
offset += len; | |||||
return result.ToString(); | |||||
} | } | ||||
public static int WriteDateTime6Little(IMemoryOwner<byte> memoryOwner, int offset, DateTime date) | public static int WriteDateTime6Little(IMemoryOwner<byte> memoryOwner, int offset, DateTime date) | ||||
@@ -186,15 +208,6 @@ namespace JT809.Protocol.JT809Extensions | |||||
return 6; | return 6; | ||||
} | } | ||||
public static int WriteDateLittle(ref byte[] write, int offset, DateTime date) | |||||
{ | |||||
write[offset] = (byte)(date.Year >> 8); | |||||
write[offset + 1] = (byte)date.Year; | |||||
write[offset + 2] = ((byte)(date.Month)).ToBcdByte(); | |||||
write[offset + 3] = ((byte)(date.Day)).ToBcdByte(); | |||||
return 4; | |||||
} | |||||
public static int WriteDateTime4Little(IMemoryOwner<byte> memoryOwner, int offset, DateTime date) | public static int WriteDateTime4Little(IMemoryOwner<byte> memoryOwner, int offset, DateTime date) | ||||
{ | { | ||||
memoryOwner.Memory.Span[offset] = (byte)(date.Year >> 8); | memoryOwner.Memory.Span[offset] = (byte)(date.Year >> 8); | ||||
@@ -204,15 +217,6 @@ namespace JT809.Protocol.JT809Extensions | |||||
return 4; | return 4; | ||||
} | } | ||||
public static int WriteLittle(ref byte[] write, int offset, int data) | |||||
{ | |||||
write[offset] = (byte)(data >> 24); | |||||
write[offset + 1] = (byte)(data >> 16); | |||||
write[offset + 2] = (byte)(data >> 8); | |||||
write[offset + 3] = (byte)data; | |||||
return 4; | |||||
} | |||||
public static int WriteInt32Little(IMemoryOwner<byte> memoryOwner, int offset, int data) | public static int WriteInt32Little(IMemoryOwner<byte> memoryOwner, int offset, int data) | ||||
{ | { | ||||
memoryOwner.Memory.Span[offset] = (byte)(data >> 24); | memoryOwner.Memory.Span[offset] = (byte)(data >> 24); | ||||
@@ -244,13 +248,6 @@ namespace JT809.Protocol.JT809Extensions | |||||
return 8; | return 8; | ||||
} | } | ||||
public static int WriteUInt16Little(ref byte[] write, int offset, ushort data) | |||||
{ | |||||
write[offset] = (byte)(data >> 8); | |||||
write[offset + 1] = (byte)data; | |||||
return 2; | |||||
} | |||||
public static int WriteUInt16Little(IMemoryOwner<byte> memoryOwner, int offset, ushort data) | public static int WriteUInt16Little(IMemoryOwner<byte> memoryOwner, int offset, ushort data) | ||||
{ | { | ||||
memoryOwner.Memory.Span[offset] = (byte)(data >> 8); | memoryOwner.Memory.Span[offset] = (byte)(data >> 8); | ||||
@@ -258,12 +255,6 @@ namespace JT809.Protocol.JT809Extensions | |||||
return 2; | return 2; | ||||
} | } | ||||
public static int WriteLittle(ref byte[] write, int offset, byte data) | |||||
{ | |||||
write[offset] = data; | |||||
return 1; | |||||
} | |||||
public static int WriteByteLittle(IMemoryOwner<byte> memoryOwner, int offset, byte data) | public static int WriteByteLittle(IMemoryOwner<byte> memoryOwner, int offset, byte data) | ||||
{ | { | ||||
memoryOwner.Memory.Span[offset] = data; | memoryOwner.Memory.Span[offset] = data; | ||||
@@ -276,24 +267,32 @@ namespace JT809.Protocol.JT809Extensions | |||||
return data.Length; | return data.Length; | ||||
} | } | ||||
public static int WriteLittle(ref byte[] write, int offset, byte[] data) | |||||
{ | |||||
Buffer.BlockCopy(data, 0, write, offset, data.Length); | |||||
return data.Length; | |||||
} | |||||
public static int WriteLittle(ref byte[] write, int offset, string data) | |||||
public static int WriteStringLittle(IMemoryOwner<byte> memoryOwner, int offset, string data) | |||||
{ | { | ||||
byte[] codeBytes = encoding.GetBytes(data); | byte[] codeBytes = encoding.GetBytes(data); | ||||
Buffer.BlockCopy(codeBytes, 0, write, offset, codeBytes.Length); | |||||
CopyTo(codeBytes, memoryOwner.Memory.Span, offset); | |||||
return codeBytes.Length; | return codeBytes.Length; | ||||
} | } | ||||
public static int WriteStringLittle(IMemoryOwner<byte> memoryOwner, int offset, string data) | |||||
public static int WriteStringLittle(IMemoryOwner<byte> memoryOwner, int offset, string data, int len) | |||||
{ | { | ||||
byte[] codeBytes = encoding.GetBytes(data); | |||||
CopyTo(codeBytes, memoryOwner.Memory.Span, offset); | |||||
return codeBytes.Length; | |||||
byte[] bytes = null; | |||||
if (string.IsNullOrEmpty(data)) | |||||
{ | |||||
bytes = new byte[0]; | |||||
} | |||||
else | |||||
{ | |||||
bytes = encoding.GetBytes(data); | |||||
} | |||||
byte[] rBytes = new byte[len]; | |||||
for (int i = 0; i < bytes.Length; i++) | |||||
{ | |||||
if (i >= len) break; | |||||
rBytes[i] = bytes[i]; | |||||
} | |||||
CopyTo(rBytes, memoryOwner.Memory.Span, offset); | |||||
return rBytes.Length; | |||||
} | } | ||||
public static int WriteStringPadLeftLittle(IMemoryOwner<byte> memoryOwner, int offset, string data, int len) | public static int WriteStringPadLeftLittle(IMemoryOwner<byte> memoryOwner, int offset, string data, int len) | ||||
@@ -332,6 +331,44 @@ namespace JT809.Protocol.JT809Extensions | |||||
return digit; | return digit; | ||||
} | } | ||||
/// <summary> | |||||
/// 数字编码 大端模式、高位在前 | |||||
/// </summary> | |||||
/// <param name="memoryOwner"></param> | |||||
/// <param name="offset"></param> | |||||
/// <param name="data"></param> | |||||
/// <param name="len"></param> | |||||
/// <returns></returns> | |||||
public static int WriteBigNumberLittle(IMemoryOwner<byte> memoryOwner, int offset, string data, int len) | |||||
{ | |||||
ulong number = string.IsNullOrEmpty(data) ? 0 : (ulong)double.Parse(data); | |||||
for (int i = len - 1; i >= 0; i--) | |||||
{ | |||||
memoryOwner.Memory.Span[offset+i] = (byte)(number & 0xFF); //取低8位 | |||||
number = number >> 8; | |||||
} | |||||
return len; | |||||
} | |||||
/// <summary> | |||||
/// 数字编码 小端模式、低位在前 | |||||
/// </summary> | |||||
/// <param name="memoryOwner"></param> | |||||
/// <param name="offset"></param> | |||||
/// <param name="data"></param> | |||||
/// <param name="len"></param> | |||||
/// <returns></returns> | |||||
public static int WriteLowNumberLittle(IMemoryOwner<byte> memoryOwner, int offset, string data, int len) | |||||
{ | |||||
ulong number = string.IsNullOrEmpty(data) ? 0 : (ulong)double.Parse(data); | |||||
for (int i = 0; i < len; i++) | |||||
{ | |||||
memoryOwner.Memory.Span[offset + i] = (byte)(number & 0xFF); //取低8位 | |||||
number = number >> 8; | |||||
} | |||||
return len; | |||||
} | |||||
public static IEnumerable<byte> ToBytes(this string data, Encoding coding) | public static IEnumerable<byte> ToBytes(this string data, Encoding coding) | ||||
{ | { | ||||
return coding.GetBytes(data); | return coding.GetBytes(data); | ||||
@@ -0,0 +1,64 @@ | |||||
using JT809.Protocol.JT809Attributes; | |||||
using JT809.Protocol.JT809Enums; | |||||
using JT809.Protocol.JT809Exceptions; | |||||
using JT809.Protocol.JT809Extensions; | |||||
using JT809.Protocol.JT809MessageBody; | |||||
using System; | |||||
using System.Buffers; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.Protocol.JT809Formatters.JT809MessageBodyFormatters | |||||
{ | |||||
public class JT809_0x1200Formatter : IJT809Formatter<JT809_0x1200> | |||||
{ | |||||
public JT809_0x1200 Deserialize(ReadOnlySpan<byte> bytes, out int readSize) | |||||
{ | |||||
int offset = 0; | |||||
JT809_0x1200 jT809_0X1200 = new JT809_0x1200(); | |||||
jT809_0X1200.VehicleNo =JT809BinaryExtensions.ReadStringLittle(bytes, ref offset,21); | |||||
jT809_0X1200.VehicleColor = (JT809VehicleColorType)JT809BinaryExtensions.ReadByteLittle(bytes, ref offset); | |||||
jT809_0X1200.SubBusinessType = (JT809SubBusinessType)JT809BinaryExtensions.ReadUInt16Little(bytes, ref offset); | |||||
jT809_0X1200.DataLength = JT809BinaryExtensions.ReadUInt32Little(bytes, ref offset); | |||||
//JT809.Protocol.JT809Enums.JT809BusinessType 映射对应消息特性 | |||||
JT809BodiesTypeAttribute jT809SubBodiesTypeAttribute = jT809_0X1200.SubBusinessType.GetAttribute<JT809BodiesTypeAttribute>(); | |||||
if (jT809SubBodiesTypeAttribute == null) | |||||
{ | |||||
throw new JT809Exception(JT809ErrorCode.GetAttributeError, $"JT809BodiesTypeAttribute Not Found>{jT809_0X1200.SubBusinessType.ToString()}"); | |||||
} | |||||
try | |||||
{ | |||||
jT809_0X1200.JT809SubBodies = JT809FormatterResolverExtensions.JT809DynamicDeserialize(JT809FormatterExtensions.GetFormatter(jT809SubBodiesTypeAttribute.JT809BodiesType), bytes.Slice(offset, (int)jT809_0X1200.DataLength), out readSize); | |||||
} | |||||
catch | |||||
{ | |||||
throw new JT809Exception(JT809ErrorCode.SubBodiesParseError, $"SubBusinessType>{jT809_0X1200.SubBusinessType.ToString()}"); | |||||
} | |||||
readSize = offset; | |||||
return jT809_0X1200; | |||||
} | |||||
public int Serialize(IMemoryOwner<byte> memoryOwner, int offset, JT809_0x1200 value) | |||||
{ | |||||
offset += JT809BinaryExtensions.WriteStringLittle(memoryOwner, offset, value.VehicleNo, 21); | |||||
offset += JT809BinaryExtensions.WriteByteLittle(memoryOwner, offset, (byte)value.VehicleColor); | |||||
offset += JT809BinaryExtensions.WriteUInt16Little(memoryOwner, offset, (ushort)value.SubBusinessType); | |||||
offset += JT809BinaryExtensions.WriteUInt32Little(memoryOwner, offset, value.DataLength); | |||||
//JT809.Protocol.JT809Enums.JT809BusinessType 映射对应消息特性 | |||||
JT809BodiesTypeAttribute jT809SubBodiesTypeAttribute = value.SubBusinessType.GetAttribute<JT809BodiesTypeAttribute>(); | |||||
if (jT809SubBodiesTypeAttribute == null) | |||||
{ | |||||
throw new JT809Exception(JT809ErrorCode.GetAttributeError, $"JT809BodiesTypeAttribute Not Found>{value.SubBusinessType.ToString()}"); | |||||
} | |||||
try | |||||
{ | |||||
offset = JT809FormatterResolverExtensions.JT809DynamicSerialize(JT809FormatterExtensions.GetFormatter(jT809SubBodiesTypeAttribute.JT809BodiesType), memoryOwner, offset, value.JT809SubBodies); | |||||
} | |||||
catch | |||||
{ | |||||
throw new JT809Exception(JT809ErrorCode.SubBodiesParseError, $"SubBusinessType>{value.SubBusinessType.ToString()}"); | |||||
} | |||||
return offset; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,37 @@ | |||||
using JT809.Protocol.JT809Extensions; | |||||
using JT809.Protocol.JT809SubMessageBody; | |||||
using JT809.Protocol.JT809Enums; | |||||
using System; | |||||
using System.Buffers; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.Protocol.JT809Formatters.JT809SubMessageBodyFormatters | |||||
{ | |||||
public class JT809_0x1200_0x1201Formatter : IJT809Formatter<JT809_0x1200_0x1201> | |||||
{ | |||||
public JT809_0x1200_0x1201 Deserialize(ReadOnlySpan<byte> bytes, out int readSize) | |||||
{ | |||||
int offset = 0; | |||||
JT809_0x1200_0x1201 jT809_0X1200_0X1201 = new JT809_0x1200_0x1201(); | |||||
jT809_0X1200_0X1201.PlateformId= JT809BinaryExtensions.ReadBigNumberLittle(bytes, ref offset, 11); | |||||
jT809_0X1200_0X1201.ProducerId = JT809BinaryExtensions.ReadBigNumberLittle(bytes, ref offset, 11); | |||||
jT809_0X1200_0X1201.TerminalModelType = JT809BinaryExtensions.ReadStringLittle(bytes, ref offset, 20); | |||||
jT809_0X1200_0X1201.TerminalId = JT809BinaryExtensions.ReadStringLittle(bytes, ref offset, 7); | |||||
jT809_0X1200_0X1201.TerminalId = jT809_0X1200_0X1201.TerminalId.ToUpper(); | |||||
jT809_0X1200_0X1201.TerminalSimCode = JT809BinaryExtensions.ReadStringLittle(bytes, ref offset, 12); | |||||
readSize = offset; | |||||
return jT809_0X1200_0X1201; | |||||
} | |||||
public int Serialize(IMemoryOwner<byte> memoryOwner, int offset, JT809_0x1200_0x1201 value) | |||||
{ | |||||
offset += JT809BinaryExtensions.WriteBigNumberLittle(memoryOwner, offset, value.PlateformId,11); | |||||
offset += JT809BinaryExtensions.WriteBigNumberLittle(memoryOwner, offset, value.ProducerId, 11); | |||||
offset += JT809BinaryExtensions.WriteStringPadRightLittle(memoryOwner, offset, value.TerminalModelType, 20); | |||||
offset += JT809BinaryExtensions.WriteStringPadRightLittle(memoryOwner, offset, value.TerminalId.ToUpper(), 7); | |||||
offset += JT809BinaryExtensions.WriteStringPadRightLittle(memoryOwner, offset, value.TerminalSimCode, 12); | |||||
return offset; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,45 @@ | |||||
using JT809.Protocol.JT809Attributes; | |||||
using JT809.Protocol.JT809Enums; | |||||
using JT809.Protocol.JT809Formatters.JT809MessageBodyFormatters; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.Protocol.JT809MessageBody | |||||
{ | |||||
/// <summary> | |||||
/// 主链路车辆动态信息交换业务 | |||||
/// <para>链路类型:主链路</para> | |||||
/// <para>消息方向:下级平台往上级平台</para> | |||||
/// <para>业务数据类型标识:UP_EXG_MSG</para> | |||||
/// <para>描述:下级平台向上级平台发送车辆动态信息交换业务数据包</para> | |||||
/// </summary> | |||||
[JT809Formatter(typeof(JT809_0x1200Formatter))] | |||||
public class JT809_0x1200:JT809Bodies | |||||
{ | |||||
/// <summary> | |||||
/// 车牌号 | |||||
/// </summary> | |||||
public string VehicleNo { get; set; } | |||||
/// <summary> | |||||
/// 车辆颜色 | |||||
/// </summary> | |||||
public JT809VehicleColorType VehicleColor { get; set; } | |||||
/// <summary> | |||||
/// 子业务类型标识 | |||||
/// </summary> | |||||
public JT809SubBusinessType SubBusinessType { get; set; } | |||||
/// <summary> | |||||
/// 后续数据长度 | |||||
/// </summary> | |||||
public uint DataLength { get; set; } | |||||
/// <summary> | |||||
/// 数据部分 | |||||
/// </summary> | |||||
public byte[] Data { get; set; } | |||||
/// <summary> | |||||
/// 子业务数据体 | |||||
/// </summary> | |||||
public JT809SubBodies JT809SubBodies { get; set; } | |||||
} | |||||
} |
@@ -39,7 +39,7 @@ namespace JT809.Protocol | |||||
{ | { | ||||
var formatter = JT809FormatterExtensions.GetFormatter<T>(); | var formatter = JT809FormatterExtensions.GetFormatter<T>(); | ||||
var pool = MemoryPool<byte>.Shared; | var pool = MemoryPool<byte>.Shared; | ||||
IMemoryOwner<byte> buffer = pool.Rent(10240); | |||||
IMemoryOwner<byte> buffer = pool.Rent(4096); | |||||
try | try | ||||
{ | { | ||||
var len = formatter.Serialize(buffer, 0, obj); | var len = formatter.Serialize(buffer, 0, obj); | ||||
@@ -0,0 +1,15 @@ | |||||
using JT809.Protocol.JT809Enums; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.Protocol | |||||
{ | |||||
/// <summary> | |||||
/// 子业务数据体 | |||||
/// </summary> | |||||
public abstract class JT809SubBodies | |||||
{ | |||||
} | |||||
} |
@@ -0,0 +1,40 @@ | |||||
using JT809.Protocol.JT809Attributes; | |||||
using JT809.Protocol.JT809Enums; | |||||
using JT809.Protocol.JT809Formatters.JT809SubMessageBodyFormatters; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.Protocol.JT809SubMessageBody | |||||
{ | |||||
/// <summary> | |||||
/// 上传车辆注册信息消息 | |||||
/// <para>子业务类型标识:UP_ EXG_ MSG_ REGISTER</para> | |||||
/// <para>描述:监控平台收到车载终端鉴权信息后,启动本命令向上级监管平台上传该车辆注册信息.各级监管平台再逐级向上级平台上传该信息</para> | |||||
/// <para>本条消息服务端无需应答</para> | |||||
/// </summary> | |||||
[JT809Formatter(typeof(JT809_0x1200_0x1201Formatter))] | |||||
public class JT809_0x1200_0x1201:JT809SubBodies | |||||
{ | |||||
/// <summary> | |||||
/// 平台唯一编码 | |||||
/// </summary> | |||||
public string PlateformId { get; set; } | |||||
/// <summary> | |||||
/// 车载终端厂商唯一编码 | |||||
/// </summary> | |||||
public string ProducerId { get; set; } | |||||
/// <summary> | |||||
/// 车载终端型号,不是 8 位时以“\0”终结 | |||||
/// </summary> | |||||
public string TerminalModelType { get; set; } | |||||
/// <summary> | |||||
/// 车载终端编号,大写字母和数字组成 | |||||
/// </summary> | |||||
public string TerminalId { get; set; } | |||||
/// <summary> | |||||
/// 车载终端 SIM 卡电话号码。号码不是12 位,则在前补充数字 0. | |||||
/// </summary> | |||||
public string TerminalSimCode { get; set; } | |||||
} | |||||
} |
@@ -1,47 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Text; | |||||
namespace JT809.Protocol.ProtocolPacket.Bodies.Master | |||||
{ | |||||
public sealed class Body_UP_CONNECT_REQ : MessageBody | |||||
{ | |||||
public uint UserId { get; private set; } | |||||
public byte[] Password { get; private set; } | |||||
public byte[] DownLinkIp { get; private set; } | |||||
public ushort DownLinkPort { get; private set; } | |||||
public Body_UP_CONNECT_REQ(byte[] buffer) : base(buffer) { } | |||||
public Body_UP_CONNECT_REQ(uint userId, byte[] password, byte[] downLinkIp, ushort downLinkPort) | |||||
: base(userId, password, downLinkIp, downLinkPort) { } | |||||
protected override void InitializeProperties(object[] properties, int startIndex) | |||||
{ | |||||
UserId = (uint)properties[startIndex++]; | |||||
Password = SetMatchBytes((byte[])properties[startIndex++], 8); | |||||
DownLinkIp = SetMatchBytes((byte[])properties[startIndex++], 32); | |||||
DownLinkPort = (ushort)properties[startIndex++]; | |||||
} | |||||
protected override void OnInitializePropertiesFromReadBuffer(BinaryReader reader) | |||||
{ | |||||
UserId = reader.ReadUInt32Little(); | |||||
Password = reader.ReadBytes(8); | |||||
DownLinkIp = reader.ReadBytes(32); | |||||
DownLinkPort = reader.ReadUInt16Little(); | |||||
} | |||||
protected override void OnWriteToBuffer(BinaryWriter writer) | |||||
{ | |||||
writer.WriteLittle(UserId); | |||||
writer.WriteLittle(Password); | |||||
writer.WriteLittle(DownLinkIp); | |||||
writer.WriteLittle(DownLinkPort); | |||||
} | |||||
} | |||||
} |
@@ -1,65 +0,0 @@ | |||||
using System; | |||||
using System.IO; | |||||
namespace JT809.Protocol.ProtocolPacket | |||||
{ | |||||
public abstract class BufferedEntityBase : IBuffered, IBuffer | |||||
{ | |||||
public byte[] Buffer { get; protected set; } | |||||
public virtual byte[] ToBuffer() | |||||
{ | |||||
using (var stream = new MemoryStream()) | |||||
{ | |||||
var writer = new BinaryWriter(stream); | |||||
OnWriteToBuffer(writer); | |||||
var reader = new BinaryReader(stream); | |||||
stream.Seek(0, SeekOrigin.Begin); | |||||
Buffer = reader.ReadBytes((int)stream.Length);//stream.GetBuffer(); | |||||
stream.Close(); | |||||
} | |||||
return Buffer; | |||||
} | |||||
protected BufferedEntityBase(params object[] properties) | |||||
{ | |||||
InitializeProperties(properties, 0); | |||||
ToBuffer(); | |||||
} | |||||
protected BufferedEntityBase(byte[] buffer) | |||||
{ | |||||
Buffer = buffer; | |||||
InitializePropertiesFromBuffer(); | |||||
} | |||||
public static byte[] SetMatchBytes(byte[] bufferIn, int lengthMatch, byte mask = 0X00) | |||||
{ | |||||
if (bufferIn.Length != lengthMatch) | |||||
{ | |||||
var tempBuffer = new byte[lengthMatch]; | |||||
Array.ForEach(tempBuffer, b => b = mask);//TODO : Maybe mask error | |||||
var copyLength = bufferIn.Length > lengthMatch ? lengthMatch : bufferIn.Length; | |||||
Array.Copy(bufferIn, 0, tempBuffer, 0, copyLength); | |||||
return tempBuffer; | |||||
} | |||||
return bufferIn; | |||||
} | |||||
protected abstract void InitializeProperties(object[] properties, int startIndex); | |||||
protected abstract void OnInitializePropertiesFromReadBuffer(BinaryReader reader); | |||||
protected virtual void InitializePropertiesFromBuffer() | |||||
{ | |||||
using (var stream = new MemoryStream(Buffer)) | |||||
{ | |||||
var reader = new BinaryReader(stream); | |||||
OnInitializePropertiesFromReadBuffer(reader); | |||||
stream.Close(); | |||||
} | |||||
} | |||||
protected abstract void OnWriteToBuffer(BinaryWriter writer); | |||||
} | |||||
} |
@@ -1,99 +0,0 @@ | |||||
using System; | |||||
using System.IO; | |||||
using System.Linq; | |||||
namespace JT809.Protocol.ProtocolPacket | |||||
{ | |||||
public static class BinaryExtensions | |||||
{ | |||||
public static void WriteLittle(this BinaryWriter writer, ulong value) | |||||
{ | |||||
writer.Write((byte)(value >> 56)); | |||||
writer.Write((byte)(value >> 48)); | |||||
writer.Write((byte)(value >> 40)); | |||||
writer.Write((byte)(value >> 32)); | |||||
writer.Write((byte)(value >> 24)); | |||||
writer.Write((byte)(value >> 16)); | |||||
writer.Write((byte)(value >> 8)); | |||||
writer.Write((byte)(value)); | |||||
} | |||||
public static void WriteLittle(this BinaryWriter writer, uint value) | |||||
{ | |||||
writer.Write((byte)(value >> 24)); | |||||
writer.Write((byte)(value >> 16)); | |||||
writer.Write((byte)(value >> 8)); | |||||
writer.Write((byte)(value)); | |||||
} | |||||
public static void WriteLittle(this BinaryWriter writer, int value) | |||||
{ | |||||
writer.Write((byte)(value >> 24)); | |||||
writer.Write((byte)(value >> 16)); | |||||
writer.Write((byte)(value >> 8)); | |||||
writer.Write((byte)(value)); | |||||
} | |||||
public static void WriteLittle(this BinaryWriter writer, short value) | |||||
{ | |||||
writer.Write((byte)(value >> 8)); | |||||
writer.Write((byte)(value)); | |||||
} | |||||
public static void WriteLittle(this BinaryWriter writer, ushort value) | |||||
{ | |||||
writer.Write((byte)(value >> 8)); | |||||
writer.Write((byte)(value)); | |||||
} | |||||
public static void WriteLittle(this BinaryWriter writer, byte value) | |||||
{ | |||||
writer.Write(value); | |||||
} | |||||
public static void WriteLittle(this BinaryWriter writer, byte[] value) | |||||
{ | |||||
writer.Write(value); | |||||
} | |||||
public static ulong ReadUInt64Little(this BinaryReader read) | |||||
{ | |||||
var buffer = read.ReadBytes(8); | |||||
return (ulong)(buffer[7] | buffer[6] << 8 | buffer[5] << 16 | buffer[4] << 24| buffer[3] << 32 | buffer[2] << 40 | buffer[1] << 48 | buffer[0] << 56); | |||||
} | |||||
public static uint ReadUInt32Little(this BinaryReader read) | |||||
{ | |||||
var buffer = read.ReadBytes(4); | |||||
return (uint)(buffer[3] | buffer[2] << 8 | buffer[1] << 16 | buffer[0] << 24); | |||||
} | |||||
public static ushort ReadUInt16Little(this BinaryReader read) | |||||
{ | |||||
var buffer = read.ReadBytes(2); | |||||
return (ushort)(buffer[1] | buffer[0] << 8); | |||||
} | |||||
/// <summary> | |||||
/// 字节数组转16进制字符串 | |||||
/// </summary> | |||||
/// <param name="bytes"></param> | |||||
/// <param name="separator">默认 " "</param> | |||||
/// <returns></returns> | |||||
public static string ToHexString(this byte[] bytes,string separator=" ") | |||||
{ | |||||
return string.Join(separator, bytes.Select(s => s.ToString("X2"))); | |||||
} | |||||
/// <summary> | |||||
/// 16进制字符串转16进制数组 | |||||
/// </summary> | |||||
/// <param name="hexString"></param> | |||||
/// <param name="separator"></param> | |||||
/// <returns></returns> | |||||
public static byte[] ToHexBytes(this string hexString, string separator = " ") | |||||
{ | |||||
return hexString.Split(new string[] { separator }, StringSplitOptions.RemoveEmptyEntries).Select(s => Convert.ToByte(s, 16)).ToArray(); | |||||
} | |||||
} | |||||
} |
@@ -1,141 +0,0 @@ | |||||
using JT809.Protocol.Configs; | |||||
using System.Collections.Generic; | |||||
namespace JT809.Protocol.ProtocolPacket.Extensions | |||||
{ | |||||
/// <summary> | |||||
/// 包扩展方法 | |||||
/// </summary> | |||||
public static class PackageExtensions | |||||
{ | |||||
const ushort cnCRC_CCITT = 0x1021; //CRC校验多项式 | |||||
static ulong[] CRC = new ulong[256]; //建立CRC16表 | |||||
static PackageExtensions() | |||||
{ | |||||
ushort i, j; | |||||
ushort nData; | |||||
ushort nAccum; | |||||
for (i = 0; i < 256; i++) | |||||
{ | |||||
nData = (ushort)(i << 8); | |||||
nAccum = 0; | |||||
for (j = 0; j < 8; j++) | |||||
{ | |||||
if (((nData ^ nAccum) & 0x8000) > 0) | |||||
nAccum = (ushort)((nAccum << 1) ^ cnCRC_CCITT); | |||||
else | |||||
nAccum <<= 1; | |||||
nData <<= 1; | |||||
} | |||||
CRC[i] = (ulong)nAccum; | |||||
} | |||||
} | |||||
internal static byte[] Escape(this Package packege, byte[] bytes) | |||||
{ | |||||
List<byte> dataList = new List<byte>(); | |||||
dataList.Add(bytes[0]); | |||||
for (int i = 1; i < bytes.Length - 1; i++) | |||||
{ | |||||
var item = bytes[i]; | |||||
switch (item) | |||||
{ | |||||
case 0x5b: | |||||
dataList.Add(0x5a); | |||||
dataList.Add(0x01); | |||||
break; | |||||
case 0x5a: | |||||
dataList.Add(0x5a); | |||||
dataList.Add(0x02); | |||||
break; | |||||
case 0x5d: | |||||
dataList.Add(0x5e); | |||||
dataList.Add(0x01); | |||||
break; | |||||
case 0x5e: | |||||
dataList.Add(0x5e); | |||||
dataList.Add(0x02); | |||||
break; | |||||
default: | |||||
dataList.Add(item); | |||||
break; | |||||
} | |||||
} | |||||
dataList.Add(bytes[bytes.Length - 1]); | |||||
var tempBuffe = dataList.ToArray(); | |||||
return tempBuffe; | |||||
} | |||||
internal static byte[] UnEscape(this Package packege, byte[] bytes) | |||||
{ | |||||
List<byte> dataList = new List<byte>(); | |||||
dataList.Add(bytes[0]); | |||||
for (int i = 1; i < bytes.Length - 1; i++) | |||||
{ | |||||
byte first = bytes[i]; | |||||
byte second = bytes[i + 1]; | |||||
if (first == 0x5a && second == 0x01) | |||||
{ | |||||
dataList.Add(0x5b); | |||||
i++; | |||||
} | |||||
else if (first == 0x5a && second == 0x02) | |||||
{ | |||||
dataList.Add(0x5a); | |||||
i++; | |||||
} | |||||
else if (first == 0x5e && second == 0x01) | |||||
{ | |||||
dataList.Add(0x5d); | |||||
i++; | |||||
} | |||||
else if (first == 0x5e && second == 0x02) | |||||
{ | |||||
dataList.Add(0x5e); | |||||
i++; | |||||
} | |||||
else | |||||
{ | |||||
dataList.Add(first); | |||||
} | |||||
} | |||||
dataList.Add(bytes[bytes.Length - 1]); | |||||
var tempBuffe = dataList.ToArray(); | |||||
return tempBuffe; | |||||
} | |||||
/// <summary> | |||||
/// 从数据头到校验码前的 CRC 1 G-CCITT 的校验值,遵循人端排序方式的规定。 | |||||
/// </summary> | |||||
/// <param name="packege"></param> | |||||
/// <param name="ucbuf"></param> | |||||
/// <param name="offset"></param> | |||||
/// <param name="iLen"></param> | |||||
/// <returns></returns> | |||||
internal static ushort CRC16_CCITT(this Package packege, byte[] ucbuf, int offset, int iLen) | |||||
{ | |||||
ushort checkCode = 0xFFFF; | |||||
for (int j = offset; j < iLen; ++j) | |||||
{ | |||||
checkCode = (ushort)((checkCode << 8) ^ (ushort)CRC[(checkCode >> 8) ^ ucbuf[j]]); | |||||
} | |||||
return checkCode; | |||||
} | |||||
internal static byte[] Encrypt(this Package packege, byte[] buffer, int size, JT809EncryptConfig Config) | |||||
{ | |||||
if (0 == Config.Key) | |||||
{ | |||||
Config.Key = 1; | |||||
} | |||||
uint mkey = Config.M1; | |||||
if (0 == mkey) | |||||
{ | |||||
mkey = 1; | |||||
} | |||||
for (int idx = 0; idx < size; idx++) | |||||
{ | |||||
Config.Key = Config.IA1 * (Config.Key % mkey) + Config.IC1; | |||||
buffer[idx] ^= (byte)((Config.Key >> 20) & 0xFF); | |||||
} | |||||
return buffer; | |||||
} | |||||
} | |||||
} |
@@ -1,101 +0,0 @@ | |||||
using JT809.Protocol.Configs; | |||||
using JT809.Protocol.Enums; | |||||
using System; | |||||
using System.IO; | |||||
namespace JT809.Protocol.ProtocolPacket | |||||
{ | |||||
/// <summary> | |||||
/// Message Header 数据头 | |||||
/// </summary> | |||||
public class Header: BufferedEntityBase | |||||
{ | |||||
/// <summary> | |||||
/// 固定为22个字节长度 | |||||
/// </summary> | |||||
public const int HeaderFixedByteLength = 22; | |||||
/// <summary> | |||||
/// 发送计数器 | |||||
/// 占用四个字节,为发送信息的序列号,用于接收方检测是否有信息的丢失,上级平台和下级平台接自己发送数据包的个数计数,互不影响。 | |||||
/// 程序开始运行时等于零,发送第一帧数据时开始计数,到最大数后自动归零 | |||||
/// </summary> | |||||
public static uint CounterOnSendGenerater { get; private set; } | |||||
/// <summary> | |||||
/// 接收计数器 | |||||
/// 占用四个字节,为发送信息的序列号,用于接收方检测是否有信息的丢失,上级平台和下级平台接自己发送数据包的个数计数,互不影响。 | |||||
/// 程序开始运行时等于零,发送第一帧数据时开始计数,到最大数后自动归零 | |||||
/// </summary> | |||||
public static uint CounterOnRecieveGenerater { get; private set; } | |||||
/// <summary> | |||||
/// 数据长度(包括头标识、数据头、数据体和尾标识) | |||||
/// </summary> | |||||
public uint Length { get; private set; } | |||||
/// <summary> | |||||
/// 报文序列号 | |||||
/// </summary> | |||||
public uint SN { get; private set; } | |||||
/// <summary> | |||||
/// 业务数据类型 | |||||
/// </summary> | |||||
public BusinessType BusinessID { get; private set; } | |||||
/// <summary> | |||||
/// 下级平台接入码,上级平台给下级平台分配唯一标识码。 | |||||
/// </summary> | |||||
public uint SessionId { get; set; } | |||||
public JT809Version JT809Version { get; private set; } | |||||
public EncryptOpitions EncryptOpition { get; private set; } | |||||
/// <summary> | |||||
/// 数据加密的密匙,长度为 4 个字节。 | |||||
/// </summary> | |||||
public uint EncryptKey { get; private set; } = 0X00; | |||||
public Header(byte[] buffer) : base(buffer) { CounterOnRecieveGenerater++; } | |||||
internal Header(uint length, uint number, BusinessType businessID, JT809Config jt809Config) : base(length, number, businessID, jt809Config) | |||||
{ CounterOnSendGenerater++; } | |||||
public Header(uint length, BusinessType businessID, JT809Config jt809Config) : this(length, CounterOnSendGenerater, businessID, jt809Config) { } | |||||
protected override void InitializeProperties(object[] properties, int startIndex) | |||||
{ | |||||
Length = (uint)properties[0] + Package.NotDataLength; | |||||
SN = (uint)properties[1]; | |||||
BusinessID = (BusinessType)properties[2]; | |||||
JT809Config jt809Config=(JT809Config)properties[3]; | |||||
SessionId = jt809Config.SessionId; | |||||
JT809Version = jt809Config.JT809Version; | |||||
EncryptOpition = jt809Config.JT809EncryptConfig==null? EncryptOpitions.None: EncryptOpitions.Common; | |||||
if(jt809Config.JT809EncryptConfig == null) | |||||
{ | |||||
EncryptOpition = EncryptOpitions.None; | |||||
} | |||||
else | |||||
{ | |||||
EncryptOpition = EncryptOpitions.Common; | |||||
EncryptKey = jt809Config.JT809EncryptConfig.Key; | |||||
} | |||||
} | |||||
protected override void OnInitializePropertiesFromReadBuffer(BinaryReader reader) | |||||
{ | |||||
Length = reader.ReadUInt32Little(); | |||||
SN = reader.ReadUInt32Little(); | |||||
BusinessID = (BusinessType)reader.ReadUInt16Little(); | |||||
SessionId = reader.ReadUInt32Little(); | |||||
JT809Version = new JT809Version(reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); | |||||
EncryptOpition = (EncryptOpitions)reader.ReadByte(); | |||||
EncryptKey = reader.ReadUInt32Little(); | |||||
} | |||||
protected override void OnWriteToBuffer(BinaryWriter writer) | |||||
{ | |||||
writer.WriteLittle(Length); | |||||
writer.WriteLittle(SN); | |||||
writer.WriteLittle((ushort)BusinessID); | |||||
writer.WriteLittle(SessionId); | |||||
writer.WriteLittle(JT809Version.Buffer); | |||||
writer.WriteLittle((byte)EncryptOpition); | |||||
writer.WriteLittle(EncryptKey); | |||||
} | |||||
} | |||||
} |
@@ -1,11 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.Protocol.ProtocolPacket | |||||
{ | |||||
public interface IBuffer | |||||
{ | |||||
byte[] Buffer { get; } | |||||
} | |||||
} |
@@ -1,11 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.Protocol.ProtocolPacket | |||||
{ | |||||
public interface IBuffered | |||||
{ | |||||
byte[] ToBuffer(); | |||||
} | |||||
} |
@@ -1,46 +0,0 @@ | |||||
namespace JT809.Protocol.ProtocolPacket | |||||
{ | |||||
/// <summary> | |||||
/// 协议版本好标识,上下级平台之间采用的标准协议版编号; | |||||
/// 长度为 3 个字节来表示, | |||||
/// 0x01 0x02 0x0F 标识的版本号是 v1.2.15,以此类推。 | |||||
/// </summary> | |||||
public class JT809Version | |||||
{ | |||||
public byte[] Buffer { get; } = new byte[3]; | |||||
private const int MajorIndex = 0; | |||||
private const int MinorIndex = 1; | |||||
private const int BuildIndex = 2; | |||||
public byte Major | |||||
{ | |||||
get { return Buffer[MajorIndex]; } | |||||
private set { Buffer[MajorIndex] = value; } | |||||
} | |||||
public byte Minor | |||||
{ | |||||
get { return Buffer[MinorIndex]; } | |||||
private set { Buffer[MinorIndex] = value; } | |||||
} | |||||
public byte Build | |||||
{ | |||||
get { return Buffer[BuildIndex]; } | |||||
private set { Buffer[BuildIndex] = value; } | |||||
} | |||||
public JT809Version() | |||||
{ | |||||
Major = 1; | |||||
Minor = 0; | |||||
Build = 0; | |||||
} | |||||
public JT809Version(byte major, byte minor, byte buid) | |||||
{ | |||||
Major = major; | |||||
Minor = minor; | |||||
Build = buid; | |||||
} | |||||
public override string ToString() | |||||
{ | |||||
return $"{Major}.{Minor}.{Build}"; | |||||
} | |||||
} | |||||
} |
@@ -1,14 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.Protocol.ProtocolPacket.Message.MainLink | |||||
{ | |||||
/// <summary> | |||||
/// 主链路登录请求消息 | |||||
/// </summary> | |||||
public sealed class MainLink_Login: MessageBody | |||||
{ | |||||
} | |||||
} |
@@ -1,15 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT809.Protocol.ProtocolPacket | |||||
{ | |||||
/// <summary> | |||||
/// 抽象消息体 | |||||
/// </summary> | |||||
public abstract class MessageBody: BufferedEntityBase | |||||
{ | |||||
protected MessageBody(params object[] properties) : base(properties){} | |||||
protected MessageBody(byte[] buffer):base(buffer){} | |||||
} | |||||
} |
@@ -1,116 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Diagnostics; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using JT809.Protocol.Configs; | |||||
using JT809.Protocol.Enums; | |||||
using JT809.Protocol.Exceptions; | |||||
using JT809.Protocol.ProtocolPacket.Extensions; | |||||
namespace JT809.Protocol.ProtocolPacket | |||||
{ | |||||
/// <summary> | |||||
/// 数据包 | |||||
/// </summary> | |||||
public class Package: BufferedEntityBase | |||||
{ | |||||
public const int NotDataLength = 26; | |||||
public const int Crc16ByteLength = 2; | |||||
public const byte BeginFlag = 0X5B; | |||||
public const byte EndFlag = 0X5D; | |||||
public const int BeginFixedByteLength = 1; | |||||
public const int EndFixedByteLength = 1; | |||||
public JT809Config JT809Config; | |||||
public Header Header { get; private set; } | |||||
public MessageBody Body { get; private set; } | |||||
private ushort CRC16 { get; set; } | |||||
public Package(byte[] buffer) : base(buffer){} | |||||
public Package(Header header, MessageBody body, JT809Config jt809Config) : base(header, body) | |||||
{ | |||||
JT809Config = jt809Config; | |||||
} | |||||
protected override void InitializeProperties(object[] properties, int startIndex) | |||||
{ | |||||
Header = properties[0] as Header; | |||||
Body = properties[1] as MessageBody; | |||||
if (Header == null) throw new NullReferenceException($"{new StackFrame().GetMethod().Name }:Header can't be null."); | |||||
if (Body == null) throw new NullReferenceException($"{new StackFrame().GetMethod().Name }:Body can't be null."); | |||||
} | |||||
protected override void OnInitializePropertiesFromReadBuffer(BinaryReader reader) | |||||
{ | |||||
var content00 = new byte[Buffer.Length- BeginFixedByteLength-EndFixedByteLength]; | |||||
Array.Copy(Buffer, BeginFixedByteLength, content00, 0, Buffer.Length- BeginFixedByteLength-EndFixedByteLength); | |||||
var content01 = this.UnEscape(content00); | |||||
var crc16 = this.CRC16_CCITT(content01, 0, content01.Length - Crc16ByteLength); | |||||
CRC16 = BitConverter.ToUInt16(new[] { content01[content01.Length - 1], content01[content01.Length - 2] }, 0); | |||||
if (CRC16 != crc16) throw new JT809Exception(ErrorCode.CRC16CheckInvalid,$"{CRC16}-{crc16},{content01.ToHexString()}"); | |||||
Header = new Header(content01); | |||||
var bodyBuffer00 = new byte[content01.Length - Header.HeaderFixedByteLength - Crc16ByteLength]; | |||||
Array.Copy(content01, Header.HeaderFixedByteLength, bodyBuffer00, 0, bodyBuffer00.Length); | |||||
//Default | |||||
byte[] bodyBuffer01 = bodyBuffer00; | |||||
switch (Header.EncryptOpition) | |||||
{ | |||||
case EncryptOpitions.None: | |||||
break; | |||||
case EncryptOpitions.Common: | |||||
bodyBuffer01 = this.Encrypt(bodyBuffer01, bodyBuffer01.Length, JT809Config.JT809EncryptConfig); | |||||
break; | |||||
} | |||||
if (Header.Length != (bodyBuffer01.Length + NotDataLength)) throw new JT809Exception(ErrorCode.HeaderLengthNotEqualBodyLength); | |||||
Body = GenerateBody(Header.BusinessID, bodyBuffer01); | |||||
} | |||||
protected override void OnWriteToBuffer(BinaryWriter writer) | |||||
{ | |||||
writer.WriteLittle(BeginFlag); | |||||
//Dealling Code On Header | |||||
var headerBuffer00 = Header.Buffer; | |||||
//Dealling Code On Body | |||||
byte[] bodyBuffer00 = Body.Buffer; | |||||
switch (Header.EncryptOpition) | |||||
{ | |||||
case EncryptOpitions.None: | |||||
bodyBuffer00 = Body.Buffer; | |||||
break; | |||||
case EncryptOpitions.Common: | |||||
bodyBuffer00 = this.Encrypt(Body.Buffer, Body.Buffer.Length, JT809Config.JT809EncryptConfig); | |||||
break; | |||||
} | |||||
//Content:Except BeginFlag & EndFlag | |||||
var content = new byte[headerBuffer00.Length + bodyBuffer00.Length + Crc16ByteLength]; | |||||
Array.Copy(headerBuffer00, 0, content, 0, headerBuffer00.Length); | |||||
Array.Copy(bodyBuffer00, 0, content, headerBuffer00.Length, bodyBuffer00.Length); | |||||
//Dealling Code On CRC16 | |||||
CRC16 = this.CRC16_CCITT(content, 0, content.Length - Crc16ByteLength); | |||||
var crc1600 = BitConverter.GetBytes(CRC16).Reverse().ToArray(); | |||||
Array.Copy(crc1600, 0, content, headerBuffer00.Length + bodyBuffer00.Length, Crc16ByteLength); | |||||
//Last Content | |||||
var content00 = this.Escape(content); | |||||
//ToBuffer Core. | |||||
writer.WriteLittle(content00); | |||||
writer.WriteLittle(EndFlag); | |||||
} | |||||
public static Package GeneratePackage(BusinessType businessType, MessageBody messageBody, JT809Config jt809Config) | |||||
{ | |||||
var header = new Header((uint)messageBody.Buffer.Length, businessType, jt809Config); | |||||
return new Package(header, messageBody, jt809Config); | |||||
} | |||||
public static MessageBody GenerateBody(BusinessType businessID, byte[] bodyBuffer) | |||||
{ | |||||
return Activator.CreateInstance(typeof(object), bodyBuffer) as MessageBody; | |||||
} | |||||
} | |||||
} |