@@ -1,2 +1,13 @@ | |||
# JT809 | |||
JT809 | |||
# JT809协议开发 | |||
### 瞎逼逼: | |||
  现在有了[JT808](https://github.com/SmallChi/GPSPlatform/blob/master/JT808.md)的基础,对JT809就只剩搬砖了。 | |||
> 该JT809协议是参考[MessagePack-CSharp](https://github.com/neuecc/MessagePack-CSharp)一款二进制序列化器,站在巨人的肩膀上搬砖就是爽歪歪。 | |||
## JT809终端通讯协议消息对照表 | |||
|序号|消息体名称|消息ID|完成情况| | |||
|:------:|:------:|:------:|:------:| | |||
| 1 | 主链路登录请求消息 | 0x1001 | √ | |
@@ -1,15 +1,22 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<TargetFramework>netcoreapp2.0</TargetFramework> | |||
<TargetFramework>netcoreapp2.1</TargetFramework> | |||
<IsPackable>false</IsPackable> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" /> | |||
<PackageReference Include="xunit" Version="2.3.1" /> | |||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" /> | |||
<Compile Remove="PackageTest.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" /> | |||
<PackageReference Include="xunit" Version="2.4.0" /> | |||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0"> | |||
<PrivateAssets>all</PrivateAssets> | |||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> | |||
</PackageReference> | |||
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" /> | |||
</ItemGroup> | |||
@@ -0,0 +1,41 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using Xunit; | |||
using JT809.Protocol; | |||
using JT809.Protocol.JT809Extensions; | |||
namespace JT809.Protocol.Test | |||
{ | |||
public class JT809HeaderTest | |||
{ | |||
[Fact] | |||
public void Test1() | |||
{ | |||
JT809Header jT809Header = new JT809Header(); | |||
jT809Header.MsgLength = 24; | |||
jT809Header.MsgSN = 1024; | |||
jT809Header.MsgID = JT809Enums.JT809BusinessType.DOWN_BASE_MSG; | |||
jT809Header.MsgGNSSCENTERID = 1200; | |||
jT809Header.Version = new JT809Header_Version(); | |||
jT809Header.EncryptFlag = JT809Header_Encrypt.None; | |||
jT809Header.EncryptKey = 0; | |||
var hex = JT809Serializer.Serialize(jT809Header).ToHexString(); | |||
//"00 00 00 18 00 00 04 00 96 00 00 00 04 B0 01 00 00 00 00 00 00 00" | |||
} | |||
[Fact] | |||
public void Test2() | |||
{ | |||
var bytes = "00 00 00 18 00 00 04 00 96 00 00 00 04 B0 01 00 00 00 00 00 00 00".ToHexBytes(); | |||
JT809Header jT809Header= JT809Serializer.Deserialize<JT809Header>(bytes); | |||
Assert.Equal((uint)24, jT809Header.MsgLength); | |||
Assert.Equal((uint)1024, jT809Header.MsgSN); | |||
Assert.Equal(JT809Enums.JT809BusinessType.DOWN_BASE_MSG, jT809Header.MsgID); | |||
Assert.Equal((uint)1200, jT809Header.MsgGNSSCENTERID); | |||
Assert.Equal(new JT809Header_Version().ToString(), jT809Header.Version.ToString()); | |||
Assert.Equal(JT809Header_Encrypt.None, jT809Header.EncryptFlag); | |||
Assert.Equal((uint)0, jT809Header.EncryptKey); | |||
} | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
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; | |||
namespace JT809.Protocol.Test.JT809MessageBody | |||
{ | |||
public class JT809_0x1001Test | |||
{ | |||
[Fact] | |||
public void Test1() | |||
{ | |||
JT809_0x1001 jT809_0X1001 = new JT809_0x1001(); | |||
jT809_0X1001.UserId = 20180920; | |||
jT809_0X1001.Password = "20180920"; | |||
jT809_0X1001.DownLinkIP = "127.0.0.1"; | |||
jT809_0X1001.DownLinkPort = 809; | |||
var hex = JT809Serializer.Serialize(jT809_0X1001).ToHexString(); | |||
//"01 33 EF B8 32 30 31 38 30 39 32 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 31 32 37 2E 30 2E 30 2E 31 03 29" | |||
} | |||
[Fact] | |||
public void Test2() | |||
{ | |||
var bytes = "01 33 EF B8 32 30 31 38 30 39 32 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 31 32 37 2E 30 2E 30 2E 31 03 29".ToHexBytes(); | |||
JT809_0x1001 jT809_0X1001 = JT809Serializer.Deserialize<JT809_0x1001>(bytes); | |||
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,81 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using Xunit; | |||
using JT809.Protocol; | |||
using JT809.Protocol.JT809Extensions; | |||
using JT809.Protocol.JT809MessageBody; | |||
namespace JT809.Protocol.Test.JT809Packages | |||
{ | |||
public class JT809_0x1001PackageTest | |||
{ | |||
[Fact] | |||
public void Test1() | |||
{ | |||
JT809Package jT809Package = new JT809Package(); | |||
jT809Package.Header = new JT809Header | |||
{ | |||
MsgSN= 133, | |||
EncryptKey=9999, | |||
MsgID= JT809Enums.JT809BusinessType.UP_CONNECT_REQ, | |||
MsgGNSSCENTERID= 20180920, | |||
}; | |||
JT809_0x1001 jT809_0X1001 = new JT809_0x1001(); | |||
jT809_0X1001.UserId = 20180920; | |||
jT809_0X1001.Password = "20180920"; | |||
jT809_0X1001.DownLinkIP = "127.0.0.1"; | |||
jT809_0X1001.DownLinkPort = 809; | |||
jT809Package.Bodies = jT809_0X1001; | |||
var hex = JT809Serializer.Serialize(jT809Package).ToHexString(); | |||
//"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" | |||
} | |||
[Fact] | |||
public void Test2() | |||
{ | |||
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(); | |||
JT809Package jT809Package = JT809Serializer.Deserialize(bytes); | |||
Assert.Equal((uint)72, jT809Package.Header.MsgLength); | |||
Assert.Equal((uint)133, jT809Package.Header.MsgSN); | |||
Assert.Equal((uint)9999, jT809Package.Header.EncryptKey); | |||
Assert.Equal((uint)20180920, jT809Package.Header.MsgGNSSCENTERID); | |||
Assert.Equal(JT809Enums.JT809BusinessType.UP_CONNECT_REQ, jT809Package.Header.MsgID); | |||
Assert.Equal(new JT809Header_Version().ToString(), jT809Package.Header.Version.ToString()); | |||
JT809_0x1001 jT809_0X1001 = (JT809_0x1001)jT809Package.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); | |||
} | |||
[Fact] | |||
public void Test3() | |||
{ | |||
//字符'0':char c = '0'; 它的ASCII码实际上是48。 | |||
//字符'\0' : ASCII码为0,表示一个字符串结束的标志。 | |||
//"5B 00 00 00 48 00 00 00 85 10 01 01 33 EF B8 01 00 00 00 00 00 27 0F 30 01 33 EF B8 32 30 31 38 30 39 32 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 31 32 37 2E 30 2E 30 2E 31 03 29 7D 38 5D" | |||
var data="32 30 31 38 30 39 32 30".ToHexBytes(); | |||
var str = Encoding.GetEncoding("GBK").GetString(data); | |||
var data1 = "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".ToHexBytes(); | |||
var str1 = Encoding.GetEncoding("GBK").GetString(data1); | |||
var test1 = Encoding.GetEncoding("GBK").GetBytes("\0\0\0\0\0\0127.0.0.1"); | |||
var test2 = Encoding.GetEncoding("GBK").GetBytes("127.0.0.1\0\0\0\0\0\0"); | |||
var test3 = Encoding.GetEncoding("GBK").GetBytes("000000127.0.0.1"); | |||
var test4 = Encoding.GetEncoding("GBK").GetBytes("127.0.0.1000000"); | |||
} | |||
} | |||
} |
@@ -1,117 +0,0 @@ | |||
using System; | |||
using System.Text; | |||
using JT809.Protocol.Configs; | |||
using JT809.Protocol.ProtocolPacket; | |||
using JT809.Protocol.ProtocolPacket.Bodies.Master; | |||
using Xunit; | |||
namespace JT809.Protocol.Test.ProtocolPacket | |||
{ | |||
public class PackageTest | |||
{ | |||
readonly static JT809Config JT809Config = new JT809Config | |||
{ | |||
}; | |||
[Fact] | |||
public void CreatePackageTest() | |||
{ | |||
var body_UP_CONNECT_REQ= new Body_UP_CONNECT_REQ(20140813, | |||
Encoding.UTF8.GetBytes("20140813"), | |||
Encoding.UTF8.GetBytes("20140813"), | |||
809); | |||
Package package = Package.GeneratePackage(Enums.BusinessType.UP_CONNECT_REQ, | |||
body_UP_CONNECT_REQ, | |||
JT809Config); | |||
var data = package.Buffer.ToHexString(); | |||
Package package1 = new Package(package.Buffer); | |||
package1.Buffer.ToHexString(); | |||
} | |||
[Fact] | |||
public void TestMethod1() | |||
{ | |||
//5B | |||
//00 00 00 48 | |||
//00 00 00 85 | |||
//10 01 | |||
//01 33 53 D5 | |||
//01 00 00 | |||
//00 | |||
//00 00 27 0F | |||
//01 33 53 0D 32 30 31 34 30 38 31 33 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 51 83 | |||
//5D | |||
//UP-CONNECT-REQ | |||
byte[] login = new byte[]{ | |||
91, | |||
00,00,00,48, | |||
00,00,00,85, | |||
10,01, | |||
01,33,53,213, | |||
01,00,00, | |||
00,00,00, | |||
27,15,01,33, | |||
53,13,32,30, | |||
31,34,30,38, | |||
31,33,31,32, | |||
37,46,30,46, | |||
30,46,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, | |||
51,83, | |||
93 }; | |||
Package package = new Package(login); | |||
} | |||
[Fact] | |||
public void CrcTest() | |||
{ | |||
byte[] login = new byte[] {51,83}; | |||
var CRC16 = BitConverter.ToUInt16(login, 0); | |||
byte[] login1 = new byte[] { 83, 51 }; | |||
var CRC161 = BitConverter.ToUInt16(login1, 0); | |||
} | |||
[Fact] | |||
public void MSG_GNSSCENTERIDTest() | |||
{//8449 | |||
UInt64 result = 0; | |||
//00, 01, D7, 28 | |||
//01 ,33 ,53 ,0xD5 | |||
//00 01 D7 27 | |||
//00 00 27 0F | |||
//5B | |||
byte[] data = new byte[] { 00 ,0x8A ,0xDA ,0xEB }; | |||
int length = 4; | |||
//00 << 8*(4-0-1)=>00 << 24 | |||
//01 << 8*(4-1-1)=>01 << 16 | |||
//0xD7 << 8*(4-2-1)=>0xD7 << 8 | |||
//27 << 8*(4-3-1)=>27 << 0 | |||
//var a1 = 27 << 0; | |||
//for (int i = 0; i < length; i++) | |||
//{ | |||
// UInt64 currentData = (UInt64)data[i] << (8 * (length - i - 1)); | |||
// result += currentData; | |||
//} | |||
uint value = 20141013; | |||
var a1 = (byte)(value >> 24); | |||
var a2 = (byte)(value >> 16); | |||
var a3 = (byte)(value >> 8); | |||
var a4 = (byte)(value); | |||
//01 51 83 213 | |||
var b = (uint)(data[3] | data[2] << 8 | data[1] << 16 | data[0] << 24); | |||
var a=result.ToString(); | |||
} | |||
} | |||
} |
@@ -1,12 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace JT809.Protocol.Enums | |||
{ | |||
public enum ErrorCode | |||
{ | |||
CRC16CheckInvalid = 1001, | |||
HeaderLengthNotEqualBodyLength=1002 | |||
} | |||
} |
@@ -1,25 +0,0 @@ | |||
using JT809.Protocol.Enums; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace JT809.Protocol.Exceptions | |||
{ | |||
public class JT809Exception:Exception | |||
{ | |||
private readonly ErrorCode errorCode; | |||
public JT809Exception(ErrorCode errorCode) : base(errorCode.ToString()) | |||
{ | |||
this.errorCode = errorCode; | |||
} | |||
public JT809Exception(ErrorCode errorCode, string message) : base(message) | |||
{ | |||
this.errorCode = errorCode; | |||
} | |||
public ErrorCode ErrorCode => errorCode; | |||
} | |||
} |
@@ -1,7 +1,8 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
<LangVersion>latest</LangVersion> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> | |||
@@ -9,20 +10,44 @@ | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Compile Remove="Configs\**" /> | |||
<Compile Remove="Encrypt\**" /> | |||
<Compile Remove="JT809Enums\**" /> | |||
<Compile Remove="Escape\**" /> | |||
<Compile Remove="ProtocolPacket\Message\**" /> | |||
<Compile Remove="ProtocolPacket\**" /> | |||
<EmbeddedResource Remove="Configs\**" /> | |||
<EmbeddedResource Remove="Encrypt\**" /> | |||
<EmbeddedResource Remove="JT809Enums\**" /> | |||
<EmbeddedResource Remove="Escape\**" /> | |||
<EmbeddedResource Remove="ProtocolPacket\Message\**" /> | |||
<EmbeddedResource Remove="ProtocolPacket\**" /> | |||
<None Remove="Configs\**" /> | |||
<None Remove="Encrypt\**" /> | |||
<None Remove="JT809Enums\**" /> | |||
<None Remove="Escape\**" /> | |||
<None Remove="ProtocolPacket\Message\**" /> | |||
<None Remove="ProtocolPacket\**" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Remove="Constants.cs" /> | |||
<Compile Remove="IEncrypt.cs" /> | |||
<Compile Remove="IEscape.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Include="JT809Enums\JT809ErrorCode.cs" /> | |||
<Compile Include="JT809Enums\JT809BusinessType.cs" /> | |||
<Compile Include="JT809Enums\JT809SubBusinessType.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" /> | |||
<PackageReference Include="System.Dynamic.Runtime" Version="4.3.0" /> | |||
<PackageReference Include="System.Memory" Version="4.5.1" /> | |||
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Folder Include="JT809Properties\" /> | |||
</ItemGroup> | |||
</Project> |
@@ -0,0 +1,14 @@ | |||
using System; | |||
namespace JT809.Protocol.JT809Attributes | |||
{ | |||
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] | |||
public sealed class JT809BodiesTypeAttribute : Attribute | |||
{ | |||
public JT809BodiesTypeAttribute(Type jT809BodiesType) | |||
{ | |||
JT809BodiesType = jT809BodiesType; | |||
} | |||
public Type JT809BodiesType { get;} | |||
} | |||
} |
@@ -0,0 +1,25 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace JT809.Protocol.JT809Attributes | |||
{ | |||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = false, Inherited = true)] | |||
public sealed class JT809FormatterAttribute:Attribute | |||
{ | |||
public Type FormatterType { get; private set; } | |||
public object[] Arguments { get; private set; } | |||
public JT809FormatterAttribute(Type formatterType) | |||
{ | |||
this.FormatterType = formatterType; | |||
} | |||
public JT809FormatterAttribute(Type formatterType, params object[] arguments) | |||
{ | |||
this.FormatterType = formatterType; | |||
this.Arguments = arguments; | |||
} | |||
} | |||
} |
@@ -0,0 +1,10 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace JT809.Protocol | |||
{ | |||
public abstract class JT809Bodies | |||
{ | |||
} | |||
} |
@@ -1,163 +1,166 @@ | |||
using System.ComponentModel; | |||
namespace JT809.Protocol.Enums | |||
{ | |||
/// <summary> | |||
/// 业务数据类型标识 | |||
/// 数据交换一协议规定的业务数据类型名称和标识常量定义见表 73。业务数据类型标识的命名规则如下: | |||
/// a) 上级平台向下级平台发送的请求消息,一般以“DOWN_”开头,以后缀_REQ 结尾;而下级平台向上级平台发送的请求消息一般以“UP_”开头,以后缀_REQ 结尾; | |||
/// b) 当上下级平台之间有应答消息情况下,应答消息可继续沿用对应的请求消息开头标识符,而通过后缀 RSP 来标识结尾。 | |||
/// </summary> | |||
public enum BusinessType : ushort | |||
{ | |||
#region 链路管理类 | |||
///<summary> | |||
///主链路登录请求消息 | |||
///</summary> | |||
[Description("主链路登录请求消息")] | |||
UP_CONNECT_REQ = 0x1001, | |||
///<summary> | |||
///主链路登录应答消息 | |||
///</summary> | |||
[Description("主链路登录应答消息")] | |||
UP_CONNECT_RSP = 0x1002, | |||
///<summary> | |||
///主链路注销请求消息 | |||
///</summary> | |||
[Description("主链路注销请求消息")] | |||
UP_DISCONNECT_REQ = 0x1003, | |||
///<summary> | |||
///主链路注销应答消息 | |||
///</summary> | |||
[Description("主链路注销应答消息")] | |||
UP_DISCONNECT_RSP = 0x1004, | |||
///<summary> | |||
///主链路连接保持请求消息 | |||
///</summary> | |||
[Description("主链路连接保持请求消息")] | |||
UP_LINKTEST_REQ = 0x1005, | |||
///<summary> | |||
///主链路连接保持应答消息 | |||
///</summary> | |||
[Description("主链路连接保持应答消息")] | |||
UP_LINKTEST_RSP = 0x1006, | |||
///<summary> | |||
///主链路断开通知消息 | |||
///</summary> | |||
[Description("主链路断开通知消息")] | |||
UP_DISCONNECT_INFORM = 0x1007, | |||
///<summary> | |||
///下级平台主动关闭链路通知消息 | |||
///</summary> | |||
[Description("下级平台主动关闭链路通知消息")] | |||
UP_CLOSELINK_INFORM = 0x1008, | |||
///<summary> | |||
///从链路连接请求消息 | |||
///</summary> | |||
[Description("从链路连接请求消息")] | |||
DOWN_CONNECT_REQ = 0x9001, | |||
///<summary> | |||
///从链路连接应答消息 | |||
///</summary> | |||
[Description("从链路连接应答消息")] | |||
DOWN_CONNECT_RSP = 0x9002, | |||
///<summary> | |||
///从链路注销请求消息 | |||
///</summary> | |||
[Description("从链路注销请求消息")] | |||
DOWN_DISCONNECT_REQ = 0x9003, | |||
///<summary> | |||
///从链路注销应答消息 | |||
///</summary> | |||
[Description("从链路注销应答消息")] | |||
DOWN_DISCONNECT_RSP = 0x9004, | |||
///<summary> | |||
///从链路连接保持请求消息 | |||
///</summary> | |||
[Description("从链路连接保持请求消息")] | |||
DOWN_LINKTEST_REQ = 0x9005, | |||
///<summary> | |||
///从链路连接保持应答消息 | |||
///</summary> | |||
[Description("从链路连接保持应答消息")] | |||
DOWN_LINKTEST_RSP = 0x9006, | |||
///<summary> | |||
///从链路断开通知消息 | |||
///</summary> | |||
[Description("从链路断开通知消息")] | |||
DOWN_DISCONNECT_INFORM = 0x9007, | |||
///<summary> | |||
///上级平台主动关闭链路通知消息 | |||
///</summary> | |||
[Description("上级平台主动关闭链路通知消息")] | |||
DOWN_CLOSELINK_INFORM = 0x9008, | |||
#endregion | |||
#region 信息统计类 | |||
///<summary> | |||
///接收定位信息数量通知消息 | |||
///</summary> | |||
[Description("接收定位信息数量通知消息")] | |||
DOWN_TOTAL_RECV_BACK_MSG = 0x9101, | |||
#endregion | |||
#region 车辆动态信息交换 | |||
///<summary> | |||
///主链路动态信息交换消息 | |||
///</summary> | |||
[Description("主链路动态信息交换消息")] | |||
UP_EXG_MSG = 0x1200, | |||
///<summary> | |||
///从链路动态信息交换消息 | |||
///</summary> | |||
[Description("从链路动态信息交换消息")] | |||
DOWN_EXG_MSG = 0x9200, | |||
#endregion | |||
#region 平台间信息交互类 | |||
///<summary> | |||
///主链路平台间信息交互消息 | |||
///</summary> | |||
[Description("主链路平台间信息交互消息")] | |||
UP_PLATFORM_MSG = 0x1300, | |||
///<summary> | |||
///从链路平台间信息交互消息 | |||
///</summary> | |||
[Description("从链路平台间信息交互消息")] | |||
DOWN_PLATFORM_MSG = 0x9300, | |||
#endregion | |||
#region 车辆报警信息交互类 | |||
///<summary> | |||
///主链路报警信息交互消息 | |||
///</summary> | |||
[Description("主链路报警信息交互消息")] | |||
UP_WARN_MSG = 0x1400, | |||
///<summary> | |||
///从链路报警信息交互消息 | |||
///</summary> | |||
[Description("从链路报警信息交互消息")] | |||
DOWN_WARN_MSG = 0x9400, | |||
#endregion | |||
#region 车辆监管类 | |||
///<summary> | |||
///主链路车辆监管消息 | |||
///</summary> | |||
[Description("主链路车辆监管消息")] | |||
UP_CTRL_MSG = 0x1500, | |||
///<summary> | |||
///从链路车辆监管消息 | |||
///</summary> | |||
[Description("从链路车辆监管消息")] | |||
DOWN_CTRL_MSG = 0x9500, | |||
#endregion | |||
#region 车辆静态信息交换类 | |||
///<summary> | |||
///主链路静态信息交换消息 | |||
///</summary> | |||
[Description("主链路静态信息交换消息")] | |||
UP_BASE_MSG = 0x1600, | |||
///<summary> | |||
///从链路静态信息交换消息 | |||
///</summary> | |||
[Description("从链路静态信息交换消息")] | |||
DOWN_BASE_MSG = 0x9600, | |||
#endregion | |||
} | |||
} | |||
using JT809.Protocol.JT809Attributes; | |||
using JT809.Protocol.JT809MessageBody; | |||
using System.ComponentModel; | |||
namespace JT809.Protocol.JT809Enums | |||
{ | |||
/// <summary> | |||
/// 业务数据类型标识 | |||
/// 数据交换一协议规定的业务数据类型名称和标识常量定义见表 73。业务数据类型标识的命名规则如下: | |||
/// a) 上级平台向下级平台发送的请求消息,一般以“DOWN_”开头,以后缀_REQ 结尾;而下级平台向上级平台发送的请求消息一般以“UP_”开头,以后缀_REQ 结尾; | |||
/// b) 当上下级平台之间有应答消息情况下,应答消息可继续沿用对应的请求消息开头标识符,而通过后缀 RSP 来标识结尾。 | |||
/// </summary> | |||
public enum JT809BusinessType : ushort | |||
{ | |||
#region 链路管理类 | |||
///<summary> | |||
///主链路登录请求消息 | |||
///</summary> | |||
[Description("主链路登录请求消息")] | |||
[JT809BodiesType(typeof(JT809_0x1001))] | |||
UP_CONNECT_REQ = 0x1001, | |||
///<summary> | |||
///主链路登录应答消息 | |||
///</summary> | |||
[Description("主链路登录应答消息")] | |||
UP_CONNECT_RSP = 0x1002, | |||
///<summary> | |||
///主链路注销请求消息 | |||
///</summary> | |||
[Description("主链路注销请求消息")] | |||
UP_DISCONNECT_REQ = 0x1003, | |||
///<summary> | |||
///主链路注销应答消息 | |||
///</summary> | |||
[Description("主链路注销应答消息")] | |||
UP_DISCONNECT_RSP = 0x1004, | |||
///<summary> | |||
///主链路连接保持请求消息 | |||
///</summary> | |||
[Description("主链路连接保持请求消息")] | |||
UP_LINKTEST_REQ = 0x1005, | |||
///<summary> | |||
///主链路连接保持应答消息 | |||
///</summary> | |||
[Description("主链路连接保持应答消息")] | |||
UP_LINKTEST_RSP = 0x1006, | |||
///<summary> | |||
///主链路断开通知消息 | |||
///</summary> | |||
[Description("主链路断开通知消息")] | |||
UP_DISCONNECT_INFORM = 0x1007, | |||
///<summary> | |||
///下级平台主动关闭链路通知消息 | |||
///</summary> | |||
[Description("下级平台主动关闭链路通知消息")] | |||
UP_CLOSELINK_INFORM = 0x1008, | |||
///<summary> | |||
///从链路连接请求消息 | |||
///</summary> | |||
[Description("从链路连接请求消息")] | |||
DOWN_CONNECT_REQ = 0x9001, | |||
///<summary> | |||
///从链路连接应答消息 | |||
///</summary> | |||
[Description("从链路连接应答消息")] | |||
DOWN_CONNECT_RSP = 0x9002, | |||
///<summary> | |||
///从链路注销请求消息 | |||
///</summary> | |||
[Description("从链路注销请求消息")] | |||
DOWN_DISCONNECT_REQ = 0x9003, | |||
///<summary> | |||
///从链路注销应答消息 | |||
///</summary> | |||
[Description("从链路注销应答消息")] | |||
DOWN_DISCONNECT_RSP = 0x9004, | |||
///<summary> | |||
///从链路连接保持请求消息 | |||
///</summary> | |||
[Description("从链路连接保持请求消息")] | |||
DOWN_LINKTEST_REQ = 0x9005, | |||
///<summary> | |||
///从链路连接保持应答消息 | |||
///</summary> | |||
[Description("从链路连接保持应答消息")] | |||
DOWN_LINKTEST_RSP = 0x9006, | |||
///<summary> | |||
///从链路断开通知消息 | |||
///</summary> | |||
[Description("从链路断开通知消息")] | |||
DOWN_DISCONNECT_INFORM = 0x9007, | |||
///<summary> | |||
///上级平台主动关闭链路通知消息 | |||
///</summary> | |||
[Description("上级平台主动关闭链路通知消息")] | |||
DOWN_CLOSELINK_INFORM = 0x9008, | |||
#endregion | |||
#region 信息统计类 | |||
///<summary> | |||
///接收定位信息数量通知消息 | |||
///</summary> | |||
[Description("接收定位信息数量通知消息")] | |||
DOWN_TOTAL_RECV_BACK_MSG = 0x9101, | |||
#endregion | |||
#region 车辆动态信息交换 | |||
///<summary> | |||
///主链路动态信息交换消息 | |||
///</summary> | |||
[Description("主链路动态信息交换消息")] | |||
UP_EXG_MSG = 0x1200, | |||
///<summary> | |||
///从链路动态信息交换消息 | |||
///</summary> | |||
[Description("从链路动态信息交换消息")] | |||
DOWN_EXG_MSG = 0x9200, | |||
#endregion | |||
#region 平台间信息交互类 | |||
///<summary> | |||
///主链路平台间信息交互消息 | |||
///</summary> | |||
[Description("主链路平台间信息交互消息")] | |||
UP_PLATFORM_MSG = 0x1300, | |||
///<summary> | |||
///从链路平台间信息交互消息 | |||
///</summary> | |||
[Description("从链路平台间信息交互消息")] | |||
DOWN_PLATFORM_MSG = 0x9300, | |||
#endregion | |||
#region 车辆报警信息交互类 | |||
///<summary> | |||
///主链路报警信息交互消息 | |||
///</summary> | |||
[Description("主链路报警信息交互消息")] | |||
UP_WARN_MSG = 0x1400, | |||
///<summary> | |||
///从链路报警信息交互消息 | |||
///</summary> | |||
[Description("从链路报警信息交互消息")] | |||
DOWN_WARN_MSG = 0x9400, | |||
#endregion | |||
#region 车辆监管类 | |||
///<summary> | |||
///主链路车辆监管消息 | |||
///</summary> | |||
[Description("主链路车辆监管消息")] | |||
UP_CTRL_MSG = 0x1500, | |||
///<summary> | |||
///从链路车辆监管消息 | |||
///</summary> | |||
[Description("从链路车辆监管消息")] | |||
DOWN_CTRL_MSG = 0x9500, | |||
#endregion | |||
#region 车辆静态信息交换类 | |||
///<summary> | |||
///主链路静态信息交换消息 | |||
///</summary> | |||
[Description("主链路静态信息交换消息")] | |||
UP_BASE_MSG = 0x1600, | |||
///<summary> | |||
///从链路静态信息交换消息 | |||
///</summary> | |||
[Description("从链路静态信息交换消息")] | |||
DOWN_BASE_MSG = 0x9600, | |||
#endregion | |||
} | |||
} |
@@ -0,0 +1,17 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace JT809.Protocol.JT809Enums | |||
{ | |||
public enum JT809ErrorCode | |||
{ | |||
CRC16CheckInvalid = 1001, | |||
HeaderLengthNotEqualBodyLength=1002, | |||
GetFormatterError=1003, | |||
SerializeError=1004, | |||
DeserializeError=1005, | |||
HeaderParseError=1006, | |||
BodiesParseError=1007 | |||
} | |||
} |
@@ -1,260 +1,260 @@ | |||
using System.ComponentModel; | |||
namespace JT809.Protocol.Enums | |||
{ | |||
///<summary> | |||
///子业务类型标识 | |||
///</summary> | |||
public enum SubBusinessType : ushort | |||
{ | |||
#region 主链路动态信息交换消息 UP_EXG_MSG | |||
/// <summary> | |||
/// 上传车辆注册信息 | |||
/// </summary> | |||
[Description("上传车辆注册信息")] | |||
UP_EXG_MSG_REGISTER = 0x1201, | |||
///<summary> | |||
///实时上传车辆定位信息 | |||
///</summary> | |||
[Description("实时上传车辆定位信息")] | |||
UP_EXG_MSG_REAL_LOCATION = 0x1202, | |||
///<summary> | |||
///车辆定位信息自动补报 | |||
///</summary> | |||
[Description("车辆定位信息自动补报")] | |||
UP_EXG_MSG_HISTORY_LOCATION = 0x1203, | |||
///<summary> | |||
///启动车辆定位信息交换应答 | |||
///</summary> | |||
[Description("启动车辆定位信息交换应答")] | |||
UP_EXG_MSG_RETURN_STARTUP_ACK = 0x1205, | |||
///<summary> | |||
///结束车辆定位信息交换应答 | |||
///</summary> | |||
[Description("结束车辆定位信息交换应答")] | |||
UP_EXG_MSG_RETURN_END_ACK = 0x1206, | |||
///<summary> | |||
///申请交换指定车辆定位信息请求 | |||
///</summary> | |||
[Description("申请交换指定车辆定位信息请求")] | |||
UP_EXG_MSG_APPLY_FOR_MONITOR_STARTUP = 0x1207, | |||
///<summary> | |||
///取消交换指定车辆定位信息请求 | |||
///</summary> | |||
[Description("取消交换指定车辆定位信息请求")] | |||
UP_EXG_MSG_APPLY_FOR_MONITOR_END = 0x1208, | |||
///<summary> | |||
///补发车辆定位信息请求 | |||
///</summary> | |||
[Description("补发车辆定位信息请求")] | |||
UP_EXG_MSG_APPLY_HISGNSSDATA_REQ = 0x1209, | |||
///<summary> | |||
///上报车辆驾驶员身份识别信息应答 | |||
///</summary> | |||
[Description("上报车辆驾驶员身份识别信息应答")] | |||
UP_EXG_MSG_REPORT_DRIVER_INFO_ACK = 0x120A, | |||
///<summary> | |||
///上报车辆电子运单应答 | |||
///</summary> | |||
[Description("上报车辆电子运单应答")] | |||
UP_EXG_MSG_TAKE_EWAYBILL_ACK = 0x120B, | |||
///<summary> | |||
///主动上报驾驶员身份信息 | |||
///</summary> | |||
[Description("主动上报驾驶员身份信息")] | |||
UP_EXG_MSG_REPORT_DRIVER_INFO = 0x120C, | |||
///<summary> | |||
///主动上报车辆电子运单信息 | |||
///</summary> | |||
[Description("主动上报车辆电子运单信息")] | |||
UP_EXG_MSG_REPORT_EWAYBILL_INFO = 0x120D, | |||
#endregion | |||
#region 从链路动态信息交换消息 DOWN_EXG_MSG | |||
///<summary> | |||
///交换车辆定位信息 | |||
///</summary> | |||
[Description("交换车辆定位信息")] | |||
DOWN_EXG_MSG_CAR_LOCATION = 0x9202, | |||
///<summary> | |||
///车辆定位信息交换补发 | |||
///</summary> | |||
[Description("车辆定位信息交换补发")] | |||
DOWN_EXG_MSG_HISTORY_ARCOSSAREA = 0x9203, | |||
///<summary> | |||
///交换车辆静态信息 | |||
///</summary> | |||
[Description("交换车辆静态信息")] | |||
DOWN_EXG_MSG_CAR_INFO = 0x9204, | |||
///<summary> | |||
///启动车辆定位信息交换请求 | |||
///</summary> | |||
[Description("启动车辆定位信息交换请求")] | |||
DOWN_EXG_MSG_RETURN_STARTUP = 0x9205, | |||
///<summary> | |||
///结束车辆定位信息交换请求 | |||
///</summary> | |||
[Description("结束车辆定位信息交换请求")] | |||
DOWN_EXG_MSG_RETURN_END = 0x9206, | |||
///<summary> | |||
///申请交换指定车辆定位信息应答 | |||
///</summary> | |||
[Description("申请交换指定车辆定位信息应答")] | |||
DOWN_EXG_MSG_APPLY_FOR_MONITOR_STARTUP_ACK = 0x9207, | |||
///<summary> | |||
///取消交换指定车辆定位信息应答 | |||
///</summary> | |||
[Description("取消交换指定车辆定位信息应答")] | |||
DOWN_EXG_MSG_APPLY_FOR_MONITOR_END_ACK = 0x9208, | |||
///<summary> | |||
///补发车辆定位信息应答 | |||
///</summary> | |||
[Description("补发车辆定位信息应答")] | |||
DOWN_EXG_MSG_APPLY_HISGNSSDATA_ACK = 0x9209, | |||
///<summary> | |||
///上报车辆驾驶员身份识别信息请求 | |||
///</summary> | |||
[Description("上报车辆驾驶员身份识别信息请求")] | |||
DOWN_EXG_MSG_REPORT_DRIVER_INFO = 0x920A, | |||
///<summary> | |||
///上报车辆电子运单请求 | |||
///</summary> | |||
[Description("上报车辆电子运单请求")] | |||
DOWN_EXG_MSG_TAKE_EWAYBILL_REQ = 0x920B, | |||
#endregion | |||
#region 主链路平台信息交互消息 UP_PLATFORM_MSG | |||
///<summary> | |||
///平台查岗应答 | |||
///</summary> | |||
[Description("平台查岗应答")] | |||
UP_PLATFORM_MSG_POST_QUERY_ACK = 0x1301, | |||
///<summary> | |||
///下发平台间报文应答 | |||
///</summary> | |||
[Description("下发平台间报文应答")] | |||
UP_PLATFORM_MSG_INFO_ACK = 0x1302, | |||
#endregion | |||
#region 从链路平台信息交互消息 DOWN_PLATFORM_MSG | |||
///<summary> | |||
///平台查岗请求 | |||
///</summary> | |||
[Description("平台查岗请求")] | |||
DOWN_PLATFORM_MSG_POST_QUERY_REQ = 0x9301, | |||
///<summary> | |||
///下发平台间报文请求 | |||
///</summary> | |||
[Description("下发平台间报文请求")] | |||
DOWN_PLATFORM_MSG_INFO_REQ = 0x9302, | |||
#endregion | |||
#region 主链路报警信息交互消息 UP_WARN_MSG | |||
///<summary> | |||
///报警督办应答 | |||
///</summary> | |||
[Description("报警督办应答")] | |||
UP_WARN_MSG_URGE_TODO_ACK = 0x1401, | |||
///<summary> | |||
///上报报警信息 | |||
///</summary> | |||
[Description("上报报警信息")] | |||
UP_WARN_MSG_ADPT_INFO = 0x1402, | |||
///<summary> | |||
///主动上报报警处理结果信息 | |||
///</summary> | |||
[Description("主动上报报警处理结果信息")] | |||
UP_WARN_MSG_ADPT_TODO_INFO = 0x1403, | |||
#endregion | |||
#region 从链路报警信息交互消息 DOWN_WARN_MSG | |||
///<summary> | |||
///报警督办请求 | |||
///</summary> | |||
[Description("报警督办请求")] | |||
DOWN_WARN_MSG_URGE_TODO_REQ = 0x9401, | |||
///<summary> | |||
///报警预警 | |||
///</summary> | |||
[Description("报警预警")] | |||
DOWN_WARN_MSG_INFORM_TIPS = 0x9402, | |||
///<summary> | |||
///实时交换报警信息 | |||
///</summary> | |||
[Description("实时交换报警信息")] | |||
DOWN_WARN_MSG_EXG_INFORM = 0x9403, | |||
#endregion | |||
#region 主链路车辆监管消息 UP_CTRL_MSG | |||
///<summary> | |||
///车辆单向监听应答 | |||
///</summary> | |||
[Description("车辆单向监听应答")] | |||
UP_CTRL_MSG_MONITOR_VEHICLE_ACK = 0x1501, | |||
///<summary> | |||
///车辆拍照应答 | |||
///</summary> | |||
[Description("车辆拍照应答")] | |||
UP_CTRL_MSG_TAKE_PHOTO_ACK = 0x1502, | |||
///<summary> | |||
///下发车辆报文应答 | |||
///</summary> | |||
[Description("下发车辆报文应答")] | |||
UP_CTRL_MSG_TEXT_INFO_ACK = 0x1503, | |||
///<summary> | |||
///上报车辆行驶记录应答 | |||
///</summary> | |||
[Description("上报车辆行驶记录应答")] | |||
UP_CTRL_MSG_TAKE_TRAVEL_ACK = 0x1504, | |||
///<summary> | |||
///车辆应急接入监管平台应答消息 | |||
///</summary> | |||
[Description("车辆应急接入监管平台应答消息")] | |||
UP_CTRL_MSG_EMERGENCY_MONITORING_ACK = 0x1505, | |||
#endregion | |||
#region 从链路车辆监管消息 DOWN_CTRL_MSG | |||
///<summary> | |||
///车辆单向监听请求 | |||
///</summary> | |||
[Description("车辆单向监听请求")] | |||
DOWN_CTRL_MSG_MONITOR_VEHICLE_REQ = 0x9501, | |||
///<summary> | |||
///车辆拍照请求 | |||
///</summary> | |||
[Description("车辆拍照请求")] | |||
DOWN_CTRL_MSG_TAKE_PHOTO_REQ = 0x9502, | |||
///<summary> | |||
///下发车辆报文请求 | |||
///</summary> | |||
[Description("下发车辆报文请求")] | |||
DOWN_CTRL_MSG_TEXT_INFO = 0x9503, | |||
///<summary> | |||
///上报车辆行驶记录请求 | |||
///</summary> | |||
[Description("上报车辆行驶记录请求")] | |||
DOWN_CTRL_MSG_TAKE_TRAVEL_REQ = 0x9504, | |||
///<summary> | |||
///车辆应急接入监管平台请求消息 | |||
///</summary> | |||
[Description("车辆应急接入监管平台请求消息")] | |||
DOWN_CTRL_MSG_EMERGENCY_MONITORING_REQ = 0x9505, | |||
#endregion | |||
#region 主链路静态信息交换消息 UP_BASE_MSG | |||
///<summary> | |||
///补报车辆静态信息应答 | |||
///</summary> | |||
[Description("补报车辆静态信息应答")] | |||
UP_BASE_MSG_VEHICLE_ADDED_ACK = 0x1601, | |||
#endregion | |||
#region 从链路静态信息交换消息 DOWN_BASE_MSG | |||
///<summary> | |||
///补报车辆静态信息请求 | |||
///</summary> | |||
[Description("补报车辆静态信息请求")] | |||
DOWN_BASE_MSG_VEHICLE_ADDED = 0x9601, | |||
#endregion | |||
} | |||
} | |||
using System.ComponentModel; | |||
namespace JT809.Protocol.JT809Enums | |||
{ | |||
///<summary> | |||
///子业务类型标识 | |||
///</summary> | |||
public enum JT809SubBusinessType : ushort | |||
{ | |||
#region 主链路动态信息交换消息 UP_EXG_MSG | |||
/// <summary> | |||
/// 上传车辆注册信息 | |||
/// </summary> | |||
[Description("上传车辆注册信息")] | |||
UP_EXG_MSG_REGISTER = 0x1201, | |||
///<summary> | |||
///实时上传车辆定位信息 | |||
///</summary> | |||
[Description("实时上传车辆定位信息")] | |||
UP_EXG_MSG_REAL_LOCATION = 0x1202, | |||
///<summary> | |||
///车辆定位信息自动补报 | |||
///</summary> | |||
[Description("车辆定位信息自动补报")] | |||
UP_EXG_MSG_HISTORY_LOCATION = 0x1203, | |||
///<summary> | |||
///启动车辆定位信息交换应答 | |||
///</summary> | |||
[Description("启动车辆定位信息交换应答")] | |||
UP_EXG_MSG_RETURN_STARTUP_ACK = 0x1205, | |||
///<summary> | |||
///结束车辆定位信息交换应答 | |||
///</summary> | |||
[Description("结束车辆定位信息交换应答")] | |||
UP_EXG_MSG_RETURN_END_ACK = 0x1206, | |||
///<summary> | |||
///申请交换指定车辆定位信息请求 | |||
///</summary> | |||
[Description("申请交换指定车辆定位信息请求")] | |||
UP_EXG_MSG_APPLY_FOR_MONITOR_STARTUP = 0x1207, | |||
///<summary> | |||
///取消交换指定车辆定位信息请求 | |||
///</summary> | |||
[Description("取消交换指定车辆定位信息请求")] | |||
UP_EXG_MSG_APPLY_FOR_MONITOR_END = 0x1208, | |||
///<summary> | |||
///补发车辆定位信息请求 | |||
///</summary> | |||
[Description("补发车辆定位信息请求")] | |||
UP_EXG_MSG_APPLY_HISGNSSDATA_REQ = 0x1209, | |||
///<summary> | |||
///上报车辆驾驶员身份识别信息应答 | |||
///</summary> | |||
[Description("上报车辆驾驶员身份识别信息应答")] | |||
UP_EXG_MSG_REPORT_DRIVER_INFO_ACK = 0x120A, | |||
///<summary> | |||
///上报车辆电子运单应答 | |||
///</summary> | |||
[Description("上报车辆电子运单应答")] | |||
UP_EXG_MSG_TAKE_EWAYBILL_ACK = 0x120B, | |||
///<summary> | |||
///主动上报驾驶员身份信息 | |||
///</summary> | |||
[Description("主动上报驾驶员身份信息")] | |||
UP_EXG_MSG_REPORT_DRIVER_INFO = 0x120C, | |||
///<summary> | |||
///主动上报车辆电子运单信息 | |||
///</summary> | |||
[Description("主动上报车辆电子运单信息")] | |||
UP_EXG_MSG_REPORT_EWAYBILL_INFO = 0x120D, | |||
#endregion | |||
#region 从链路动态信息交换消息 DOWN_EXG_MSG | |||
///<summary> | |||
///交换车辆定位信息 | |||
///</summary> | |||
[Description("交换车辆定位信息")] | |||
DOWN_EXG_MSG_CAR_LOCATION = 0x9202, | |||
///<summary> | |||
///车辆定位信息交换补发 | |||
///</summary> | |||
[Description("车辆定位信息交换补发")] | |||
DOWN_EXG_MSG_HISTORY_ARCOSSAREA = 0x9203, | |||
///<summary> | |||
///交换车辆静态信息 | |||
///</summary> | |||
[Description("交换车辆静态信息")] | |||
DOWN_EXG_MSG_CAR_INFO = 0x9204, | |||
///<summary> | |||
///启动车辆定位信息交换请求 | |||
///</summary> | |||
[Description("启动车辆定位信息交换请求")] | |||
DOWN_EXG_MSG_RETURN_STARTUP = 0x9205, | |||
///<summary> | |||
///结束车辆定位信息交换请求 | |||
///</summary> | |||
[Description("结束车辆定位信息交换请求")] | |||
DOWN_EXG_MSG_RETURN_END = 0x9206, | |||
///<summary> | |||
///申请交换指定车辆定位信息应答 | |||
///</summary> | |||
[Description("申请交换指定车辆定位信息应答")] | |||
DOWN_EXG_MSG_APPLY_FOR_MONITOR_STARTUP_ACK = 0x9207, | |||
///<summary> | |||
///取消交换指定车辆定位信息应答 | |||
///</summary> | |||
[Description("取消交换指定车辆定位信息应答")] | |||
DOWN_EXG_MSG_APPLY_FOR_MONITOR_END_ACK = 0x9208, | |||
///<summary> | |||
///补发车辆定位信息应答 | |||
///</summary> | |||
[Description("补发车辆定位信息应答")] | |||
DOWN_EXG_MSG_APPLY_HISGNSSDATA_ACK = 0x9209, | |||
///<summary> | |||
///上报车辆驾驶员身份识别信息请求 | |||
///</summary> | |||
[Description("上报车辆驾驶员身份识别信息请求")] | |||
DOWN_EXG_MSG_REPORT_DRIVER_INFO = 0x920A, | |||
///<summary> | |||
///上报车辆电子运单请求 | |||
///</summary> | |||
[Description("上报车辆电子运单请求")] | |||
DOWN_EXG_MSG_TAKE_EWAYBILL_REQ = 0x920B, | |||
#endregion | |||
#region 主链路平台信息交互消息 UP_PLATFORM_MSG | |||
///<summary> | |||
///平台查岗应答 | |||
///</summary> | |||
[Description("平台查岗应答")] | |||
UP_PLATFORM_MSG_POST_QUERY_ACK = 0x1301, | |||
///<summary> | |||
///下发平台间报文应答 | |||
///</summary> | |||
[Description("下发平台间报文应答")] | |||
UP_PLATFORM_MSG_INFO_ACK = 0x1302, | |||
#endregion | |||
#region 从链路平台信息交互消息 DOWN_PLATFORM_MSG | |||
///<summary> | |||
///平台查岗请求 | |||
///</summary> | |||
[Description("平台查岗请求")] | |||
DOWN_PLATFORM_MSG_POST_QUERY_REQ = 0x9301, | |||
///<summary> | |||
///下发平台间报文请求 | |||
///</summary> | |||
[Description("下发平台间报文请求")] | |||
DOWN_PLATFORM_MSG_INFO_REQ = 0x9302, | |||
#endregion | |||
#region 主链路报警信息交互消息 UP_WARN_MSG | |||
///<summary> | |||
///报警督办应答 | |||
///</summary> | |||
[Description("报警督办应答")] | |||
UP_WARN_MSG_URGE_TODO_ACK = 0x1401, | |||
///<summary> | |||
///上报报警信息 | |||
///</summary> | |||
[Description("上报报警信息")] | |||
UP_WARN_MSG_ADPT_INFO = 0x1402, | |||
///<summary> | |||
///主动上报报警处理结果信息 | |||
///</summary> | |||
[Description("主动上报报警处理结果信息")] | |||
UP_WARN_MSG_ADPT_TODO_INFO = 0x1403, | |||
#endregion | |||
#region 从链路报警信息交互消息 DOWN_WARN_MSG | |||
///<summary> | |||
///报警督办请求 | |||
///</summary> | |||
[Description("报警督办请求")] | |||
DOWN_WARN_MSG_URGE_TODO_REQ = 0x9401, | |||
///<summary> | |||
///报警预警 | |||
///</summary> | |||
[Description("报警预警")] | |||
DOWN_WARN_MSG_INFORM_TIPS = 0x9402, | |||
///<summary> | |||
///实时交换报警信息 | |||
///</summary> | |||
[Description("实时交换报警信息")] | |||
DOWN_WARN_MSG_EXG_INFORM = 0x9403, | |||
#endregion | |||
#region 主链路车辆监管消息 UP_CTRL_MSG | |||
///<summary> | |||
///车辆单向监听应答 | |||
///</summary> | |||
[Description("车辆单向监听应答")] | |||
UP_CTRL_MSG_MONITOR_VEHICLE_ACK = 0x1501, | |||
///<summary> | |||
///车辆拍照应答 | |||
///</summary> | |||
[Description("车辆拍照应答")] | |||
UP_CTRL_MSG_TAKE_PHOTO_ACK = 0x1502, | |||
///<summary> | |||
///下发车辆报文应答 | |||
///</summary> | |||
[Description("下发车辆报文应答")] | |||
UP_CTRL_MSG_TEXT_INFO_ACK = 0x1503, | |||
///<summary> | |||
///上报车辆行驶记录应答 | |||
///</summary> | |||
[Description("上报车辆行驶记录应答")] | |||
UP_CTRL_MSG_TAKE_TRAVEL_ACK = 0x1504, | |||
///<summary> | |||
///车辆应急接入监管平台应答消息 | |||
///</summary> | |||
[Description("车辆应急接入监管平台应答消息")] | |||
UP_CTRL_MSG_EMERGENCY_MONITORING_ACK = 0x1505, | |||
#endregion | |||
#region 从链路车辆监管消息 DOWN_CTRL_MSG | |||
///<summary> | |||
///车辆单向监听请求 | |||
///</summary> | |||
[Description("车辆单向监听请求")] | |||
DOWN_CTRL_MSG_MONITOR_VEHICLE_REQ = 0x9501, | |||
///<summary> | |||
///车辆拍照请求 | |||
///</summary> | |||
[Description("车辆拍照请求")] | |||
DOWN_CTRL_MSG_TAKE_PHOTO_REQ = 0x9502, | |||
///<summary> | |||
///下发车辆报文请求 | |||
///</summary> | |||
[Description("下发车辆报文请求")] | |||
DOWN_CTRL_MSG_TEXT_INFO = 0x9503, | |||
///<summary> | |||
///上报车辆行驶记录请求 | |||
///</summary> | |||
[Description("上报车辆行驶记录请求")] | |||
DOWN_CTRL_MSG_TAKE_TRAVEL_REQ = 0x9504, | |||
///<summary> | |||
///车辆应急接入监管平台请求消息 | |||
///</summary> | |||
[Description("车辆应急接入监管平台请求消息")] | |||
DOWN_CTRL_MSG_EMERGENCY_MONITORING_REQ = 0x9505, | |||
#endregion | |||
#region 主链路静态信息交换消息 UP_BASE_MSG | |||
///<summary> | |||
///补报车辆静态信息应答 | |||
///</summary> | |||
[Description("补报车辆静态信息应答")] | |||
UP_BASE_MSG_VEHICLE_ADDED_ACK = 0x1601, | |||
#endregion | |||
#region 从链路静态信息交换消息 DOWN_BASE_MSG | |||
///<summary> | |||
///补报车辆静态信息请求 | |||
///</summary> | |||
[Description("补报车辆静态信息请求")] | |||
DOWN_BASE_MSG_VEHICLE_ADDED = 0x9601, | |||
#endregion | |||
} | |||
} |
@@ -0,0 +1,32 @@ | |||
using JT809.Protocol.JT809Enums; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace JT809.Protocol.JT809Exceptions | |||
{ | |||
public class JT809Exception:Exception | |||
{ | |||
public JT809Exception(JT809ErrorCode errorCode) : base(errorCode.ToString()) | |||
{ | |||
ErrorCode = errorCode; | |||
} | |||
public JT809Exception(JT809ErrorCode errorCode, string message) : base(message) | |||
{ | |||
ErrorCode = errorCode; | |||
} | |||
public JT809Exception(JT809ErrorCode errorCode, Exception ex) : base(ex.Message, ex) | |||
{ | |||
ErrorCode = errorCode; | |||
} | |||
public JT809Exception(JT809ErrorCode errorCode, string message, Exception ex) : base(ex.Message, ex) | |||
{ | |||
ErrorCode = errorCode; | |||
} | |||
public JT809ErrorCode ErrorCode { get;} | |||
} | |||
} |
@@ -0,0 +1,429 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Buffers.Binary; | |||
using System.Buffers; | |||
namespace JT809.Protocol.JT809Extensions | |||
{ | |||
public static class JT809BinaryExtensions | |||
{ | |||
/// <summary> | |||
/// 日期限制于2000年 | |||
/// </summary> | |||
private const int DateLimitYear = 2000; | |||
private const ushort cnCRC_CCITT = 0x1021; //CRC校验多项式 | |||
private static ulong[] CRC = new ulong[256]; //建立CRC16表 | |||
static JT809BinaryExtensions() | |||
{ | |||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); | |||
encoding = Encoding.GetEncoding("GBK"); | |||
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; | |||
} | |||
} | |||
private static Encoding encoding; | |||
public static int ReadBCD32(this byte data, byte dig) | |||
{ | |||
int result = Convert.ToInt32(data.ToString("X")); | |||
return result * (int)Math.Pow(100, dig - 1); | |||
} | |||
public static long ReadBCD64(this byte data, byte dig) | |||
{ | |||
long result = Convert.ToInt64(data.ToString("X")); | |||
return result * (long)Math.Pow(100, dig - 1); | |||
} | |||
public static string ReadStringLittle(ReadOnlySpan<byte> read, ref int offset, int len) | |||
{ | |||
string value = encoding.GetString(read.Slice(offset, len).ToArray()); | |||
offset += value.Length; | |||
return value.Trim('\0'); | |||
} | |||
public static string ReadStringLittle(ReadOnlySpan<byte> read, ref int offset) | |||
{ | |||
string value = encoding.GetString(read.Slice(offset).ToArray()); | |||
offset += value.Length; | |||
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) | |||
{ | |||
long result = 0; | |||
try | |||
{ | |||
for (int i = offset; i < offset + len; i++) | |||
{ | |||
result += buf[i].ReadBCD64((byte)(offset + len - i)); | |||
} | |||
} | |||
catch | |||
{ | |||
} | |||
offset = offset + len; | |||
return result; | |||
} | |||
public static DateTime ReadDateTimeLittle(ReadOnlySpan<byte> buf,ref int offset) | |||
{ | |||
DateTime dateTime= new DateTime( | |||
(buf[offset]).ReadBCD32(1) + DateLimitYear, | |||
(buf[offset + 1]).ReadBCD32(1), | |||
(buf[offset + 2]).ReadBCD32(1), | |||
(buf[offset + 3]).ReadBCD32(1), | |||
(buf[offset + 4]).ReadBCD32(1), | |||
(buf[offset + 5]).ReadBCD32(1)); | |||
offset = offset + 6; | |||
return dateTime; | |||
} | |||
public static DateTime ReadDateLittle(ReadOnlySpan<byte> buf, ref int offset) | |||
{ | |||
DateTime dateTime = new DateTime( | |||
((buf[offset] << 8) | (buf[offset + 1])), | |||
(buf[offset + 2]).ReadBCD32(1), | |||
(buf[offset + 3]).ReadBCD32(1)); | |||
offset = offset + 4; | |||
return dateTime; | |||
} | |||
public static int ReadInt32Little(ReadOnlySpan<byte> read, ref int offset) | |||
{ | |||
int value= (read[offset] << 24) | (read[offset + 1] << 16) | (read[offset + 2] << 8) | read[offset + 3]; | |||
offset = offset + 4; | |||
return value; | |||
} | |||
public static uint ReadUInt32Little(ReadOnlySpan<byte> read, ref int offset) | |||
{ | |||
uint value =(uint) ((read[offset] << 24) | (read[offset + 1] << 16) | (read[offset + 2] << 8) | read[offset + 3]); | |||
offset = offset + 4; | |||
return value; | |||
} | |||
public static ushort ReadUInt16Little(ReadOnlySpan<byte> read, ref int offset) | |||
{ | |||
ushort value = (ushort)((read[offset] << 8) | (read[offset + 1])); | |||
offset = offset + 2; | |||
return value; | |||
} | |||
public static byte ReadByteLittle(ReadOnlySpan<byte> read, ref int offset) | |||
{ | |||
byte value = read[offset]; | |||
offset = offset + 1; | |||
return value; | |||
} | |||
public static byte[] ReadBytesLittle(ReadOnlySpan<byte> read, ref int offset,int len) | |||
{ | |||
ReadOnlySpan<byte> temp = read.Slice(offset, len); | |||
offset = offset + len; | |||
return temp.ToArray(); | |||
} | |||
public static int WriteLittle(ref byte[] write, int offset, DateTime date) | |||
{ | |||
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; | |||
} | |||
public static int WriteDateTime6Little(IMemoryOwner<byte> memoryOwner, int offset, DateTime date) | |||
{ | |||
memoryOwner.Memory.Span[offset] = ((byte)(date.Year - DateLimitYear)).ToBcdByte(); | |||
memoryOwner.Memory.Span[offset + 1] = ((byte)(date.Month)).ToBcdByte(); | |||
memoryOwner.Memory.Span[offset + 2] = ((byte)(date.Day)).ToBcdByte(); | |||
memoryOwner.Memory.Span[offset + 3] = ((byte)(date.Hour)).ToBcdByte(); | |||
memoryOwner.Memory.Span[offset + 4] = ((byte)(date.Minute)).ToBcdByte(); | |||
memoryOwner.Memory.Span[offset + 5] = ((byte)(date.Second)).ToBcdByte(); | |||
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) | |||
{ | |||
memoryOwner.Memory.Span[offset] = (byte)(date.Year >> 8); | |||
memoryOwner.Memory.Span[offset + 1] = (byte)date.Year; | |||
memoryOwner.Memory.Span[offset + 2] = ((byte)(date.Month)).ToBcdByte(); | |||
memoryOwner.Memory.Span[offset + 3] = ((byte)(date.Day)).ToBcdByte(); | |||
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) | |||
{ | |||
memoryOwner.Memory.Span[offset] = (byte)(data >> 24); | |||
memoryOwner.Memory.Span[offset + 1] = (byte)(data >> 16); | |||
memoryOwner.Memory.Span[offset + 2] = (byte)(data >> 8); | |||
memoryOwner.Memory.Span[offset + 3] = (byte)data; | |||
return 4; | |||
} | |||
public static int WriteUInt32Little(IMemoryOwner<byte> memoryOwner, int offset, uint data) | |||
{ | |||
memoryOwner.Memory.Span[offset] = (byte)(data >> 24); | |||
memoryOwner.Memory.Span[offset + 1] = (byte)(data >> 16); | |||
memoryOwner.Memory.Span[offset + 2] = (byte)(data >> 8); | |||
memoryOwner.Memory.Span[offset + 3] = (byte)data; | |||
return 4; | |||
} | |||
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) | |||
{ | |||
memoryOwner.Memory.Span[offset] = (byte)(data >> 8); | |||
memoryOwner.Memory.Span[offset + 1] = (byte)data; | |||
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) | |||
{ | |||
memoryOwner.Memory.Span[offset] = data; | |||
return 1; | |||
} | |||
public static int WriteBytesLittle(IMemoryOwner<byte> memoryOwner, int offset, byte[] data) | |||
{ | |||
CopyTo(data, memoryOwner.Memory.Span, offset); | |||
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) | |||
{ | |||
byte[] codeBytes = encoding.GetBytes(data); | |||
Buffer.BlockCopy(codeBytes, 0, write, offset, codeBytes.Length); | |||
return codeBytes.Length; | |||
} | |||
public static int WriteStringLittle(IMemoryOwner<byte> memoryOwner, int offset, string data) | |||
{ | |||
byte[] codeBytes = encoding.GetBytes(data); | |||
CopyTo(codeBytes, memoryOwner.Memory.Span, offset); | |||
return codeBytes.Length; | |||
} | |||
public static int WriteBCDLittle(ref byte[] write, int offset, string data, int digit, int len) | |||
{ | |||
ReadOnlySpan<char> bcd = data.PadLeft(len, '0').AsSpan(); | |||
for (int i = 0; i < digit; i++) | |||
{ | |||
write[offset + i] = Convert.ToByte(bcd.Slice(i * 2, 2).ToString(), 16); | |||
} | |||
return digit; | |||
} | |||
public static int WriteBCDLittle(IMemoryOwner<byte> memoryOwner, int offset, string data, int digit, int len) | |||
{ | |||
ReadOnlySpan<char> bcd = data.PadLeft(len, '0').AsSpan(); | |||
for (int i = 0; i < digit; i++) | |||
{ | |||
memoryOwner.Memory.Span[offset + i] = Convert.ToByte(bcd.Slice(i * 2, 2).ToString(), 16); | |||
} | |||
return digit; | |||
} | |||
public static IEnumerable<byte> ToBytes(this string data, Encoding coding) | |||
{ | |||
return coding.GetBytes(data); | |||
} | |||
public static IEnumerable<byte> ToBytes(this string data) | |||
{ | |||
return ToBytes(data, encoding); | |||
} | |||
public static IEnumerable<byte> ToBytes(this int data, int len) | |||
{ | |||
List<byte> bytes = new List<byte>(); | |||
int n = 1; | |||
for (int i = 0; i < len; i++) | |||
{ | |||
bytes.Add((byte)(data >> 8 * (len - n))); | |||
n++; | |||
} | |||
return bytes; | |||
} | |||
/// <summary> | |||
/// 从数据头到校验码前的 CRC 1 G-CCITT 的校验值,遵循人端排序方式的规定。 | |||
/// </summary> | |||
/// <param name="packege"></param> | |||
/// <param name="ucbuf"></param> | |||
/// <param name="offset"></param> | |||
/// <param name="iLen"></param> | |||
/// <returns></returns> | |||
public static ushort ToCRC16_CCITT(this Span<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; | |||
} | |||
/// <summary> | |||
/// 从数据头到校验码前的 CRC 1 G-CCITT 的校验值,遵循人端排序方式的规定。 | |||
/// </summary> | |||
/// <param name="packege"></param> | |||
/// <param name="ucbuf"></param> | |||
/// <param name="offset"></param> | |||
/// <param name="iLen"></param> | |||
/// <returns></returns> | |||
public static ushort ToCRC16_CCITT(this ReadOnlySpan<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; | |||
} | |||
public static byte ToBcdByte(this byte buf) | |||
{ | |||
return (byte)Convert.ToInt32(buf.ToString(), 16); | |||
} | |||
/// <summary> | |||
/// 经纬度 | |||
/// </summary> | |||
/// <param name="latlng"></param> | |||
/// <returns></returns> | |||
public static double ToLatLng(this int latlng) | |||
{ | |||
return Math.Round(latlng / Math.Pow(10, 6), 6); | |||
} | |||
public static void CopyTo(Span<byte> source, Span<byte> destination, int offset) | |||
{ | |||
for (int i = 0; i < source.Length; i++) | |||
{ | |||
destination[offset + i] = source[i]; | |||
} | |||
} | |||
/// <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(); | |||
} | |||
/// <summary> | |||
/// 16进制字符串转16进制数组 | |||
/// </summary> | |||
/// <param name="hexString"></param> | |||
/// <returns></returns> | |||
public static byte[] ToStr2HexBytes(this string hexString) | |||
{ | |||
//byte[] buf = new byte[hexString.Length / 2]; | |||
//for (int i = 0; i < hexString.Length; i++) | |||
//{ | |||
// if (i % 2 == 0) | |||
// { | |||
// buf[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16) ; | |||
// } | |||
//} | |||
byte[] buf = new byte[hexString.Length / 2]; | |||
ReadOnlySpan<char> readOnlySpan = hexString.AsSpan(); | |||
for (int i = 0; i < hexString.Length; i++) | |||
{ | |||
if (i % 2 == 0) | |||
{ | |||
buf[i / 2] = Convert.ToByte(readOnlySpan.Slice(i, 2).ToString(), 16); | |||
} | |||
} | |||
return buf; | |||
//List<byte> bytes = new List<byte>(); | |||
//while (hexString.Length>0) | |||
//{ | |||
// bytes.Add(Convert.ToByte(hexString.AsSpan(0, 2).ToString(), 16)); | |||
// hexString = hexString.Remove(0,2); | |||
//} | |||
//return Regex.Replace(hexString, @"(\w{2})", "$1 ").Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries).Select(s => Convert.ToByte(s, 16)).ToArray(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,155 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.ComponentModel; | |||
using System.Linq; | |||
using System.Reflection; | |||
namespace JT809.Protocol.JT809Extensions | |||
{ | |||
/// <summary> | |||
/// 枚举扩展 | |||
/// </summary> | |||
public static class JT809EnumExtensions | |||
{ | |||
/// <summary> | |||
/// 转为整型 | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
/// <param name="t"></param> | |||
/// <returns></returns> | |||
public static int ToValue<T>(this T t) where T : struct | |||
{ | |||
return Convert.ToInt32(t); | |||
} | |||
/// <summary> | |||
/// 转为Byte | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
/// <param name="t"></param> | |||
/// <returns></returns> | |||
public static byte ToByteValue<T>(this T t) where T : struct | |||
{ | |||
return Convert.ToByte(t); | |||
} | |||
/// <summary> | |||
/// 转为整型 | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
/// <param name="t"></param> | |||
/// <returns></returns> | |||
public static string ToValueString<T>(this T t) where T : struct | |||
{ | |||
return Convert.ToInt32(t).ToString(); | |||
} | |||
/// <summary> | |||
/// 字符转枚举 | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
/// <param name="value"></param> | |||
/// <returns></returns> | |||
public static T ToEnum<T>(this string value) where T : struct | |||
{ | |||
return (T)Enum.Parse(typeof(T), value); | |||
} | |||
/// <summary> | |||
/// 获取枚举字符串 | |||
/// </summary> | |||
/// <param name="valueEnum"></param> | |||
public static string GetName(this Enum valueEnum) | |||
{ | |||
return valueEnum.ToString(); | |||
} | |||
/// <summary> | |||
/// 获取DescriptionAttribute特性枚举值的描述 | |||
/// </summary> | |||
/// <param name="value"></param> | |||
/// <returns></returns> | |||
public static string GetDescription(this Enum value) | |||
{ | |||
var attribute = value.GetAttribute<DescriptionAttribute>(); | |||
return attribute == null ? value.ToString() : attribute.Description; | |||
} | |||
/// <summary> | |||
/// 验证是否是枚举类型 | |||
/// </summary> | |||
/// <typeparam name="TEnum"></typeparam> | |||
/// <param name="enumValue"></param> | |||
/// <returns></returns> | |||
public static bool IsEnumValid<TEnum>(this int enumValue) | |||
{ | |||
return Enum.IsDefined(typeof(TEnum), enumValue); | |||
} | |||
/// <summary> | |||
/// 获取DescriptionAttribute特性枚举及描述 | |||
/// </summary> | |||
/// <param name="type"></param> | |||
/// <returns></returns> | |||
public static Dictionary<string, string> GetDescriptionAttributeDictionary(this Enum value) | |||
{ | |||
Dictionary<string, string> dictionary = new Dictionary<string, string>(); | |||
var fields = value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public); | |||
foreach (var fi in fields) | |||
{ | |||
DescriptionAttribute attr = Attribute.GetCustomAttribute(fi, typeof(DescriptionAttribute), false) as DescriptionAttribute; | |||
dictionary.Add(fi.Name, attr != null ? attr.Description : ""); | |||
} | |||
return dictionary; | |||
} | |||
/// <summary> | |||
/// 获取DisplayNameAttribute特性枚举值的描述 | |||
/// </summary> | |||
/// <param name="obj">枚举值</param> | |||
/// <returns></returns> | |||
public static string GetDisplayName(this Enum value) | |||
{ | |||
var attribute = value.GetAttribute<DisplayNameAttribute>(); | |||
return attribute == null ? value.ToString() : attribute.DisplayName; | |||
} | |||
/// <summary> | |||
/// 获取DisplayNameAttribute特性枚举及描述 | |||
/// </summary> | |||
/// <param name="type"></param> | |||
/// <returns></returns> | |||
public static Dictionary<string, string> GetDisplayNameAttributeDictionary(this Enum value) | |||
{ | |||
Dictionary<string, string> dictionary = new Dictionary<string, string>(); | |||
var fields = value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public); | |||
foreach (var fi in fields) | |||
{ | |||
DisplayNameAttribute attr = Attribute.GetCustomAttribute(fi, typeof(DisplayNameAttribute), false) as DisplayNameAttribute; | |||
dictionary.Add(fi.Name, attr != null ? attr.DisplayName : ""); | |||
} | |||
return dictionary; | |||
} | |||
/// <summary> | |||
/// 获取枚举对应特性 | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
/// <param name="value"></param> | |||
/// <returns></returns> | |||
public static T GetAttribute<T>(this Enum value) where T : Attribute | |||
{ | |||
try | |||
{ | |||
var type = value.GetType(); | |||
var memberInfo = type.GetMember(value.ToString()); | |||
var attributes = memberInfo[0].GetCustomAttributes(typeof(T), false); | |||
return (T)attributes[0]; | |||
} | |||
catch | |||
{ | |||
return default; | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,50 @@ | |||
using JT809.Protocol.JT809Attributes; | |||
using JT809.Protocol.JT809Enums; | |||
using JT809.Protocol.JT809Exceptions; | |||
using JT809.Protocol.JT809Formatters; | |||
using System; | |||
using System.Reflection; | |||
namespace JT809.Protocol.JT809Extensions | |||
{ | |||
public static class JT809FormatterExtensions | |||
{ | |||
public static IJT809Formatter<T> GetFormatter<T>() | |||
{ | |||
IJT809Formatter<T> formatter; | |||
var attr = typeof(T).GetTypeInfo().GetCustomAttribute<JT809FormatterAttribute>(); | |||
if (attr == null) | |||
{ | |||
throw new JT809Exception(JT809ErrorCode.GetFormatterError, $"该类{typeof(T).FullName}没有标记JT809FormatterAttribute"); | |||
} | |||
if (attr.Arguments == null) | |||
{ | |||
formatter = (IJT809Formatter<T>)Activator.CreateInstance(attr.FormatterType); | |||
} | |||
else | |||
{ | |||
formatter = (IJT809Formatter<T>)Activator.CreateInstance(attr.FormatterType, attr.Arguments); | |||
} | |||
return formatter; | |||
} | |||
public static object GetFormatter(Type formatterType) | |||
{ | |||
object formatter; | |||
var attr = formatterType.GetTypeInfo().GetCustomAttribute<JT809FormatterAttribute>(); | |||
if (attr == null) | |||
{ | |||
throw new JT809Exception(JT809ErrorCode.GetFormatterError,$"该类{formatterType.FullName}没有标记JT809FormatterAttribute"); | |||
} | |||
if (attr.Arguments == null) | |||
{ | |||
formatter = Activator.CreateInstance(attr.FormatterType); | |||
} | |||
else | |||
{ | |||
formatter = Activator.CreateInstance(attr.FormatterType, attr.Arguments); | |||
} | |||
return formatter; | |||
} | |||
} | |||
} |
@@ -0,0 +1,87 @@ | |||
using JT809.Protocol.JT809Formatters; | |||
using System; | |||
using System.Buffers; | |||
using System.Collections.Concurrent; | |||
using System.Collections.Generic; | |||
using System.Linq.Expressions; | |||
using System.Reflection; | |||
using System.Text; | |||
namespace JT809.Protocol.JT809Extensions | |||
{ | |||
/// <summary> | |||
/// | |||
/// <para>ref http://adamsitnik.com/Span/#span-must-not-be-a-generic-type-argument </para> | |||
/// <para>ref http://adamsitnik.com/Span/ </para> | |||
/// <para>ref MessagePack.Formatters.DynamicObjectTypeFallbackFormatter </para> | |||
/// </summary> | |||
public static class JT809FormatterResolverExtensions | |||
{ | |||
delegate int JT809SerializeMethod(object dynamicFormatter, IMemoryOwner<byte> memoryOwner, int offset, object value); | |||
delegate dynamic JT809DeserializeMethod(object dynamicFormatter, ReadOnlySpan<byte> bytes, out int readSize); | |||
static readonly ConcurrentDictionary<Type, (object Value, JT809SerializeMethod SerializeMethod)> jT809Serializers = new ConcurrentDictionary<Type, (object Value, JT809SerializeMethod SerializeMethod)>(); | |||
static readonly ConcurrentDictionary<Type, (object Value, JT809DeserializeMethod DeserializeMethod)> jT809Deserializes = new ConcurrentDictionary<Type, (object Value, JT809DeserializeMethod DeserializeMethod)>(); | |||
//T Deserialize(ReadOnlySpan<byte> bytes, out int readSize); | |||
//int Serialize(IMemoryOwner<byte> memoryOwner, int offset, T value); | |||
public static int JT809DynamicSerialize(object objFormatter, IMemoryOwner<byte> memoryOwner, int offset, dynamic value) | |||
{ | |||
Type type = value.GetType(); | |||
var ti = type.GetTypeInfo(); | |||
(object Value, JT809SerializeMethod SerializeMethod) formatterAndDelegate; | |||
if (!jT809Serializers.TryGetValue(type, out formatterAndDelegate)) | |||
{ | |||
var t = type; | |||
{ | |||
var formatterType = typeof(IJT809Formatter<>).MakeGenericType(t); | |||
var param0 = Expression.Parameter(typeof(object), "formatter"); | |||
var param1 = Expression.Parameter(typeof(IMemoryOwner<byte>), "memoryOwner"); | |||
var param2 = Expression.Parameter(typeof(int), "offset"); | |||
var param3 = Expression.Parameter(typeof(object), "value"); | |||
var serializeMethodInfo = formatterType.GetRuntimeMethod("Serialize", new[] { typeof(IMemoryOwner<byte>), typeof(int), t}); | |||
var body = Expression.Call( | |||
Expression.Convert(param0, formatterType), | |||
serializeMethodInfo, | |||
param1, | |||
param2, | |||
ti.IsValueType ? Expression.Unbox(param3, t) : Expression.Convert(param3, t)); | |||
var lambda = Expression.Lambda<JT809SerializeMethod>(body, param0, param1, param2, param3).Compile(); | |||
formatterAndDelegate = (objFormatter, lambda); | |||
} | |||
jT809Serializers.TryAdd(t, formatterAndDelegate); | |||
} | |||
return formatterAndDelegate.SerializeMethod(formatterAndDelegate.Value, memoryOwner, offset, value); | |||
} | |||
public static dynamic JT809DynamicDeserialize(object objFormatter,ReadOnlySpan<byte> bytes, out int readSize) | |||
{ | |||
var type = objFormatter.GetType(); | |||
(object Value, JT809DeserializeMethod DeserializeMethod) formatterAndDelegate; | |||
if (!jT809Deserializes.TryGetValue(type, out formatterAndDelegate)) | |||
{ | |||
var t = type; | |||
{ | |||
var formatterType = typeof(IJT809Formatter<>).MakeGenericType(t); | |||
ParameterExpression param0 = Expression.Parameter(typeof(object), "formatter"); | |||
ParameterExpression param1 = Expression.Parameter(typeof(ReadOnlySpan<byte>), "bytes"); | |||
ParameterExpression param2 = Expression.Parameter(typeof(int).MakeByRefType(), "readSize"); | |||
var deserializeMethodInfo = type.GetRuntimeMethod("Deserialize", new[] { typeof(ReadOnlySpan<byte>), typeof(int).MakeByRefType() }); | |||
var body = Expression.Call( | |||
Expression.Convert(param0, type), | |||
deserializeMethodInfo, | |||
param1, | |||
param2 | |||
); | |||
var lambda = Expression.Lambda<JT809DeserializeMethod>(body, param0, param1, param2).Compile(); | |||
formatterAndDelegate = (objFormatter, lambda); | |||
} | |||
jT809Deserializes.TryAdd(t, formatterAndDelegate); | |||
} | |||
return formatterAndDelegate.DeserializeMethod(formatterAndDelegate.Value, bytes, out readSize); | |||
} | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
using System; | |||
using System.Buffers; | |||
namespace JT809.Protocol.JT809Formatters | |||
{ | |||
public interface IJT809Formatter<T> | |||
{ | |||
T Deserialize(ReadOnlySpan<byte> bytes, out int readSize); | |||
int Serialize(IMemoryOwner<byte> memoryOwner, int offset, T value); | |||
} | |||
} |
@@ -0,0 +1,39 @@ | |||
using JT809.Protocol.JT809Enums; | |||
using JT809.Protocol.JT809Extensions; | |||
using System; | |||
using System.Buffers; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace JT809.Protocol.JT809Formatters | |||
{ | |||
public class JT809HeaderFormatter : IJT809Formatter<JT809Header> | |||
{ | |||
public JT809Header Deserialize(ReadOnlySpan<byte> bytes, out int readSize) | |||
{ | |||
int offset = 0; | |||
JT809Header jT809Header = new JT809Header(); | |||
jT809Header.MsgLength = JT809BinaryExtensions.ReadUInt32Little(bytes, ref offset); | |||
jT809Header.MsgSN= JT809BinaryExtensions.ReadUInt32Little(bytes, ref offset); | |||
jT809Header.MsgID = (JT809BusinessType)JT809BinaryExtensions.ReadUInt16Little(bytes, ref offset); | |||
jT809Header.MsgGNSSCENTERID = JT809BinaryExtensions.ReadUInt32Little(bytes, ref offset); | |||
jT809Header.Version = new JT809Header_Version(JT809BinaryExtensions.ReadBytesLittle(bytes, ref offset, JT809Header_Version.FixedByteLength)); | |||
jT809Header.EncryptFlag= (JT809Header_Encrypt)JT809BinaryExtensions.ReadByteLittle(bytes, ref offset); | |||
jT809Header.EncryptKey= JT809BinaryExtensions.ReadUInt32Little(bytes, ref offset); | |||
readSize = offset; | |||
return jT809Header; | |||
} | |||
public int Serialize(IMemoryOwner<byte> memoryOwner, int offset, JT809Header value) | |||
{ | |||
offset += JT809BinaryExtensions.WriteUInt32Little(memoryOwner, offset, value.MsgLength); | |||
offset += JT809BinaryExtensions.WriteUInt32Little(memoryOwner, offset, value.MsgSN); | |||
offset += JT809BinaryExtensions.WriteUInt16Little(memoryOwner, offset, (ushort)value.MsgID); | |||
offset += JT809BinaryExtensions.WriteUInt32Little(memoryOwner, offset, value.MsgGNSSCENTERID); | |||
offset += JT809BinaryExtensions.WriteBytesLittle(memoryOwner, offset, value.Version.Buffer); | |||
offset += JT809BinaryExtensions.WriteByteLittle(memoryOwner, offset, (byte)value.EncryptFlag); | |||
offset += JT809BinaryExtensions.WriteUInt32Little(memoryOwner, offset, value.EncryptKey); | |||
return offset; | |||
} | |||
} | |||
} |
@@ -0,0 +1,33 @@ | |||
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_0x1001Formatter : IJT809Formatter<JT809_0x1001> | |||
{ | |||
public JT809_0x1001 Deserialize(ReadOnlySpan<byte> bytes, out int readSize) | |||
{ | |||
int offset = 0; | |||
JT809_0x1001 jT809_0X1001 = new JT809_0x1001(); | |||
jT809_0X1001.UserId = JT809BinaryExtensions.ReadUInt32Little(bytes, ref offset); | |||
jT809_0X1001.Password = JT809BinaryExtensions.ReadStringLittle(bytes, ref offset, 8); | |||
jT809_0X1001.DownLinkIP = JT809BinaryExtensions.ReadStringLittle(bytes, ref offset, 32); | |||
jT809_0X1001.DownLinkPort = JT809BinaryExtensions.ReadUInt16Little(bytes, ref offset); | |||
readSize = offset; | |||
return jT809_0X1001; | |||
} | |||
public int Serialize(IMemoryOwner<byte> memoryOwner, int offset, JT809_0x1001 value) | |||
{ | |||
offset += JT809BinaryExtensions.WriteUInt32Little(memoryOwner, offset, value.UserId); | |||
offset += JT809BinaryExtensions.WriteStringLittle(memoryOwner, offset, value.Password.PadRight(8,'\0')); | |||
offset += JT809BinaryExtensions.WriteStringLittle(memoryOwner, offset, value.DownLinkIP.PadRight(32, '\0')); | |||
offset += JT809BinaryExtensions.WriteUInt16Little(memoryOwner, offset, value.DownLinkPort); | |||
return offset; | |||
} | |||
} | |||
} |
@@ -0,0 +1,209 @@ | |||
using JT809.Protocol.JT809Attributes; | |||
using JT809.Protocol.JT809Enums; | |||
using JT809.Protocol.JT809Exceptions; | |||
using JT809.Protocol.JT809Extensions; | |||
using System; | |||
using System.Buffers; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace JT809.Protocol.JT809Formatters | |||
{ | |||
public class JT809PackageFormatter : IJT809Formatter<JT809Package> | |||
{ | |||
public JT809Package Deserialize(ReadOnlySpan<byte> bytes, out int readSize) | |||
{ | |||
int offset = 0; | |||
JT809Package jT809Package = new JT809Package(); | |||
// 转义还原——>验证校验码——>解析消息 | |||
// 1. 解码(转义还原) | |||
ReadOnlySpan<byte> buffer = JT809DeEscape(bytes); | |||
// 2. 验证校验码 | |||
// 2.1. 获取校验位索引 | |||
int checkIndex = buffer.Length - 3; | |||
// 2.2. 获取校验码 | |||
int crcCodeOffset = 0; | |||
jT809Package.CRCCode = JT809BinaryExtensions.ReadUInt16Little(buffer.Slice(checkIndex,2),ref crcCodeOffset); | |||
// 2.3. 从消息头到校验码前一个字节 | |||
ushort checkCode = buffer.ToCRC16_CCITT(1, checkIndex); | |||
// 2.4. 验证校验码 | |||
if (jT809Package.CRCCode != checkCode) | |||
{ | |||
throw new JT809Exception(JT809ErrorCode.CRC16CheckInvalid,$"{jT809Package.CRCCode.ToString()}!={checkCode.ToString()}"); | |||
} | |||
jT809Package.BeginFlag = JT809BinaryExtensions.ReadByteLittle(buffer, ref offset); | |||
// 3.初始化消息头 | |||
try | |||
{ | |||
jT809Package.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; | |||
// 4.是否加密 | |||
switch (jT809Package.Header.EncryptFlag) | |||
{ | |||
case JT809Header_Encrypt.None: | |||
break; | |||
case JT809Header_Encrypt.Common: | |||
#warning 加密尚未实现 | |||
break; | |||
} | |||
// 5.数据体处理 | |||
// 5.1 判断是否有数据体(总长度-固定长度)> 0 | |||
if ((jT809Package.Header.MsgLength - JT809Package.FixedByteLength) > 0) | |||
{ | |||
//JT809.Protocol.JT809Enums.JT809BusinessType 映射对应消息特性 | |||
JT809BodiesTypeAttribute jT809BodiesTypeAttribute = jT809Package.Header.MsgID.GetAttribute<JT809BodiesTypeAttribute>(); | |||
if (jT809BodiesTypeAttribute != null) | |||
{ | |||
try | |||
{ | |||
var a = buffer.Slice(offset); | |||
var b = buffer.Slice(offset,checkIndex - offset); | |||
//5.2 处理消息体 | |||
jT809Package.Bodies = JT809FormatterResolverExtensions.JT809DynamicDeserialize(JT809FormatterExtensions.GetFormatter(jT809BodiesTypeAttribute.JT809BodiesType), buffer.Slice(offset, checkIndex - offset), out readSize); | |||
} | |||
catch (Exception ex) | |||
{ | |||
throw new JT809Exception(JT809ErrorCode.BodiesParseError,$"offset>{offset.ToString()}", ex); | |||
} | |||
} | |||
} | |||
jT809Package.EndFlag = buffer[buffer.Length - 1]; | |||
readSize = buffer.Length; | |||
return jT809Package; | |||
} | |||
public int Serialize(IMemoryOwner<byte> memoryOwner, int offset, JT809Package value) | |||
{ | |||
// 1. 先序列化数据体,根据数据体的长度赋值给头部,在序列化头部。 | |||
int messageBodyOffset = 0; | |||
JT809BodiesTypeAttribute jT809BodiesTypeAttribute = value.Header.MsgID.GetAttribute<JT809BodiesTypeAttribute>(); | |||
if (jT809BodiesTypeAttribute != null) | |||
{ | |||
if (value.Bodies != null) | |||
{ | |||
// 1.1 处理数据体 | |||
messageBodyOffset = JT809FormatterResolverExtensions.JT809DynamicSerialize(JT809FormatterExtensions.GetFormatter(jT809BodiesTypeAttribute.JT809BodiesType), memoryOwner, messageBodyOffset, value.Bodies); | |||
} | |||
} | |||
byte[] messageBodyData=null; | |||
if (messageBodyOffset != 0) | |||
{ | |||
// 1.2 数据加密 | |||
switch (value.Header.EncryptFlag) | |||
{ | |||
case JT809Header_Encrypt.None: | |||
break; | |||
case JT809Header_Encrypt.Common: | |||
#warning 加密尚未实现 | |||
break; | |||
} | |||
// 1.3 数据内容进行转义 | |||
messageBodyData = JT809Escape(memoryOwner.Memory.Slice(0, messageBodyOffset).Span); | |||
} | |||
// ------------------------------------开始组包 | |||
// 1.起始符 | |||
offset += JT809BinaryExtensions.WriteByteLittle(memoryOwner, offset, value.BeginFlag); | |||
// 2.赋值头数据长度 | |||
if (messageBodyOffset != 0) | |||
{ | |||
value.Header.MsgLength = (uint)(JT809Package.FixedByteLength + messageBodyData.Length); | |||
} | |||
else | |||
{ | |||
value.Header.MsgLength = JT809Package.FixedByteLength; | |||
} | |||
// 2.1写入头部数据 | |||
offset = JT809FormatterExtensions.GetFormatter<JT809Header>().Serialize(memoryOwner, offset, value.Header); | |||
if (messageBodyOffset != 0) | |||
{ | |||
// 3. 写入数据体 | |||
JT809BinaryExtensions.CopyTo(messageBodyData, memoryOwner.Memory.Span, offset); | |||
offset += messageBodyData.Length; | |||
messageBodyData = null; | |||
} | |||
// 4.校验码 | |||
offset += JT809BinaryExtensions.WriteUInt16Little(memoryOwner, offset, memoryOwner.Memory.Span.ToCRC16_CCITT(1, offset)); | |||
// 5.终止符 | |||
offset += JT809BinaryExtensions.WriteByteLittle(memoryOwner, offset, value.EndFlag); | |||
return offset; | |||
} | |||
private static ReadOnlySpan<byte> JT809DeEscape(ReadOnlySpan<byte> buffer) | |||
{ | |||
List<byte> dataList = new List<byte>(); | |||
dataList.Add(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) | |||
{ | |||
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(buffer[buffer.Length - 1]); | |||
return dataList.ToArray(); | |||
} | |||
private static byte[] JT809Escape(Span<byte> buf) | |||
{ | |||
List<byte> dataList = new List<byte>(); | |||
dataList.Add(buf[0]); | |||
for (int i = 1; i < buf.Length - 1; i++) | |||
{ | |||
var item = buf[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(buf[buf.Length - 1]); | |||
return dataList.ToArray(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
using JT809.Protocol.JT809Attributes; | |||
using JT809.Protocol.JT809Enums; | |||
using JT809.Protocol.JT809Formatters; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace JT809.Protocol | |||
{ | |||
[JT809Formatter(typeof(JT809HeaderFormatter))] | |||
public class JT809Header | |||
{ | |||
/// <summary> | |||
/// 固定为22个字节长度 | |||
/// <para>MSG LENGTH + MSG_SN + MSG_ID + MSG_GNSSCENTERID + VERSION_FLAG + ENCRYPT_FLAG + ENCRYPT_KEY</para> | |||
/// <para>4 + 4 + 2 + 4 + 3 + 1 + 4 = 22</para> | |||
/// </summary> | |||
public const int FixedByteLength = 22; | |||
/// <summary> | |||
/// 数据长度(包括头标识、数据头、数据体和尾标识) | |||
/// <para>头标识 + 数据头 + 数据体 + 尾标识</para> | |||
/// <para>1 + 22 + n + 1</para> | |||
/// </summary> | |||
public uint MsgLength { get; set; } | |||
/// <summary> | |||
/// 报文序列号 a | |||
/// </summary> | |||
public uint MsgSN { get; set; } | |||
/// <summary> | |||
/// 业务数据类型 | |||
/// </summary> | |||
public JT809BusinessType MsgID { get; set; } | |||
/// <summary> | |||
/// 下级平台接入码,上级平台给下级平台分配唯一标识码。 | |||
/// </summary> | |||
public uint MsgGNSSCENTERID { get; set; } | |||
/// <summary> | |||
/// 协议版本好标识,上下级平台之间采用的标准协议版 | |||
/// 编号;长度为 3 个字节来表示,0x01 0x02 0x0F 标识 | |||
/// 的版本号是 v1.2.15,以此类推。 | |||
/// </summary> | |||
public JT809Header_Version Version { get; set; } = new JT809Header_Version(); | |||
/// <summary> | |||
/// 报文加密标识位 b: 0 表示报文不加密,1 表示报文加密。 | |||
/// </summary> | |||
public JT809Header_Encrypt EncryptFlag { get; set; } = JT809Header_Encrypt.None; | |||
/// <summary> | |||
/// 数据加密的密匙,长度为 4 个字节 | |||
/// </summary> | |||
public uint EncryptKey { get; set; } | |||
} | |||
} |
@@ -1,9 +1,9 @@ | |||
namespace JT809.Protocol.Enums | |||
namespace JT809.Protocol | |||
{ | |||
/// <summary> | |||
/// 报文加密标识位 b: 0 表示报文不加密,1 表示报文加密。 | |||
/// </summary> | |||
public enum EncryptOpitions : byte | |||
public enum JT809Header_Encrypt:byte | |||
{ | |||
None = 0X00, | |||
Common = 0X01, |
@@ -0,0 +1,62 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace JT809.Protocol | |||
{ | |||
public class JT809Header_Version | |||
{ | |||
public byte[] Buffer { get; } = new byte[3]; | |||
private const int MajorIndex = 0; | |||
private const int MinorIndex = 1; | |||
private const int BuildIndex = 2; | |||
public const int FixedByteLength = 3; | |||
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; } | |||
} | |||
/// <summary> | |||
/// 默认1.0.0版本 | |||
/// </summary> | |||
public JT809Header_Version() | |||
{ | |||
Major = 1; | |||
Minor = 0; | |||
Build = 0; | |||
} | |||
public JT809Header_Version(byte major, byte minor, byte buid) | |||
{ | |||
Major = major; | |||
Minor = minor; | |||
Build = buid; | |||
} | |||
public JT809Header_Version(byte[] bytes) | |||
{ | |||
Major = bytes[0]; | |||
Minor = bytes[1]; | |||
Build = bytes[2]; | |||
} | |||
public override string ToString() | |||
{ | |||
return $"{Major}.{Minor}.{Build}"; | |||
} | |||
} | |||
} |
@@ -0,0 +1,34 @@ | |||
using JT809.Protocol.JT809Attributes; | |||
using JT809.Protocol.JT809Formatters.JT809MessageBodyFormatters; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace JT809.Protocol.JT809MessageBody | |||
{ | |||
/// <summary> | |||
/// 主链路登录请求消息 | |||
/// </summary> | |||
[JT809Formatter(typeof(JT809_0x1001Formatter))] | |||
public class JT809_0x1001: JT809Bodies | |||
{ | |||
/// <summary> | |||
/// 用户名 | |||
/// </summary> | |||
public uint UserId { get; set; } | |||
/// <summary> | |||
/// 密码 | |||
/// 8位 | |||
/// </summary> | |||
public string Password { get; set; } | |||
/// <summary> | |||
/// 下级平台提供对应的从链路服务端 IP 地址 | |||
/// 32位 | |||
/// </summary> | |||
public string DownLinkIP { get; set; } | |||
/// <summary> | |||
/// 下级平台提供对应的从链路服务器端口号 | |||
/// </summary> | |||
public ushort DownLinkPort { get; set; } | |||
} | |||
} |
@@ -0,0 +1,33 @@ | |||
using JT809.Protocol.JT809Attributes; | |||
using JT809.Protocol.JT809Formatters; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace JT809.Protocol | |||
{ | |||
[JT809Formatter(typeof(JT809PackageFormatter))] | |||
public class JT809Package | |||
{ | |||
public const byte BEGINFLAG = 0X5B; | |||
public const byte ENDFLAG = 0X5D; | |||
/// <summary> | |||
/// 固定为26个字节长度 | |||
/// <para>Head flag + Message Header + CRC Code + End Flag</para> | |||
/// <para>1 + 22 + 2 + 1 = 26</para> | |||
/// </summary> | |||
public const int FixedByteLength = 26; | |||
public byte BeginFlag { get; set; } = BEGINFLAG; | |||
public JT809Header Header { get; set; } | |||
public JT809Bodies Bodies { get; set; } | |||
public ushort CRCCode { get; set; } | |||
public byte EndFlag { get; set; } = ENDFLAG; | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
using JT809.Protocol.JT809Enums; | |||
using JT809.Protocol.JT809Exceptions; | |||
using JT809.Protocol.JT809Extensions; | |||
using System; | |||
using System.Buffers; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace JT809.Protocol | |||
{ | |||
public static class JT809Serializer | |||
{ | |||
public static byte[] Serialize(JT809Package jT809Package) | |||
{ | |||
var formatter = JT809FormatterExtensions.GetFormatter<JT809Package>(); | |||
var pool = MemoryPool<byte>.Shared; | |||
IMemoryOwner<byte> buffer = pool.Rent(4096); | |||
try | |||
{ | |||
var len = formatter.Serialize(buffer, 0, jT809Package); | |||
return buffer.Memory.Slice(0, len).ToArray(); | |||
} | |||
finally | |||
{ | |||
// 源码:System.Memory.MemoryPool | |||
// private static readonly MemoryPool<T> s_shared = new ArrayMemoryPool<T>(); | |||
// 单例内存池 不需要手动释放资源 | |||
buffer.Dispose(); | |||
} | |||
} | |||
public static JT809Package Deserialize(ReadOnlySpan<byte> bytes) | |||
{ | |||
var formatter = JT809FormatterExtensions.GetFormatter<JT809Package>(); | |||
return formatter.Deserialize(bytes, out int readSize); | |||
} | |||
public static byte[] Serialize<T>(T obj) | |||
{ | |||
var formatter = JT809FormatterExtensions.GetFormatter<T>(); | |||
var pool = MemoryPool<byte>.Shared; | |||
IMemoryOwner<byte> buffer = pool.Rent(10240); | |||
try | |||
{ | |||
var len = formatter.Serialize(buffer, 0, obj); | |||
return buffer.Memory.Slice(0, len).ToArray(); | |||
} | |||
finally | |||
{ | |||
// 源码:System.Memory.MemoryPool | |||
// private static readonly MemoryPool<T> s_shared = new ArrayMemoryPool<T>(); | |||
// 单例内存池 不需要手动释放资源 | |||
buffer.Dispose(); | |||
} | |||
} | |||
public static T Deserialize<T>(ReadOnlySpan<byte> bytes) | |||
{ | |||
var formatter = JT809FormatterExtensions.GetFormatter<T>(); | |||
int readSize; | |||
return formatter.Deserialize(bytes, out readSize); | |||
} | |||
} | |||
} |