diff --git a/README.md b/README.md index 1161da5..e3207e0 100644 --- a/README.md +++ b/README.md @@ -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 | √ | diff --git a/src/JT809.Protocol.Test/JT809.Protocol.Test.csproj b/src/JT809.Protocol.Test/JT809.Protocol.Test.csproj index 0970015..0553d6f 100644 --- a/src/JT809.Protocol.Test/JT809.Protocol.Test.csproj +++ b/src/JT809.Protocol.Test/JT809.Protocol.Test.csproj @@ -1,15 +1,22 @@ - netcoreapp2.0 + netcoreapp2.1 false - - - + + + + + + + + all + runtime; build; native; contentfiles; analyzers + diff --git a/src/JT809.Protocol.Test/JT809HeaderTest.cs b/src/JT809.Protocol.Test/JT809HeaderTest.cs new file mode 100644 index 0000000..7cdd98b --- /dev/null +++ b/src/JT809.Protocol.Test/JT809HeaderTest.cs @@ -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(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); + } + } +} diff --git a/src/JT809.Protocol.Test/JT809MessageBody/JT809_0x1001Test.cs b/src/JT809.Protocol.Test/JT809MessageBody/JT809_0x1001Test.cs new file mode 100644 index 0000000..246115f --- /dev/null +++ b/src/JT809.Protocol.Test/JT809MessageBody/JT809_0x1001Test.cs @@ -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(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); + } + } +} diff --git a/src/JT809.Protocol.Test/JT809Packages/JT809_0x1001PackageTest.cs b/src/JT809.Protocol.Test/JT809Packages/JT809_0x1001PackageTest.cs new file mode 100644 index 0000000..b9717db --- /dev/null +++ b/src/JT809.Protocol.Test/JT809Packages/JT809_0x1001PackageTest.cs @@ -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"); + } + } +} diff --git a/src/JT809.Protocol.Test/PackageTest.cs b/src/JT809.Protocol.Test/PackageTest.cs deleted file mode 100644 index e1e86fb..0000000 --- a/src/JT809.Protocol.Test/PackageTest.cs +++ /dev/null @@ -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(); - } - } -} diff --git a/src/JT809.Protocol/Enums/ErrorCode.cs b/src/JT809.Protocol/Enums/ErrorCode.cs deleted file mode 100644 index d4481e3..0000000 --- a/src/JT809.Protocol/Enums/ErrorCode.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace JT809.Protocol.Enums -{ - public enum ErrorCode - { - CRC16CheckInvalid = 1001, - HeaderLengthNotEqualBodyLength=1002 - } -} diff --git a/src/JT809.Protocol/Exceptions/JT809Exception.cs b/src/JT809.Protocol/Exceptions/JT809Exception.cs deleted file mode 100644 index 96a9d17..0000000 --- a/src/JT809.Protocol/Exceptions/JT809Exception.cs +++ /dev/null @@ -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; - } -} diff --git a/src/JT809.Protocol/JT809.Protocol.csproj b/src/JT809.Protocol/JT809.Protocol.csproj index 4543a1c..7b668e3 100644 --- a/src/JT809.Protocol/JT809.Protocol.csproj +++ b/src/JT809.Protocol/JT809.Protocol.csproj @@ -1,7 +1,8 @@ - + netstandard2.0 + latest @@ -9,20 +10,44 @@ + + - + + + - + + + - + + + + + + + + + + + + + + + + + + + diff --git a/src/JT809.Protocol/JT809Attributes/JT809BodiesTypeAttribute.cs b/src/JT809.Protocol/JT809Attributes/JT809BodiesTypeAttribute.cs new file mode 100644 index 0000000..bf0e710 --- /dev/null +++ b/src/JT809.Protocol/JT809Attributes/JT809BodiesTypeAttribute.cs @@ -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;} + } +} diff --git a/src/JT809.Protocol/JT809Attributes/JT809FormatterAttribute.cs b/src/JT809.Protocol/JT809Attributes/JT809FormatterAttribute.cs new file mode 100644 index 0000000..af1287b --- /dev/null +++ b/src/JT809.Protocol/JT809Attributes/JT809FormatterAttribute.cs @@ -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; + } + } +} diff --git a/src/JT809.Protocol/JT809Bodies.cs b/src/JT809.Protocol/JT809Bodies.cs new file mode 100644 index 0000000..69d1d77 --- /dev/null +++ b/src/JT809.Protocol/JT809Bodies.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JT809.Protocol +{ + public abstract class JT809Bodies + { + } +} diff --git a/src/JT809.Protocol/Enums/BusinessType.cs b/src/JT809.Protocol/JT809Enums/JT809BusinessType.cs similarity index 95% rename from src/JT809.Protocol/Enums/BusinessType.cs rename to src/JT809.Protocol/JT809Enums/JT809BusinessType.cs index 4e735d2..40c418b 100644 --- a/src/JT809.Protocol/Enums/BusinessType.cs +++ b/src/JT809.Protocol/JT809Enums/JT809BusinessType.cs @@ -1,163 +1,166 @@ -using System.ComponentModel; - -namespace JT809.Protocol.Enums -{ - /// - /// 业务数据类型标识 - /// 数据交换一协议规定的业务数据类型名称和标识常量定义见表 73。业务数据类型标识的命名规则如下: - /// a) 上级平台向下级平台发送的请求消息,一般以“DOWN_”开头,以后缀_REQ 结尾;而下级平台向上级平台发送的请求消息一般以“UP_”开头,以后缀_REQ 结尾; - /// b) 当上下级平台之间有应答消息情况下,应答消息可继续沿用对应的请求消息开头标识符,而通过后缀 RSP 来标识结尾。 - /// - public enum BusinessType : ushort - { - #region 链路管理类 - /// - ///主链路登录请求消息 - /// - [Description("主链路登录请求消息")] - UP_CONNECT_REQ = 0x1001, - /// - ///主链路登录应答消息 - /// - [Description("主链路登录应答消息")] - UP_CONNECT_RSP = 0x1002, - /// - ///主链路注销请求消息 - /// - [Description("主链路注销请求消息")] - UP_DISCONNECT_REQ = 0x1003, - /// - ///主链路注销应答消息 - /// - [Description("主链路注销应答消息")] - UP_DISCONNECT_RSP = 0x1004, - /// - ///主链路连接保持请求消息 - /// - [Description("主链路连接保持请求消息")] - UP_LINKTEST_REQ = 0x1005, - /// - ///主链路连接保持应答消息 - /// - [Description("主链路连接保持应答消息")] - UP_LINKTEST_RSP = 0x1006, - /// - ///主链路断开通知消息 - /// - [Description("主链路断开通知消息")] - UP_DISCONNECT_INFORM = 0x1007, - /// - ///下级平台主动关闭链路通知消息 - /// - [Description("下级平台主动关闭链路通知消息")] - UP_CLOSELINK_INFORM = 0x1008, - /// - ///从链路连接请求消息 - /// - [Description("从链路连接请求消息")] - DOWN_CONNECT_REQ = 0x9001, - /// - ///从链路连接应答消息 - /// - [Description("从链路连接应答消息")] - DOWN_CONNECT_RSP = 0x9002, - /// - ///从链路注销请求消息 - /// - [Description("从链路注销请求消息")] - DOWN_DISCONNECT_REQ = 0x9003, - /// - ///从链路注销应答消息 - /// - [Description("从链路注销应答消息")] - DOWN_DISCONNECT_RSP = 0x9004, - /// - ///从链路连接保持请求消息 - /// - [Description("从链路连接保持请求消息")] - DOWN_LINKTEST_REQ = 0x9005, - /// - ///从链路连接保持应答消息 - /// - [Description("从链路连接保持应答消息")] - DOWN_LINKTEST_RSP = 0x9006, - /// - ///从链路断开通知消息 - /// - [Description("从链路断开通知消息")] - DOWN_DISCONNECT_INFORM = 0x9007, - /// - ///上级平台主动关闭链路通知消息 - /// - [Description("上级平台主动关闭链路通知消息")] - DOWN_CLOSELINK_INFORM = 0x9008, - #endregion - #region 信息统计类 - /// - ///接收定位信息数量通知消息 - /// - [Description("接收定位信息数量通知消息")] - DOWN_TOTAL_RECV_BACK_MSG = 0x9101, - #endregion - #region 车辆动态信息交换 - /// - ///主链路动态信息交换消息 - /// - [Description("主链路动态信息交换消息")] - UP_EXG_MSG = 0x1200, - /// - ///从链路动态信息交换消息 - /// - [Description("从链路动态信息交换消息")] - DOWN_EXG_MSG = 0x9200, - #endregion - #region 平台间信息交互类 - /// - ///主链路平台间信息交互消息 - /// - [Description("主链路平台间信息交互消息")] - UP_PLATFORM_MSG = 0x1300, - /// - ///从链路平台间信息交互消息 - /// - [Description("从链路平台间信息交互消息")] - DOWN_PLATFORM_MSG = 0x9300, - #endregion - #region 车辆报警信息交互类 - /// - ///主链路报警信息交互消息 - /// - [Description("主链路报警信息交互消息")] - UP_WARN_MSG = 0x1400, - /// - ///从链路报警信息交互消息 - /// - [Description("从链路报警信息交互消息")] - DOWN_WARN_MSG = 0x9400, - #endregion - #region 车辆监管类 - /// - ///主链路车辆监管消息 - /// - [Description("主链路车辆监管消息")] - UP_CTRL_MSG = 0x1500, - /// - ///从链路车辆监管消息 - /// - [Description("从链路车辆监管消息")] - DOWN_CTRL_MSG = 0x9500, - #endregion - #region 车辆静态信息交换类 - /// - ///主链路静态信息交换消息 - /// - [Description("主链路静态信息交换消息")] - UP_BASE_MSG = 0x1600, - /// - ///从链路静态信息交换消息 - /// - [Description("从链路静态信息交换消息")] - DOWN_BASE_MSG = 0x9600, - #endregion - } -} +using JT809.Protocol.JT809Attributes; +using JT809.Protocol.JT809MessageBody; +using System.ComponentModel; + +namespace JT809.Protocol.JT809Enums +{ + /// + /// 业务数据类型标识 + /// 数据交换一协议规定的业务数据类型名称和标识常量定义见表 73。业务数据类型标识的命名规则如下: + /// a) 上级平台向下级平台发送的请求消息,一般以“DOWN_”开头,以后缀_REQ 结尾;而下级平台向上级平台发送的请求消息一般以“UP_”开头,以后缀_REQ 结尾; + /// b) 当上下级平台之间有应答消息情况下,应答消息可继续沿用对应的请求消息开头标识符,而通过后缀 RSP 来标识结尾。 + /// + public enum JT809BusinessType : ushort + { + #region 链路管理类 + /// + ///主链路登录请求消息 + /// + [Description("主链路登录请求消息")] + [JT809BodiesType(typeof(JT809_0x1001))] + UP_CONNECT_REQ = 0x1001, + /// + ///主链路登录应答消息 + /// + [Description("主链路登录应答消息")] + UP_CONNECT_RSP = 0x1002, + /// + ///主链路注销请求消息 + /// + [Description("主链路注销请求消息")] + UP_DISCONNECT_REQ = 0x1003, + /// + ///主链路注销应答消息 + /// + [Description("主链路注销应答消息")] + UP_DISCONNECT_RSP = 0x1004, + /// + ///主链路连接保持请求消息 + /// + [Description("主链路连接保持请求消息")] + UP_LINKTEST_REQ = 0x1005, + /// + ///主链路连接保持应答消息 + /// + [Description("主链路连接保持应答消息")] + UP_LINKTEST_RSP = 0x1006, + /// + ///主链路断开通知消息 + /// + [Description("主链路断开通知消息")] + UP_DISCONNECT_INFORM = 0x1007, + /// + ///下级平台主动关闭链路通知消息 + /// + [Description("下级平台主动关闭链路通知消息")] + UP_CLOSELINK_INFORM = 0x1008, + /// + ///从链路连接请求消息 + /// + [Description("从链路连接请求消息")] + DOWN_CONNECT_REQ = 0x9001, + /// + ///从链路连接应答消息 + /// + [Description("从链路连接应答消息")] + DOWN_CONNECT_RSP = 0x9002, + /// + ///从链路注销请求消息 + /// + [Description("从链路注销请求消息")] + DOWN_DISCONNECT_REQ = 0x9003, + /// + ///从链路注销应答消息 + /// + [Description("从链路注销应答消息")] + DOWN_DISCONNECT_RSP = 0x9004, + /// + ///从链路连接保持请求消息 + /// + [Description("从链路连接保持请求消息")] + DOWN_LINKTEST_REQ = 0x9005, + /// + ///从链路连接保持应答消息 + /// + [Description("从链路连接保持应答消息")] + DOWN_LINKTEST_RSP = 0x9006, + /// + ///从链路断开通知消息 + /// + [Description("从链路断开通知消息")] + DOWN_DISCONNECT_INFORM = 0x9007, + /// + ///上级平台主动关闭链路通知消息 + /// + [Description("上级平台主动关闭链路通知消息")] + DOWN_CLOSELINK_INFORM = 0x9008, + #endregion + #region 信息统计类 + /// + ///接收定位信息数量通知消息 + /// + [Description("接收定位信息数量通知消息")] + DOWN_TOTAL_RECV_BACK_MSG = 0x9101, + #endregion + #region 车辆动态信息交换 + /// + ///主链路动态信息交换消息 + /// + [Description("主链路动态信息交换消息")] + UP_EXG_MSG = 0x1200, + /// + ///从链路动态信息交换消息 + /// + [Description("从链路动态信息交换消息")] + DOWN_EXG_MSG = 0x9200, + #endregion + #region 平台间信息交互类 + /// + ///主链路平台间信息交互消息 + /// + [Description("主链路平台间信息交互消息")] + UP_PLATFORM_MSG = 0x1300, + /// + ///从链路平台间信息交互消息 + /// + [Description("从链路平台间信息交互消息")] + DOWN_PLATFORM_MSG = 0x9300, + #endregion + #region 车辆报警信息交互类 + /// + ///主链路报警信息交互消息 + /// + [Description("主链路报警信息交互消息")] + UP_WARN_MSG = 0x1400, + /// + ///从链路报警信息交互消息 + /// + [Description("从链路报警信息交互消息")] + DOWN_WARN_MSG = 0x9400, + #endregion + #region 车辆监管类 + /// + ///主链路车辆监管消息 + /// + [Description("主链路车辆监管消息")] + UP_CTRL_MSG = 0x1500, + /// + ///从链路车辆监管消息 + /// + [Description("从链路车辆监管消息")] + DOWN_CTRL_MSG = 0x9500, + #endregion + #region 车辆静态信息交换类 + /// + ///主链路静态信息交换消息 + /// + [Description("主链路静态信息交换消息")] + UP_BASE_MSG = 0x1600, + /// + ///从链路静态信息交换消息 + /// + [Description("从链路静态信息交换消息")] + DOWN_BASE_MSG = 0x9600, + #endregion + } +} diff --git a/src/JT809.Protocol/JT809Enums/JT809ErrorCode.cs b/src/JT809.Protocol/JT809Enums/JT809ErrorCode.cs new file mode 100644 index 0000000..81f5c68 --- /dev/null +++ b/src/JT809.Protocol/JT809Enums/JT809ErrorCode.cs @@ -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 + } +} diff --git a/src/JT809.Protocol/Enums/SubBusinessType.cs b/src/JT809.Protocol/JT809Enums/JT809SubBusinessType.cs similarity index 96% rename from src/JT809.Protocol/Enums/SubBusinessType.cs rename to src/JT809.Protocol/JT809Enums/JT809SubBusinessType.cs index 18ca14a..3ef3ca0 100644 --- a/src/JT809.Protocol/Enums/SubBusinessType.cs +++ b/src/JT809.Protocol/JT809Enums/JT809SubBusinessType.cs @@ -1,260 +1,260 @@ -using System.ComponentModel; - -namespace JT809.Protocol.Enums -{ - /// - ///子业务类型标识 - /// - public enum SubBusinessType : ushort - { - #region 主链路动态信息交换消息 UP_EXG_MSG - /// - /// 上传车辆注册信息 - /// - [Description("上传车辆注册信息")] - UP_EXG_MSG_REGISTER = 0x1201, - /// - ///实时上传车辆定位信息 - /// - [Description("实时上传车辆定位信息")] - UP_EXG_MSG_REAL_LOCATION = 0x1202, - /// - ///车辆定位信息自动补报 - /// - [Description("车辆定位信息自动补报")] - UP_EXG_MSG_HISTORY_LOCATION = 0x1203, - /// - ///启动车辆定位信息交换应答 - /// - [Description("启动车辆定位信息交换应答")] - UP_EXG_MSG_RETURN_STARTUP_ACK = 0x1205, - /// - ///结束车辆定位信息交换应答 - /// - [Description("结束车辆定位信息交换应答")] - UP_EXG_MSG_RETURN_END_ACK = 0x1206, - /// - ///申请交换指定车辆定位信息请求 - /// - [Description("申请交换指定车辆定位信息请求")] - UP_EXG_MSG_APPLY_FOR_MONITOR_STARTUP = 0x1207, - /// - ///取消交换指定车辆定位信息请求 - /// - [Description("取消交换指定车辆定位信息请求")] - UP_EXG_MSG_APPLY_FOR_MONITOR_END = 0x1208, - /// - ///补发车辆定位信息请求 - /// - [Description("补发车辆定位信息请求")] - UP_EXG_MSG_APPLY_HISGNSSDATA_REQ = 0x1209, - /// - ///上报车辆驾驶员身份识别信息应答 - /// - [Description("上报车辆驾驶员身份识别信息应答")] - UP_EXG_MSG_REPORT_DRIVER_INFO_ACK = 0x120A, - /// - ///上报车辆电子运单应答 - /// - [Description("上报车辆电子运单应答")] - UP_EXG_MSG_TAKE_EWAYBILL_ACK = 0x120B, - /// - ///主动上报驾驶员身份信息 - /// - [Description("主动上报驾驶员身份信息")] - UP_EXG_MSG_REPORT_DRIVER_INFO = 0x120C, - /// - ///主动上报车辆电子运单信息 - /// - [Description("主动上报车辆电子运单信息")] - UP_EXG_MSG_REPORT_EWAYBILL_INFO = 0x120D, - #endregion - - #region 从链路动态信息交换消息 DOWN_EXG_MSG - /// - ///交换车辆定位信息 - /// - [Description("交换车辆定位信息")] - DOWN_EXG_MSG_CAR_LOCATION = 0x9202, - /// - ///车辆定位信息交换补发 - /// - [Description("车辆定位信息交换补发")] - DOWN_EXG_MSG_HISTORY_ARCOSSAREA = 0x9203, - /// - ///交换车辆静态信息 - /// - [Description("交换车辆静态信息")] - DOWN_EXG_MSG_CAR_INFO = 0x9204, - /// - ///启动车辆定位信息交换请求 - /// - [Description("启动车辆定位信息交换请求")] - DOWN_EXG_MSG_RETURN_STARTUP = 0x9205, - /// - ///结束车辆定位信息交换请求 - /// - [Description("结束车辆定位信息交换请求")] - DOWN_EXG_MSG_RETURN_END = 0x9206, - /// - ///申请交换指定车辆定位信息应答 - /// - [Description("申请交换指定车辆定位信息应答")] - DOWN_EXG_MSG_APPLY_FOR_MONITOR_STARTUP_ACK = 0x9207, - /// - ///取消交换指定车辆定位信息应答 - /// - [Description("取消交换指定车辆定位信息应答")] - DOWN_EXG_MSG_APPLY_FOR_MONITOR_END_ACK = 0x9208, - /// - ///补发车辆定位信息应答 - /// - [Description("补发车辆定位信息应答")] - DOWN_EXG_MSG_APPLY_HISGNSSDATA_ACK = 0x9209, - /// - ///上报车辆驾驶员身份识别信息请求 - /// - [Description("上报车辆驾驶员身份识别信息请求")] - DOWN_EXG_MSG_REPORT_DRIVER_INFO = 0x920A, - /// - ///上报车辆电子运单请求 - /// - [Description("上报车辆电子运单请求")] - DOWN_EXG_MSG_TAKE_EWAYBILL_REQ = 0x920B, - #endregion - - #region 主链路平台信息交互消息 UP_PLATFORM_MSG - /// - ///平台查岗应答 - /// - [Description("平台查岗应答")] - UP_PLATFORM_MSG_POST_QUERY_ACK = 0x1301, - /// - ///下发平台间报文应答 - /// - [Description("下发平台间报文应答")] - UP_PLATFORM_MSG_INFO_ACK = 0x1302, - #endregion - - #region 从链路平台信息交互消息 DOWN_PLATFORM_MSG - /// - ///平台查岗请求 - /// - [Description("平台查岗请求")] - DOWN_PLATFORM_MSG_POST_QUERY_REQ = 0x9301, - /// - ///下发平台间报文请求 - /// - [Description("下发平台间报文请求")] - DOWN_PLATFORM_MSG_INFO_REQ = 0x9302, - #endregion - - #region 主链路报警信息交互消息 UP_WARN_MSG - /// - ///报警督办应答 - /// - [Description("报警督办应答")] - UP_WARN_MSG_URGE_TODO_ACK = 0x1401, - /// - ///上报报警信息 - /// - [Description("上报报警信息")] - UP_WARN_MSG_ADPT_INFO = 0x1402, - /// - ///主动上报报警处理结果信息 - /// - [Description("主动上报报警处理结果信息")] - UP_WARN_MSG_ADPT_TODO_INFO = 0x1403, - #endregion - - #region 从链路报警信息交互消息 DOWN_WARN_MSG - /// - ///报警督办请求 - /// - [Description("报警督办请求")] - DOWN_WARN_MSG_URGE_TODO_REQ = 0x9401, - /// - ///报警预警 - /// - [Description("报警预警")] - DOWN_WARN_MSG_INFORM_TIPS = 0x9402, - /// - ///实时交换报警信息 - /// - [Description("实时交换报警信息")] - DOWN_WARN_MSG_EXG_INFORM = 0x9403, - #endregion - - #region 主链路车辆监管消息 UP_CTRL_MSG - /// - ///车辆单向监听应答 - /// - [Description("车辆单向监听应答")] - UP_CTRL_MSG_MONITOR_VEHICLE_ACK = 0x1501, - /// - ///车辆拍照应答 - /// - [Description("车辆拍照应答")] - UP_CTRL_MSG_TAKE_PHOTO_ACK = 0x1502, - /// - ///下发车辆报文应答 - /// - [Description("下发车辆报文应答")] - UP_CTRL_MSG_TEXT_INFO_ACK = 0x1503, - /// - ///上报车辆行驶记录应答 - /// - [Description("上报车辆行驶记录应答")] - UP_CTRL_MSG_TAKE_TRAVEL_ACK = 0x1504, - /// - ///车辆应急接入监管平台应答消息 - /// - [Description("车辆应急接入监管平台应答消息")] - UP_CTRL_MSG_EMERGENCY_MONITORING_ACK = 0x1505, - #endregion - - #region 从链路车辆监管消息 DOWN_CTRL_MSG - /// - ///车辆单向监听请求 - /// - [Description("车辆单向监听请求")] - DOWN_CTRL_MSG_MONITOR_VEHICLE_REQ = 0x9501, - /// - ///车辆拍照请求 - /// - [Description("车辆拍照请求")] - DOWN_CTRL_MSG_TAKE_PHOTO_REQ = 0x9502, - /// - ///下发车辆报文请求 - /// - [Description("下发车辆报文请求")] - DOWN_CTRL_MSG_TEXT_INFO = 0x9503, - /// - ///上报车辆行驶记录请求 - /// - [Description("上报车辆行驶记录请求")] - DOWN_CTRL_MSG_TAKE_TRAVEL_REQ = 0x9504, - /// - ///车辆应急接入监管平台请求消息 - /// - [Description("车辆应急接入监管平台请求消息")] - DOWN_CTRL_MSG_EMERGENCY_MONITORING_REQ = 0x9505, - #endregion - - #region 主链路静态信息交换消息 UP_BASE_MSG - /// - ///补报车辆静态信息应答 - /// - [Description("补报车辆静态信息应答")] - UP_BASE_MSG_VEHICLE_ADDED_ACK = 0x1601, - #endregion - - #region 从链路静态信息交换消息 DOWN_BASE_MSG - /// - ///补报车辆静态信息请求 - /// - [Description("补报车辆静态信息请求")] - DOWN_BASE_MSG_VEHICLE_ADDED = 0x9601, - #endregion - } -} +using System.ComponentModel; + +namespace JT809.Protocol.JT809Enums +{ + /// + ///子业务类型标识 + /// + public enum JT809SubBusinessType : ushort + { + #region 主链路动态信息交换消息 UP_EXG_MSG + /// + /// 上传车辆注册信息 + /// + [Description("上传车辆注册信息")] + UP_EXG_MSG_REGISTER = 0x1201, + /// + ///实时上传车辆定位信息 + /// + [Description("实时上传车辆定位信息")] + UP_EXG_MSG_REAL_LOCATION = 0x1202, + /// + ///车辆定位信息自动补报 + /// + [Description("车辆定位信息自动补报")] + UP_EXG_MSG_HISTORY_LOCATION = 0x1203, + /// + ///启动车辆定位信息交换应答 + /// + [Description("启动车辆定位信息交换应答")] + UP_EXG_MSG_RETURN_STARTUP_ACK = 0x1205, + /// + ///结束车辆定位信息交换应答 + /// + [Description("结束车辆定位信息交换应答")] + UP_EXG_MSG_RETURN_END_ACK = 0x1206, + /// + ///申请交换指定车辆定位信息请求 + /// + [Description("申请交换指定车辆定位信息请求")] + UP_EXG_MSG_APPLY_FOR_MONITOR_STARTUP = 0x1207, + /// + ///取消交换指定车辆定位信息请求 + /// + [Description("取消交换指定车辆定位信息请求")] + UP_EXG_MSG_APPLY_FOR_MONITOR_END = 0x1208, + /// + ///补发车辆定位信息请求 + /// + [Description("补发车辆定位信息请求")] + UP_EXG_MSG_APPLY_HISGNSSDATA_REQ = 0x1209, + /// + ///上报车辆驾驶员身份识别信息应答 + /// + [Description("上报车辆驾驶员身份识别信息应答")] + UP_EXG_MSG_REPORT_DRIVER_INFO_ACK = 0x120A, + /// + ///上报车辆电子运单应答 + /// + [Description("上报车辆电子运单应答")] + UP_EXG_MSG_TAKE_EWAYBILL_ACK = 0x120B, + /// + ///主动上报驾驶员身份信息 + /// + [Description("主动上报驾驶员身份信息")] + UP_EXG_MSG_REPORT_DRIVER_INFO = 0x120C, + /// + ///主动上报车辆电子运单信息 + /// + [Description("主动上报车辆电子运单信息")] + UP_EXG_MSG_REPORT_EWAYBILL_INFO = 0x120D, + #endregion + + #region 从链路动态信息交换消息 DOWN_EXG_MSG + /// + ///交换车辆定位信息 + /// + [Description("交换车辆定位信息")] + DOWN_EXG_MSG_CAR_LOCATION = 0x9202, + /// + ///车辆定位信息交换补发 + /// + [Description("车辆定位信息交换补发")] + DOWN_EXG_MSG_HISTORY_ARCOSSAREA = 0x9203, + /// + ///交换车辆静态信息 + /// + [Description("交换车辆静态信息")] + DOWN_EXG_MSG_CAR_INFO = 0x9204, + /// + ///启动车辆定位信息交换请求 + /// + [Description("启动车辆定位信息交换请求")] + DOWN_EXG_MSG_RETURN_STARTUP = 0x9205, + /// + ///结束车辆定位信息交换请求 + /// + [Description("结束车辆定位信息交换请求")] + DOWN_EXG_MSG_RETURN_END = 0x9206, + /// + ///申请交换指定车辆定位信息应答 + /// + [Description("申请交换指定车辆定位信息应答")] + DOWN_EXG_MSG_APPLY_FOR_MONITOR_STARTUP_ACK = 0x9207, + /// + ///取消交换指定车辆定位信息应答 + /// + [Description("取消交换指定车辆定位信息应答")] + DOWN_EXG_MSG_APPLY_FOR_MONITOR_END_ACK = 0x9208, + /// + ///补发车辆定位信息应答 + /// + [Description("补发车辆定位信息应答")] + DOWN_EXG_MSG_APPLY_HISGNSSDATA_ACK = 0x9209, + /// + ///上报车辆驾驶员身份识别信息请求 + /// + [Description("上报车辆驾驶员身份识别信息请求")] + DOWN_EXG_MSG_REPORT_DRIVER_INFO = 0x920A, + /// + ///上报车辆电子运单请求 + /// + [Description("上报车辆电子运单请求")] + DOWN_EXG_MSG_TAKE_EWAYBILL_REQ = 0x920B, + #endregion + + #region 主链路平台信息交互消息 UP_PLATFORM_MSG + /// + ///平台查岗应答 + /// + [Description("平台查岗应答")] + UP_PLATFORM_MSG_POST_QUERY_ACK = 0x1301, + /// + ///下发平台间报文应答 + /// + [Description("下发平台间报文应答")] + UP_PLATFORM_MSG_INFO_ACK = 0x1302, + #endregion + + #region 从链路平台信息交互消息 DOWN_PLATFORM_MSG + /// + ///平台查岗请求 + /// + [Description("平台查岗请求")] + DOWN_PLATFORM_MSG_POST_QUERY_REQ = 0x9301, + /// + ///下发平台间报文请求 + /// + [Description("下发平台间报文请求")] + DOWN_PLATFORM_MSG_INFO_REQ = 0x9302, + #endregion + + #region 主链路报警信息交互消息 UP_WARN_MSG + /// + ///报警督办应答 + /// + [Description("报警督办应答")] + UP_WARN_MSG_URGE_TODO_ACK = 0x1401, + /// + ///上报报警信息 + /// + [Description("上报报警信息")] + UP_WARN_MSG_ADPT_INFO = 0x1402, + /// + ///主动上报报警处理结果信息 + /// + [Description("主动上报报警处理结果信息")] + UP_WARN_MSG_ADPT_TODO_INFO = 0x1403, + #endregion + + #region 从链路报警信息交互消息 DOWN_WARN_MSG + /// + ///报警督办请求 + /// + [Description("报警督办请求")] + DOWN_WARN_MSG_URGE_TODO_REQ = 0x9401, + /// + ///报警预警 + /// + [Description("报警预警")] + DOWN_WARN_MSG_INFORM_TIPS = 0x9402, + /// + ///实时交换报警信息 + /// + [Description("实时交换报警信息")] + DOWN_WARN_MSG_EXG_INFORM = 0x9403, + #endregion + + #region 主链路车辆监管消息 UP_CTRL_MSG + /// + ///车辆单向监听应答 + /// + [Description("车辆单向监听应答")] + UP_CTRL_MSG_MONITOR_VEHICLE_ACK = 0x1501, + /// + ///车辆拍照应答 + /// + [Description("车辆拍照应答")] + UP_CTRL_MSG_TAKE_PHOTO_ACK = 0x1502, + /// + ///下发车辆报文应答 + /// + [Description("下发车辆报文应答")] + UP_CTRL_MSG_TEXT_INFO_ACK = 0x1503, + /// + ///上报车辆行驶记录应答 + /// + [Description("上报车辆行驶记录应答")] + UP_CTRL_MSG_TAKE_TRAVEL_ACK = 0x1504, + /// + ///车辆应急接入监管平台应答消息 + /// + [Description("车辆应急接入监管平台应答消息")] + UP_CTRL_MSG_EMERGENCY_MONITORING_ACK = 0x1505, + #endregion + + #region 从链路车辆监管消息 DOWN_CTRL_MSG + /// + ///车辆单向监听请求 + /// + [Description("车辆单向监听请求")] + DOWN_CTRL_MSG_MONITOR_VEHICLE_REQ = 0x9501, + /// + ///车辆拍照请求 + /// + [Description("车辆拍照请求")] + DOWN_CTRL_MSG_TAKE_PHOTO_REQ = 0x9502, + /// + ///下发车辆报文请求 + /// + [Description("下发车辆报文请求")] + DOWN_CTRL_MSG_TEXT_INFO = 0x9503, + /// + ///上报车辆行驶记录请求 + /// + [Description("上报车辆行驶记录请求")] + DOWN_CTRL_MSG_TAKE_TRAVEL_REQ = 0x9504, + /// + ///车辆应急接入监管平台请求消息 + /// + [Description("车辆应急接入监管平台请求消息")] + DOWN_CTRL_MSG_EMERGENCY_MONITORING_REQ = 0x9505, + #endregion + + #region 主链路静态信息交换消息 UP_BASE_MSG + /// + ///补报车辆静态信息应答 + /// + [Description("补报车辆静态信息应答")] + UP_BASE_MSG_VEHICLE_ADDED_ACK = 0x1601, + #endregion + + #region 从链路静态信息交换消息 DOWN_BASE_MSG + /// + ///补报车辆静态信息请求 + /// + [Description("补报车辆静态信息请求")] + DOWN_BASE_MSG_VEHICLE_ADDED = 0x9601, + #endregion + } +} diff --git a/src/JT809.Protocol/JT809Exceptions/JT809Exception.cs b/src/JT809.Protocol/JT809Exceptions/JT809Exception.cs new file mode 100644 index 0000000..4001415 --- /dev/null +++ b/src/JT809.Protocol/JT809Exceptions/JT809Exception.cs @@ -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;} + } +} diff --git a/src/JT809.Protocol/JT809Extensions/JT809BinaryExtensions.cs b/src/JT809.Protocol/JT809Extensions/JT809BinaryExtensions.cs new file mode 100644 index 0000000..c7f5eb2 --- /dev/null +++ b/src/JT809.Protocol/JT809Extensions/JT809BinaryExtensions.cs @@ -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 + { + /// + /// 日期限制于2000年 + /// + 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 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 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 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 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 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 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 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 read, ref int offset) + { + ushort value = (ushort)((read[offset] << 8) | (read[offset + 1])); + offset = offset + 2; + return value; + } + + public static byte ReadByteLittle(ReadOnlySpan read, ref int offset) + { + byte value = read[offset]; + offset = offset + 1; + return value; + } + + public static byte[] ReadBytesLittle(ReadOnlySpan read, ref int offset,int len) + { + ReadOnlySpan 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 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 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 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 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 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 memoryOwner, int offset, byte data) + { + memoryOwner.Memory.Span[offset] = data; + return 1; + } + + public static int WriteBytesLittle(IMemoryOwner 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 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 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 memoryOwner, int offset, string data, int digit, int len) + { + ReadOnlySpan 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 ToBytes(this string data, Encoding coding) + { + return coding.GetBytes(data); + } + + public static IEnumerable ToBytes(this string data) + { + return ToBytes(data, encoding); + } + + public static IEnumerable ToBytes(this int data, int len) + { + List bytes = new List(); + int n = 1; + for (int i = 0; i < len; i++) + { + bytes.Add((byte)(data >> 8 * (len - n))); + n++; + } + return bytes; + } + + /// + /// 从数据头到校验码前的 CRC 1 G-CCITT 的校验值,遵循人端排序方式的规定。 + /// + /// + /// + /// + /// + /// + public static ushort ToCRC16_CCITT(this Span 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; + } + + /// + /// 从数据头到校验码前的 CRC 1 G-CCITT 的校验值,遵循人端排序方式的规定。 + /// + /// + /// + /// + /// + /// + public static ushort ToCRC16_CCITT(this ReadOnlySpan 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); + } + + /// + /// 经纬度 + /// + /// + /// + public static double ToLatLng(this int latlng) + { + return Math.Round(latlng / Math.Pow(10, 6), 6); + } + + public static void CopyTo(Span source, Span destination, int offset) + { + for (int i = 0; i < source.Length; i++) + { + destination[offset + i] = source[i]; + } + } + + /// + /// 字节数组转16进制字符串 + /// + /// + /// 默认 " " + /// + public static string ToHexString(this byte[] bytes, string separator = " ") + { + return string.Join(separator, bytes.Select(s => s.ToString("X2"))); + } + + /// + /// 16进制字符串转16进制数组 + /// + /// + /// + /// + public static byte[] ToHexBytes(this string hexString, string separator = " ") + { + return hexString.Split(new string[] { separator }, StringSplitOptions.RemoveEmptyEntries).Select(s => Convert.ToByte(s, 16)).ToArray(); + } + + /// + /// 16进制字符串转16进制数组 + /// + /// + /// + 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 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 bytes = new List(); + //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(); + } + } +} diff --git a/src/JT809.Protocol/JT809Extensions/JT809EnumExtensions.cs b/src/JT809.Protocol/JT809Extensions/JT809EnumExtensions.cs new file mode 100644 index 0000000..6030d16 --- /dev/null +++ b/src/JT809.Protocol/JT809Extensions/JT809EnumExtensions.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; + +namespace JT809.Protocol.JT809Extensions +{ + /// + /// 枚举扩展 + /// + public static class JT809EnumExtensions + { + /// + /// 转为整型 + /// + /// + /// + /// + public static int ToValue(this T t) where T : struct + { + return Convert.ToInt32(t); + } + + /// + /// 转为Byte + /// + /// + /// + /// + public static byte ToByteValue(this T t) where T : struct + { + return Convert.ToByte(t); + } + + /// + /// 转为整型 + /// + /// + /// + /// + public static string ToValueString(this T t) where T : struct + { + return Convert.ToInt32(t).ToString(); + } + + /// + /// 字符转枚举 + /// + /// + /// + /// + public static T ToEnum(this string value) where T : struct + { + return (T)Enum.Parse(typeof(T), value); + } + + /// + /// 获取枚举字符串 + /// + /// + public static string GetName(this Enum valueEnum) + { + return valueEnum.ToString(); + } + + /// + /// 获取DescriptionAttribute特性枚举值的描述 + /// + /// + /// + public static string GetDescription(this Enum value) + { + var attribute = value.GetAttribute(); + return attribute == null ? value.ToString() : attribute.Description; + } + + /// + /// 验证是否是枚举类型 + /// + /// + /// + /// + public static bool IsEnumValid(this int enumValue) + { + return Enum.IsDefined(typeof(TEnum), enumValue); + } + + /// + /// 获取DescriptionAttribute特性枚举及描述 + /// + /// + /// + public static Dictionary GetDescriptionAttributeDictionary(this Enum value) + { + Dictionary dictionary = new Dictionary(); + 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; + } + + /// + /// 获取DisplayNameAttribute特性枚举值的描述 + /// + /// 枚举值 + /// + public static string GetDisplayName(this Enum value) + { + var attribute = value.GetAttribute(); + return attribute == null ? value.ToString() : attribute.DisplayName; + } + + /// + /// 获取DisplayNameAttribute特性枚举及描述 + /// + /// + /// + public static Dictionary GetDisplayNameAttributeDictionary(this Enum value) + { + Dictionary dictionary = new Dictionary(); + 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; + } + + /// + /// 获取枚举对应特性 + /// + /// + /// + /// + public static T GetAttribute(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; + } + } + } +} diff --git a/src/JT809.Protocol/JT809Extensions/JT809FormatterExtensions.cs b/src/JT809.Protocol/JT809Extensions/JT809FormatterExtensions.cs new file mode 100644 index 0000000..baf2e40 --- /dev/null +++ b/src/JT809.Protocol/JT809Extensions/JT809FormatterExtensions.cs @@ -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 GetFormatter() + { + IJT809Formatter formatter; + var attr = typeof(T).GetTypeInfo().GetCustomAttribute(); + if (attr == null) + { + throw new JT809Exception(JT809ErrorCode.GetFormatterError, $"该类{typeof(T).FullName}没有标记JT809FormatterAttribute"); + } + if (attr.Arguments == null) + { + formatter = (IJT809Formatter)Activator.CreateInstance(attr.FormatterType); + } + else + { + formatter = (IJT809Formatter)Activator.CreateInstance(attr.FormatterType, attr.Arguments); + } + return formatter; + } + + public static object GetFormatter(Type formatterType) + { + object formatter; + var attr = formatterType.GetTypeInfo().GetCustomAttribute(); + 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; + } + } +} diff --git a/src/JT809.Protocol/JT809Extensions/JT809FormatterResolverExtensions.cs b/src/JT809.Protocol/JT809Extensions/JT809FormatterResolverExtensions.cs new file mode 100644 index 0000000..96cf1c3 --- /dev/null +++ b/src/JT809.Protocol/JT809Extensions/JT809FormatterResolverExtensions.cs @@ -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 +{ + /// + /// + /// ref http://adamsitnik.com/Span/#span-must-not-be-a-generic-type-argument + /// ref http://adamsitnik.com/Span/ + /// ref MessagePack.Formatters.DynamicObjectTypeFallbackFormatter + /// + public static class JT809FormatterResolverExtensions + { + delegate int JT809SerializeMethod(object dynamicFormatter, IMemoryOwner memoryOwner, int offset, object value); + + delegate dynamic JT809DeserializeMethod(object dynamicFormatter, ReadOnlySpan bytes, out int readSize); + + static readonly ConcurrentDictionary jT809Serializers = new ConcurrentDictionary(); + + static readonly ConcurrentDictionary jT809Deserializes = new ConcurrentDictionary(); + + //T Deserialize(ReadOnlySpan bytes, out int readSize); + //int Serialize(IMemoryOwner memoryOwner, int offset, T value); + + public static int JT809DynamicSerialize(object objFormatter, IMemoryOwner 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), "memoryOwner"); + var param2 = Expression.Parameter(typeof(int), "offset"); + var param3 = Expression.Parameter(typeof(object), "value"); + var serializeMethodInfo = formatterType.GetRuntimeMethod("Serialize", new[] { typeof(IMemoryOwner), 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(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 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), "bytes"); + ParameterExpression param2 = Expression.Parameter(typeof(int).MakeByRefType(), "readSize"); + var deserializeMethodInfo = type.GetRuntimeMethod("Deserialize", new[] { typeof(ReadOnlySpan), typeof(int).MakeByRefType() }); + var body = Expression.Call( + Expression.Convert(param0, type), + deserializeMethodInfo, + param1, + param2 + ); + var lambda = Expression.Lambda(body, param0, param1, param2).Compile(); + formatterAndDelegate = (objFormatter, lambda); + } + jT809Deserializes.TryAdd(t, formatterAndDelegate); + } + return formatterAndDelegate.DeserializeMethod(formatterAndDelegate.Value, bytes, out readSize); + } + } +} diff --git a/src/JT809.Protocol/JT809Formatters/IJT809FormatterOfT.cs b/src/JT809.Protocol/JT809Formatters/IJT809FormatterOfT.cs new file mode 100644 index 0000000..11c45fd --- /dev/null +++ b/src/JT809.Protocol/JT809Formatters/IJT809FormatterOfT.cs @@ -0,0 +1,12 @@ +using System; +using System.Buffers; + +namespace JT809.Protocol.JT809Formatters +{ + public interface IJT809Formatter + { + T Deserialize(ReadOnlySpan bytes, out int readSize); + + int Serialize(IMemoryOwner memoryOwner, int offset, T value); + } +} diff --git a/src/JT809.Protocol/JT809Formatters/JT809HeaderFormatter.cs b/src/JT809.Protocol/JT809Formatters/JT809HeaderFormatter.cs new file mode 100644 index 0000000..71088fd --- /dev/null +++ b/src/JT809.Protocol/JT809Formatters/JT809HeaderFormatter.cs @@ -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 + { + public JT809Header Deserialize(ReadOnlySpan 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 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; + } + } +} diff --git a/src/JT809.Protocol/JT809Formatters/JT809MessageBodyFormatters/JT809_0x1001Formatter.cs b/src/JT809.Protocol/JT809Formatters/JT809MessageBodyFormatters/JT809_0x1001Formatter.cs new file mode 100644 index 0000000..3663856 --- /dev/null +++ b/src/JT809.Protocol/JT809Formatters/JT809MessageBodyFormatters/JT809_0x1001Formatter.cs @@ -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 + { + public JT809_0x1001 Deserialize(ReadOnlySpan 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 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; + } + } +} diff --git a/src/JT809.Protocol/JT809Formatters/JT809PackageFormatter.cs b/src/JT809.Protocol/JT809Formatters/JT809PackageFormatter.cs new file mode 100644 index 0000000..5961670 --- /dev/null +++ b/src/JT809.Protocol/JT809Formatters/JT809PackageFormatter.cs @@ -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 + { + public JT809Package Deserialize(ReadOnlySpan bytes, out int readSize) + { + int offset = 0; + JT809Package jT809Package = new JT809Package(); + // 转义还原——>验证校验码——>解析消息 + // 1. 解码(转义还原) + ReadOnlySpan 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().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(); + 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 memoryOwner, int offset, JT809Package value) + { + // 1. 先序列化数据体,根据数据体的长度赋值给头部,在序列化头部。 + int messageBodyOffset = 0; + JT809BodiesTypeAttribute jT809BodiesTypeAttribute = value.Header.MsgID.GetAttribute(); + 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().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 JT809DeEscape(ReadOnlySpan buffer) + { + List dataList = new List(); + 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 buf) + { + List dataList = new List(); + 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(); + } + } +} diff --git a/src/JT809.Protocol/JT809Header.cs b/src/JT809.Protocol/JT809Header.cs new file mode 100644 index 0000000..76e4c7c --- /dev/null +++ b/src/JT809.Protocol/JT809Header.cs @@ -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 + { + /// + /// 固定为22个字节长度 + /// MSG LENGTH + MSG_SN + MSG_ID + MSG_GNSSCENTERID + VERSION_FLAG + ENCRYPT_FLAG + ENCRYPT_KEY + /// 4 + 4 + 2 + 4 + 3 + 1 + 4 = 22 + /// + public const int FixedByteLength = 22; + /// + /// 数据长度(包括头标识、数据头、数据体和尾标识) + /// 头标识 + 数据头 + 数据体 + 尾标识 + /// 1 + 22 + n + 1 + /// + public uint MsgLength { get; set; } + /// + /// 报文序列号 a + /// + public uint MsgSN { get; set; } + /// + /// 业务数据类型 + /// + public JT809BusinessType MsgID { get; set; } + /// + /// 下级平台接入码,上级平台给下级平台分配唯一标识码。 + /// + public uint MsgGNSSCENTERID { get; set; } + /// + /// 协议版本好标识,上下级平台之间采用的标准协议版 + /// 编号;长度为 3 个字节来表示,0x01 0x02 0x0F 标识 + /// 的版本号是 v1.2.15,以此类推。 + /// + public JT809Header_Version Version { get; set; } = new JT809Header_Version(); + /// + /// 报文加密标识位 b: 0 表示报文不加密,1 表示报文加密。 + /// + public JT809Header_Encrypt EncryptFlag { get; set; } = JT809Header_Encrypt.None; + /// + /// 数据加密的密匙,长度为 4 个字节 + /// + public uint EncryptKey { get; set; } + } +} diff --git a/src/JT809.Protocol/Enums/EncryptOpitions.cs b/src/JT809.Protocol/JT809Header_Encrypt.cs similarity index 71% rename from src/JT809.Protocol/Enums/EncryptOpitions.cs rename to src/JT809.Protocol/JT809Header_Encrypt.cs index fa8dc90..49ae3d1 100644 --- a/src/JT809.Protocol/Enums/EncryptOpitions.cs +++ b/src/JT809.Protocol/JT809Header_Encrypt.cs @@ -1,9 +1,9 @@ -namespace JT809.Protocol.Enums +namespace JT809.Protocol { /// /// 报文加密标识位 b: 0 表示报文不加密,1 表示报文加密。 /// - public enum EncryptOpitions : byte + public enum JT809Header_Encrypt:byte { None = 0X00, Common = 0X01, diff --git a/src/JT809.Protocol/JT809Header_Version.cs b/src/JT809.Protocol/JT809Header_Version.cs new file mode 100644 index 0000000..f7958f5 --- /dev/null +++ b/src/JT809.Protocol/JT809Header_Version.cs @@ -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; } + } + + /// + /// 默认1.0.0版本 + /// + 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}"; + } + } +} diff --git a/src/JT809.Protocol/JT809MessageBody/JT809_0x1001.cs b/src/JT809.Protocol/JT809MessageBody/JT809_0x1001.cs new file mode 100644 index 0000000..e629d6c --- /dev/null +++ b/src/JT809.Protocol/JT809MessageBody/JT809_0x1001.cs @@ -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 +{ + /// + /// 主链路登录请求消息 + /// + [JT809Formatter(typeof(JT809_0x1001Formatter))] + public class JT809_0x1001: JT809Bodies + { + /// + /// 用户名 + /// + public uint UserId { get; set; } + /// + /// 密码 + /// 8位 + /// + public string Password { get; set; } + /// + /// 下级平台提供对应的从链路服务端 IP 地址 + /// 32位 + /// + public string DownLinkIP { get; set; } + /// + /// 下级平台提供对应的从链路服务器端口号 + /// + public ushort DownLinkPort { get; set; } + } +} diff --git a/src/JT809.Protocol/JT809Package.cs b/src/JT809.Protocol/JT809Package.cs new file mode 100644 index 0000000..ae98417 --- /dev/null +++ b/src/JT809.Protocol/JT809Package.cs @@ -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; + + /// + /// 固定为26个字节长度 + /// Head flag + Message Header + CRC Code + End Flag + /// 1 + 22 + 2 + 1 = 26 + /// + 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; + } +} diff --git a/src/JT809.Protocol/JT809Serializer.cs b/src/JT809.Protocol/JT809Serializer.cs new file mode 100644 index 0000000..fe3d632 --- /dev/null +++ b/src/JT809.Protocol/JT809Serializer.cs @@ -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(); + var pool = MemoryPool.Shared; + IMemoryOwner 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 s_shared = new ArrayMemoryPool(); + // 单例内存池 不需要手动释放资源 + buffer.Dispose(); + } + } + + public static JT809Package Deserialize(ReadOnlySpan bytes) + { + var formatter = JT809FormatterExtensions.GetFormatter(); + return formatter.Deserialize(bytes, out int readSize); + } + + public static byte[] Serialize(T obj) + { + var formatter = JT809FormatterExtensions.GetFormatter(); + var pool = MemoryPool.Shared; + IMemoryOwner 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 s_shared = new ArrayMemoryPool(); + // 单例内存池 不需要手动释放资源 + buffer.Dispose(); + } + } + + public static T Deserialize(ReadOnlySpan bytes) + { + var formatter = JT809FormatterExtensions.GetFormatter(); + int readSize; + return formatter.Deserialize(bytes, out readSize); + } + } +}