From d051a9c5b264589241c26074f586bab4e3c83d6b Mon Sep 17 00:00:00 2001 From: SmallChi <564952747@qq.com> Date: Fri, 25 Jan 2019 20:47:00 +0800 Subject: [PATCH] =?UTF-8?q?1.=E5=A2=9E=E5=8A=A0=E5=88=9B=E5=BB=BA=E5=8C=85?= =?UTF-8?q?=E7=9A=84=E6=89=A9=E5=B1=95=E6=96=B9=E6=B3=95=202.=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=8A=A0=E5=AF=86=E6=8E=A5=E5=8F=A3=E5=8F=8A=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E6=B5=8B=E8=AF=95=E5=AE=9E=E7=8E=B0=203.=E5=A2=9E?= =?UTF-8?q?=E5=8A=A00x02=E4=BD=8D=E7=BD=AE=E4=BF=A1=E6=81=AF=E4=B8=8A?= =?UTF-8?q?=E6=8A=A5=EF=BC=88=E5=BE=85=E5=AE=8C=E5=96=84=EF=BC=89=204.?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=B4=E9=83=A8=E8=A7=A3=E5=8C=85=205.?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0CI=E9=85=8D=E7=BD=AE=E5=8F=8A=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .travis.yml | 15 ++++ README.md | 47 ++++++++++++- .../Extensions/JTNEPackageExtensionsTest.cs | 33 +++++++++ .../JTNE.Protocol.Test.csproj | 2 + .../JTNEHeaderPackageTest.cs | 55 +++++++++++++++ .../Package/JTNE_0x01_PackageTest.cs | 66 +++++++++++++++++ .../Extensions/JTNEPackageExtensions.cs | 8 --- .../Formatters/JTNEPackageFormatter.cs | 69 ++++++++++++++---- .../JTNE_0x02_0x01_Formatter.cs | 52 ++++++++++++++ .../JTNE_0x02_Formatter.cs | 27 +++++++ src/JTNE.Protocol/Interfaces/IJTNEEncrypt.cs | 12 ++++ .../Internal/Default_AES128EncryptImpl.cs | 47 +++++++++++++ src/JTNE.Protocol/JTNEGlobalConfigs.cs | 33 ++++++++- src/JTNE.Protocol/JTNEHeaderPackage.cs | 2 + src/JTNE.Protocol/MessageBody/JTNE_0x02.cs | 24 +++++++ .../MessageBody/JTNE_0x02_0x01.cs | 70 +++++++++++++++++++ .../MessageBody/JTNE_0x02_Body.cs | 32 +++++++++ src/JTNewEnergy.sln | 18 ++--- 18 files changed, 576 insertions(+), 36 deletions(-) create mode 100644 .travis.yml create mode 100644 src/JTNE.Protocol.Test/Extensions/JTNEPackageExtensionsTest.cs create mode 100644 src/JTNE.Protocol.Test/JTNEHeaderPackageTest.cs create mode 100644 src/JTNE.Protocol/Formatters/MessageBodyFormatters/JTNE_0x02_0x01_Formatter.cs create mode 100644 src/JTNE.Protocol/Formatters/MessageBodyFormatters/JTNE_0x02_Formatter.cs create mode 100644 src/JTNE.Protocol/Interfaces/IJTNEEncrypt.cs create mode 100644 src/JTNE.Protocol/Internal/Default_AES128EncryptImpl.cs create mode 100644 src/JTNE.Protocol/MessageBody/JTNE_0x02.cs create mode 100644 src/JTNE.Protocol/MessageBody/JTNE_0x02_0x01.cs create mode 100644 src/JTNE.Protocol/MessageBody/JTNE_0x02_Body.cs diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f14d3db --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: csharp +solution: JTNewEnergy.sln +dotnet: 2.2.101 +os: linux +mono: none +dist: trusty2 +script: + - dotnet restore src/JTNewEnergy.sln + - dotnet build src/JTNE.Protocol.Test/JTNE.Protocol.Test.csproj + - dotnet test src/JTNE.Protocol.Test/JTNE.Protocol.Test.csproj +after_success: + - echo successful build! +branches: + only: + - master diff --git a/README.md b/README.md index 882fbee..8525f17 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,45 @@ -# GBNewEnergy -GBNewEnergy +# JTNewEnergy协议 + +[![MIT Licence](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/SmallChi/JTNewEnergy/blob/master/LICENSE)[![Build Status](https://travis-ci.org/SmallChi/JTNewEnergy.svg?branch=master)](https://travis-ci.org/SmallChi/JTNewEnergy) + + +## 前提条件 + +1. 掌握进制转换:二进制转十六进制; +2. 掌握BCD编码、Hex编码; +3. 掌握各种位移、异或; +4. 掌握常用反射; +5. 掌握快速ctrl+c、ctrl+v; +6. 掌握以上装逼技能,就可以开始搬砖了。 + +## JTNewEnergy数据结构解析 + +### 数据包[JTNEPackage] + +| 起始标识1|起始标识2 | 命令标识 | 应答标志 | 车辆识别码 | 数据加密方式 |数据单元长度| 数据体|校验码| +| :---------:| :---------: | :---------: | :-------: | :-------: |:-------: |:-------: |:-------: |:-------:| +| BeginFlag1 | BeginFlag2 | MsgId | AskId | VIN |EncryptMethod|DataUnitLength|JTNEBodies|BCCCode| +| 0x23(#) | 0x23(#) | - | - | - |- |- |- |- | + +#### 消息体属性[JTNEBodies] + +> 根据对应消息ID:MsgId + +## JTNewEnergy终端通讯协议消息对照表 + +| 序号 | 消息ID | 完成情况 | 测试情况 | 消息体名称 | +| :---: | :-----------: | :------: | :------: | :----------------------------: | +| 1 | 0x01 | √ | √ | 车辆登入 | +| 2 | 0x02 | x | x | 实时信息上传 | +| 3 | 0x03 | x | x | 补传信息上传 | +| 4 | 0x04 | √ | √ | 车辆登出 | +| 5 | 0x05 | √ | √ | 平台登入 | +| 6 | 0x06 | √ | √ | 平台登出 | +| 7 | 0x07 | √ | √ | 心跳 | +| 8 | 0x08 | √ | √ | 终端校时 | +| 9 | 0x09~0x7F | - | - | 上行数据系统预留 | +| 10 | 0x80 | x | x | 查询命令 | +| 11 | 0x81 | x | x | 设置命令 | +| 12 | 0x82 | x | x | 车载终端控制命令 | +| 13 | 0x83~0xBF | - | - | 下行数据系统预留 | +| 14 | 0xC0~0xFE | - | - | 平台交换自定义数据 | \ No newline at end of file diff --git a/src/JTNE.Protocol.Test/Extensions/JTNEPackageExtensionsTest.cs b/src/JTNE.Protocol.Test/Extensions/JTNEPackageExtensionsTest.cs new file mode 100644 index 0000000..378fae3 --- /dev/null +++ b/src/JTNE.Protocol.Test/Extensions/JTNEPackageExtensionsTest.cs @@ -0,0 +1,33 @@ +using JTNE.Protocol.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; +using JTNE.Protocol.Extensions; +using JTNE.Protocol.MessageBody; + +namespace JTNE.Protocol.Test.Extensions +{ + public class JTNEPackageExtensionsTest + { + [Fact] + public void Test1() + { + JTNEPackage jTNEPackage= JTNEMsgId.login.Create("123456789", JTNEAskId.CMD, new JTNE_0x01 + { + PDATime = DateTime.Parse("2019-01-22 23:55:56"), + LoginNum = 1, + BatteryLength = 0x04, + SIM = "12345678998765432100", + BatteryNos = new List() + { + "1234", + "4567", + "9870" + } + }); + var hex = JTNESerializer.Serialize(jTNEPackage).ToHexString(); + Assert.Equal("232301FE313233343536373839000000000000000001002A130116173738000131323334353637383939383736353433323130300304313233343435363739383730FD", hex); + } + } +} diff --git a/src/JTNE.Protocol.Test/JTNE.Protocol.Test.csproj b/src/JTNE.Protocol.Test/JTNE.Protocol.Test.csproj index 880d991..3d41a8c 100644 --- a/src/JTNE.Protocol.Test/JTNE.Protocol.Test.csproj +++ b/src/JTNE.Protocol.Test/JTNE.Protocol.Test.csproj @@ -4,6 +4,8 @@ netcoreapp2.2 false + + 7.1 diff --git a/src/JTNE.Protocol.Test/JTNEHeaderPackageTest.cs b/src/JTNE.Protocol.Test/JTNEHeaderPackageTest.cs new file mode 100644 index 0000000..6c8a0e4 --- /dev/null +++ b/src/JTNE.Protocol.Test/JTNEHeaderPackageTest.cs @@ -0,0 +1,55 @@ +using JTNE.Protocol.Enums; +using JTNE.Protocol.Extensions; +using JTNE.Protocol.MessageBody; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; + +namespace JTNE.Protocol.Test +{ + public class JTNEHeaderPackageTest + { + [Fact] + public void Test1() + { + JTNEHeaderPackage jTNEHeaderPackage = new JTNEHeaderPackage(); + jTNEHeaderPackage.VIN = "123456789"; + jTNEHeaderPackage.AskId = JTNEAskId.CMD.ToByteValue(); + jTNEHeaderPackage.MsgId = JTNEMsgId.login.ToByteValue(); + JTNE_0x01 jTNE_0X01 = new JTNE_0x01(); + jTNE_0X01.PDATime = DateTime.Parse("2019-01-22 23:55:56"); + jTNE_0X01.LoginNum = 1; + jTNE_0X01.BatteryLength = 0x04; + jTNE_0X01.SIM = "12345678998765432100"; + jTNE_0X01.BatteryNos = new List() + { + "1234", + "4567", + "9870" + }; + jTNEHeaderPackage.Bodies = JTNESerializer.Serialize(jTNE_0X01); + var hex = JTNESerializer.Serialize(jTNEHeaderPackage).ToHexString(); + Assert.Equal("232301FE313233343536373839000000000000000001002A130116173738000131323334353637383939383736353433323130300304313233343435363739383730FD", hex); + } + + [Fact] + public void Test2() + { + var data= "232301FE313233343536373839000000000000000001002A130116173738000131323334353637383939383736353433323130300304313233343435363739383730FD".ToHexBytes(); + JTNEHeaderPackage jTNEHeaderPackage = JTNESerializer.Deserialize(data); + Assert.Equal(JTNEAskId.CMD.ToByteValue(), jTNEHeaderPackage.AskId); + Assert.Equal(JTNEMsgId.login.ToByteValue(), jTNEHeaderPackage.MsgId); + Assert.Equal("123456789", jTNEHeaderPackage.VIN); + JTNE_0x01 jTNE_0X01 = JTNESerializer.Deserialize(jTNEHeaderPackage.Bodies); + Assert.Equal(DateTime.Parse("2019-01-22 23:55:56"), jTNE_0X01.PDATime); + Assert.Equal(1, jTNE_0X01.LoginNum); + Assert.Equal(0x04, jTNE_0X01.BatteryLength); + Assert.Equal("12345678998765432100", jTNE_0X01.SIM); + Assert.Equal(3, jTNE_0X01.BatteryCount); + Assert.Equal("1234", jTNE_0X01.BatteryNos[0]); + Assert.Equal("4567", jTNE_0X01.BatteryNos[1]); + Assert.Equal("9870", jTNE_0X01.BatteryNos[2]); + } + } +} diff --git a/src/JTNE.Protocol.Test/Package/JTNE_0x01_PackageTest.cs b/src/JTNE.Protocol.Test/Package/JTNE_0x01_PackageTest.cs index 4e07b99..7741a0c 100644 --- a/src/JTNE.Protocol.Test/Package/JTNE_0x01_PackageTest.cs +++ b/src/JTNE.Protocol.Test/Package/JTNE_0x01_PackageTest.cs @@ -5,6 +5,7 @@ using System.Text; using Xunit; using JTNE.Protocol.Enums; using JTNE.Protocol.MessageBody; +using JTNE.Protocol.Internal; namespace JTNE.Protocol.Test.Package { @@ -52,5 +53,70 @@ namespace JTNE.Protocol.Test.Package Assert.Equal("4567", jTNE_0X01.BatteryNos[1]); Assert.Equal("9870", jTNE_0X01.BatteryNos[2]); } + + [Fact] + public void Test3() + { + JTNEGlobalConfigs.Instance.SetDataBodiesEncrypt((msgId)=> + { + switch (msgId) + { + case 0x03: + return new Default_AES128EncryptImpl(); + default: + return default; + } + }); + JTNEPackage jTNEPackage = new JTNEPackage(); + jTNEPackage.AskId = JTNEAskId.CMD.ToByteValue(); + jTNEPackage.MsgId = JTNEMsgId.login.ToByteValue(); + jTNEPackage.VIN = "123456789"; + jTNEPackage.EncryptMethod = JTNEEncryptMethod.AES128.ToByteValue(); + JTNE_0x01 jTNE_0X01 = new JTNE_0x01(); + jTNE_0X01.PDATime = DateTime.Parse("2019-01-22 23:55:56"); + jTNE_0X01.LoginNum = 1; + jTNE_0X01.BatteryLength = 0x04; + jTNE_0X01.SIM = "12345678998765432100"; + jTNE_0X01.BatteryNos = new List() + { + "1234", + "4567", + "9870" + }; + jTNEPackage.Bodies = jTNE_0X01; + var hex = JTNESerializer.Serialize(jTNEPackage).ToHexString(); + Assert.Equal("232301FE31323334353637383900000000000000000300307C9AAF67FB9408A75FAFC1C87F1E2AECD79DDAB8219016A5DD0911283922805EF450045EA3611C0D5CFBFD8F2581CEED30", hex); + } + + [Fact] + public void Test4() + { + JTNEGlobalConfigs.Instance.SetDataBodiesEncrypt((msgId) => + { + switch (msgId) + { + case 0x03: + return new Default_AES128EncryptImpl(); + default: + return default; + } + }); + var data = "232301FE31323334353637383900000000000000000300307C9AAF67FB9408A75FAFC1C87F1E2AECD79DDAB8219016A5DD0911283922805EF450045EA3611C0D5CFBFD8F2581CEED30".ToHexBytes(); + JTNEPackage jTNEPackage = JTNESerializer.Deserialize(data); + Assert.Equal(JTNEAskId.CMD.ToByteValue(), jTNEPackage.AskId); + Assert.Equal(JTNEMsgId.login.ToByteValue(), jTNEPackage.MsgId); + Assert.Equal("123456789", jTNEPackage.VIN); + Assert.Equal(JTNEEncryptMethod.AES128.ToByteValue(), jTNEPackage.EncryptMethod); + + JTNE_0x01 jTNE_0X01 = jTNEPackage.Bodies as JTNE_0x01; + Assert.Equal(DateTime.Parse("2019-01-22 23:55:56"), jTNE_0X01.PDATime); + Assert.Equal(1, jTNE_0X01.LoginNum); + Assert.Equal(0x04, jTNE_0X01.BatteryLength); + Assert.Equal("12345678998765432100", jTNE_0X01.SIM); + Assert.Equal(3, jTNE_0X01.BatteryCount); + Assert.Equal("1234", jTNE_0X01.BatteryNos[0]); + Assert.Equal("4567", jTNE_0X01.BatteryNos[1]); + Assert.Equal("9870", jTNE_0X01.BatteryNos[2]); + } } } diff --git a/src/JTNE.Protocol/Extensions/JTNEPackageExtensions.cs b/src/JTNE.Protocol/Extensions/JTNEPackageExtensions.cs index 3a52418..080f3d4 100644 --- a/src/JTNE.Protocol/Extensions/JTNEPackageExtensions.cs +++ b/src/JTNE.Protocol/Extensions/JTNEPackageExtensions.cs @@ -27,8 +27,6 @@ namespace JTNE.Protocol.Extensions jTNEPackage.MsgId = msgId.ToByteValue(); jTNEPackage.Bodies = bodies; jTNEPackage.VIN = vin; - // todo:加密处理 - return jTNEPackage; } @@ -45,8 +43,6 @@ namespace JTNE.Protocol.Extensions jTNEPackage.AskId = askId.ToByteValue(); jTNEPackage.MsgId = msgId.ToByteValue(); jTNEPackage.VIN = vin; - // todo:加密处理 - return jTNEPackage; } @@ -67,8 +63,6 @@ namespace JTNE.Protocol.Extensions jTNEPackage.MsgId = msgId; jTNEPackage.Bodies = bodies; jTNEPackage.VIN = vin; - // todo:加密处理 - return jTNEPackage; } @@ -85,8 +79,6 @@ namespace JTNE.Protocol.Extensions jTNEPackage.AskId = askId.ToByteValue(); jTNEPackage.MsgId = msgId; jTNEPackage.VIN = vin; - // todo:加密处理 - return jTNEPackage; } } diff --git a/src/JTNE.Protocol/Formatters/JTNEPackageFormatter.cs b/src/JTNE.Protocol/Formatters/JTNEPackageFormatter.cs index a46eb69..5eee4cf 100644 --- a/src/JTNE.Protocol/Formatters/JTNEPackageFormatter.cs +++ b/src/JTNE.Protocol/Formatters/JTNEPackageFormatter.cs @@ -41,7 +41,7 @@ namespace JTNE.Protocol.Formatters jTNEPackage.DataUnitLength = JTNEBinaryExtensions.ReadUInt16Little(bytes, ref offset); // 8.数据体 // 8.1.根据数据加密方式进行解码 - // todo: 8.2.解析出对应数据体 + // 8.2.解析出对应数据体 if (jTNEPackage.DataUnitLength > 0) { Type jTNEBodiesImplType = JTNEMsgIdFactory.GetBodiesImplTypeByMsgId(jTNEPackage.MsgId); @@ -50,10 +50,25 @@ namespace JTNE.Protocol.Formatters int bodyReadSize = 0; try { - jTNEPackage.Bodies = JTNEFormatterResolverExtensions.JTNEDynamicDeserialize( - JTNEFormatterExtensions.GetFormatter(jTNEBodiesImplType), - bytes.Slice(offset, jTNEPackage.DataUnitLength), - out bodyReadSize); + if (jTNEPackage.EncryptMethod == 0x01) + { + jTNEPackage.Bodies = JTNEFormatterResolverExtensions.JTNEDynamicDeserialize( + JTNEFormatterExtensions.GetFormatter(jTNEBodiesImplType), + bytes.Slice(offset, jTNEPackage.DataUnitLength), + out bodyReadSize); + } + else + { + if (JTNEGlobalConfigs.Instance.DataBodiesEncrypt != null) + { + var data = JTNEGlobalConfigs.Instance.DataBodiesEncrypt(jTNEPackage.EncryptMethod) + .Decrypt(bytes.Slice(offset, jTNEPackage.DataUnitLength).ToArray()); + jTNEPackage.Bodies = JTNEFormatterResolverExtensions.JTNEDynamicDeserialize( + JTNEFormatterExtensions.GetFormatter(jTNEBodiesImplType), + data, + out bodyReadSize); + } + } } catch (Exception ex) { @@ -82,9 +97,8 @@ namespace JTNE.Protocol.Formatters offset += JTNEBinaryExtensions.WriteStringPadRightLittle(bytes, offset, value.VIN, 17); // 6.数据加密方式 offset += JTNEBinaryExtensions.WriteByteLittle(bytes, offset, value.EncryptMethod); - // 7.记录存储数据长度的当前偏移量 + // 7.记录当前偏移量 int tmpOffset = offset; - offset += FixedDataBodyLength; // 8.数据体 Type jTNEBodiesImplType = JTNEMsgIdFactory.GetBodiesImplTypeByMsgId(value.MsgId); int messageBodyOffset = 0; @@ -92,17 +106,46 @@ namespace JTNE.Protocol.Formatters { if (value.Bodies != null) { - // 8.1 处理数据体 - // todo: 8.2.判断是否有加密 + // 8.1.处理数据体 + // 8.2.判断是否有加密 messageBodyOffset = JTNEFormatterResolverExtensions.JTNEDynamicSerialize( JTNEFormatterExtensions.GetFormatter(jTNEBodiesImplType), ref bytes, - offset, + offset + FixedDataBodyLength, value.Bodies); - // 9.通过tmpOffset反写数据单元长度 - JTNEBinaryExtensions.WriteUInt16Little(bytes, tmpOffset, (ushort)(messageBodyOffset - offset)); - offset = messageBodyOffset; + if (value.EncryptMethod == 0x01) + { + // 9.通过tmpOffset反写数据单元长度 + JTNEBinaryExtensions.WriteUInt16Little(bytes, tmpOffset, (ushort)(messageBodyOffset - offset- FixedDataBodyLength)); + offset = messageBodyOffset; + } + else + { + if (JTNEGlobalConfigs.Instance.DataBodiesEncrypt != null) + { + // 8.1.先进行分割数据体 + var bodiesData = bytes.AsSpan(tmpOffset+ FixedDataBodyLength, messageBodyOffset - offset - FixedDataBodyLength).ToArray(); + // 8.2.将数据体进行加密 + var data = JTNEGlobalConfigs.Instance.DataBodiesEncrypt(value.EncryptMethod) + .Encrypt(bodiesData); + // 9.通过tmpOffset反写加密后数据单元长度 + JTNEBinaryExtensions.WriteUInt16Little(bytes, tmpOffset, (ushort)data.Length); + // 8.3.写入加密后的数据体 + offset += FixedDataBodyLength; + offset += JTNEBinaryExtensions.WriteBytesLittle(bytes, offset, data); + } + } } + else + { + // 9.数据单元长度 + offset += JTNEBinaryExtensions.WriteUInt16Little(bytes, offset, 0); + } + } + else + { + // 9.数据单元长度 + offset += JTNEBinaryExtensions.WriteUInt16Little(bytes, offset, 0); } // 10.校验码 var bccCode = bytes.ToXor(2, offset); diff --git a/src/JTNE.Protocol/Formatters/MessageBodyFormatters/JTNE_0x02_0x01_Formatter.cs b/src/JTNE.Protocol/Formatters/MessageBodyFormatters/JTNE_0x02_0x01_Formatter.cs new file mode 100644 index 0000000..1ec01ce --- /dev/null +++ b/src/JTNE.Protocol/Formatters/MessageBodyFormatters/JTNE_0x02_0x01_Formatter.cs @@ -0,0 +1,52 @@ +using JTNE.Protocol.Extensions; +using JTNE.Protocol.MessageBody; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JTNE.Protocol.Formatters.MessageBodyFormatters +{ + public class JTNE_0x02_0x01_Formatter : IJTNEFormatter + { + public JTNE_0x02_0x01 Deserialize(ReadOnlySpan bytes, out int readSize) + { + int offset = 0; + JTNE_0x02_0x01 jTNE_0X02_0X01 = new JTNE_0x02_0x01(); + jTNE_0X02_0X01.TypeCode = JTNEBinaryExtensions.ReadByteLittle(bytes, ref offset); + jTNE_0X02_0X01.CarStatus= JTNEBinaryExtensions.ReadByteLittle(bytes, ref offset); + jTNE_0X02_0X01.ChargeStatus = JTNEBinaryExtensions.ReadByteLittle(bytes, ref offset); + jTNE_0X02_0X01.OperationMode = JTNEBinaryExtensions.ReadByteLittle(bytes, ref offset); + jTNE_0X02_0X01.Speed = JTNEBinaryExtensions.ReadUInt16Little(bytes, ref offset); + jTNE_0X02_0X01.TotalDis = JTNEBinaryExtensions.ReadUInt32Little(bytes, ref offset); + jTNE_0X02_0X01.TotalVoltage= JTNEBinaryExtensions.ReadUInt16Little(bytes, ref offset); + jTNE_0X02_0X01.TotalTemp = JTNEBinaryExtensions.ReadUInt16Little(bytes, ref offset); + jTNE_0X02_0X01.SOC = JTNEBinaryExtensions.ReadByteLittle(bytes, ref offset); + jTNE_0X02_0X01.DCStatus = JTNEBinaryExtensions.ReadByteLittle(bytes, ref offset); + jTNE_0X02_0X01.Stall = JTNEBinaryExtensions.ReadByteLittle(bytes, ref offset); + jTNE_0X02_0X01.Resistance = JTNEBinaryExtensions.ReadUInt16Little(bytes, ref offset); + jTNE_0X02_0X01.Accelerator = JTNEBinaryExtensions.ReadByteLittle(bytes, ref offset); + jTNE_0X02_0X01.Brakes = JTNEBinaryExtensions.ReadByteLittle(bytes, ref offset); + readSize = offset; + return jTNE_0X02_0X01; + } + + public int Serialize(ref byte[] bytes, int offset, JTNE_0x02_0x01 value) + { + offset += JTNEBinaryExtensions.WriteByteLittle(bytes, offset, value.TypeCode); + offset += JTNEBinaryExtensions.WriteByteLittle(bytes, offset, value.CarStatus); + offset += JTNEBinaryExtensions.WriteByteLittle(bytes, offset, value.ChargeStatus); + offset += JTNEBinaryExtensions.WriteByteLittle(bytes, offset, value.OperationMode); + offset += JTNEBinaryExtensions.WriteUInt16Little(bytes, offset, value.Speed); + offset += JTNEBinaryExtensions.WriteUInt32Little(bytes, offset, value.TotalDis); + offset += JTNEBinaryExtensions.WriteUInt16Little(bytes, offset, value.TotalVoltage); + offset += JTNEBinaryExtensions.WriteUInt16Little(bytes, offset, value.TotalTemp); + offset += JTNEBinaryExtensions.WriteByteLittle(bytes, offset, value.SOC); + offset += JTNEBinaryExtensions.WriteByteLittle(bytes, offset, value.DCStatus); + offset += JTNEBinaryExtensions.WriteByteLittle(bytes, offset, value.Stall); + offset += JTNEBinaryExtensions.WriteUInt16Little(bytes, offset, value.Resistance); + offset += JTNEBinaryExtensions.WriteByteLittle(bytes, offset, value.Accelerator); + offset += JTNEBinaryExtensions.WriteByteLittle(bytes, offset, value.Brakes); + return offset; + } + } +} diff --git a/src/JTNE.Protocol/Formatters/MessageBodyFormatters/JTNE_0x02_Formatter.cs b/src/JTNE.Protocol/Formatters/MessageBodyFormatters/JTNE_0x02_Formatter.cs new file mode 100644 index 0000000..bf1108c --- /dev/null +++ b/src/JTNE.Protocol/Formatters/MessageBodyFormatters/JTNE_0x02_Formatter.cs @@ -0,0 +1,27 @@ +using JTNE.Protocol.Extensions; +using JTNE.Protocol.MessageBody; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JTNE.Protocol.Formatters.MessageBodyFormatters +{ + public class JTNE_0x02_Formatter : IJTNEFormatter + { + public JTNE_0x02 Deserialize(ReadOnlySpan bytes, out int readSize) + { + int offset = 0; + JTNE_0x02 jTNE_0X02 = new JTNE_0x02(); + + + readSize = offset; + return jTNE_0X02; + } + + public int Serialize(ref byte[] bytes, int offset, JTNE_0x02 value) + { + + return offset; + } + } +} diff --git a/src/JTNE.Protocol/Interfaces/IJTNEEncrypt.cs b/src/JTNE.Protocol/Interfaces/IJTNEEncrypt.cs new file mode 100644 index 0000000..a2e1489 --- /dev/null +++ b/src/JTNE.Protocol/Interfaces/IJTNEEncrypt.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JTNE.Protocol.Interfaces +{ + public interface IJTNEEncrypt + { + byte[] Encrypt(byte[] buffer); + byte[] Decrypt(byte[] buffer); + } +} diff --git a/src/JTNE.Protocol/Internal/Default_AES128EncryptImpl.cs b/src/JTNE.Protocol/Internal/Default_AES128EncryptImpl.cs new file mode 100644 index 0000000..75141b6 --- /dev/null +++ b/src/JTNE.Protocol/Internal/Default_AES128EncryptImpl.cs @@ -0,0 +1,47 @@ +using JTNE.Protocol.Interfaces; +using System.IO; +using System.Security.Cryptography; + +namespace JTNE.Protocol.Internal +{ + public class Default_AES128EncryptImpl : IJTNEEncrypt + { + public string PrivateKey => "test"; + + public byte[] SaltBytes => new byte[9] { 13, 34, 27, 67, 189, 255, 104, 219, 122 }; + + public byte[] Encrypt(byte[] buffer) + { + using (Aes encryptor = Aes.Create()) + { + Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(PrivateKey, SaltBytes); + encryptor.Key = pdb.GetBytes(32); + encryptor.IV = pdb.GetBytes(16); + using (MemoryStream ms = new MemoryStream()) + using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(),CryptoStreamMode.Write)) + { + cs.Write(buffer, 0, buffer.Length); + cs.Close(); + return ms.ToArray(); + } + } + } + + public byte[] Decrypt(byte[] buffer) + { + using (Aes encryptor = Aes.Create()) + { + Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(PrivateKey, SaltBytes); + encryptor.Key = pdb.GetBytes(32); + encryptor.IV = pdb.GetBytes(16); + using (MemoryStream ms = new MemoryStream()) + using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(),CryptoStreamMode.Write)) + { + cs.Write(buffer, 0, buffer.Length); + cs.Close(); + return ms.ToArray(); + } + } + } + } +} diff --git a/src/JTNE.Protocol/JTNEGlobalConfigs.cs b/src/JTNE.Protocol/JTNEGlobalConfigs.cs index f80ec19..0baf1e0 100644 --- a/src/JTNE.Protocol/JTNEGlobalConfigs.cs +++ b/src/JTNE.Protocol/JTNEGlobalConfigs.cs @@ -35,7 +35,6 @@ namespace JTNE.Protocol return instance.Value; } } - /// /// 设备流水号 /// @@ -51,6 +50,18 @@ namespace JTNE.Protocol /// public bool SkipCRCCode { get; private set; } /// + /// 消息数据体加密算法 + /// RSA=>IJTNEEncryptImpl + /// AES=>IJTNEEncryptImpl + /// + public Func DataBodiesEncrypt { get; private set; } + /// + /// 平台登入加密算法 + /// RSA=>IJTNEEncryptImpl + /// AES=>IJTNEEncryptImpl + /// + public Func PlatformLoginEncrypt { get; private set; } + /// /// 注册自定义消息 /// /// @@ -105,5 +116,25 @@ namespace JTNE.Protocol instance.Value.PlatformMsgSNDistributed = platformMsgSNDistributed; return instance.Value; } + /// + /// 设置消息数据体加密算法 + /// + /// + /// + public JTNEGlobalConfigs SetDataBodiesEncrypt(Func dataBodiesEncrypt) + { + instance.Value.DataBodiesEncrypt = dataBodiesEncrypt; + return instance.Value; + } + /// + /// 设置平台登入加密算法 + /// + /// + /// + public JTNEGlobalConfigs SetPlatformLoginEncrypt(Func platformLoginEncrypt) + { + instance.Value.PlatformLoginEncrypt = platformLoginEncrypt; + return instance.Value; + } } } diff --git a/src/JTNE.Protocol/JTNEHeaderPackage.cs b/src/JTNE.Protocol/JTNEHeaderPackage.cs index e086a76..01f7691 100644 --- a/src/JTNE.Protocol/JTNEHeaderPackage.cs +++ b/src/JTNE.Protocol/JTNEHeaderPackage.cs @@ -7,6 +7,8 @@ namespace JTNE.Protocol { /// /// 新能源包 + /// 只做简单的头部解析不做复杂的业务逻辑 + /// 例如:不同的厂商可能加密方式不同,所以消息数据体不做加解密的判断。 /// [JTNEFormatter(typeof(JTNEHeaderPackageFormatter))] public class JTNEHeaderPackage diff --git a/src/JTNE.Protocol/MessageBody/JTNE_0x02.cs b/src/JTNE.Protocol/MessageBody/JTNE_0x02.cs new file mode 100644 index 0000000..3d50be2 --- /dev/null +++ b/src/JTNE.Protocol/MessageBody/JTNE_0x02.cs @@ -0,0 +1,24 @@ +using JTNE.Protocol.Attributes; +using JTNE.Protocol.Formatters.MessageBodyFormatters; +using System; +using System.Collections.Generic; +using System.Text; + +namespace JTNE.Protocol.MessageBody +{ + /// + /// 实时信息上报 + /// + [JTNEFormatter(typeof(JTNE_0x02_Formatter))] + public class JTNE_0x02 : JTNEBodies + { + /// + /// 公共值 + /// + public Dictionary Values { get; set; } + /// + /// 自定义值 + /// + public Dictionary CusotmValues { get; set; } + } +} diff --git a/src/JTNE.Protocol/MessageBody/JTNE_0x02_0x01.cs b/src/JTNE.Protocol/MessageBody/JTNE_0x02_0x01.cs new file mode 100644 index 0000000..8643fc1 --- /dev/null +++ b/src/JTNE.Protocol/MessageBody/JTNE_0x02_0x01.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Text; +using JTNE.Protocol.Attributes; +using JTNE.Protocol.Formatters.MessageBodyFormatters; + +namespace JTNE.Protocol.MessageBody +{ + /// + /// 整车数据 + /// + [JTNEFormatter(typeof(JTNE_0x02_0x01_Formatter))] + public class JTNE_0x02_0x01 : JTNE_0x02_Body + { + public override byte TypeCode { get; set; } = 0x01; + + /// + /// 车辆状态 + /// + public byte CarStatus { get; set; } + /// + /// 充放电状态 + /// + public byte ChargeStatus { get; set; } + /// + /// 运行模式 + /// + public byte OperationMode { get; set; } + /// + /// 车速 + /// + public ushort Speed { get; set; } + /// + /// 累计里程 + /// + public uint TotalDis { get; set; } + /// + /// 总电压 + /// + public ushort TotalVoltage { get; set; } + /// + /// 总电流 + /// + public ushort TotalTemp { get; set; } + /// + /// SOC + /// + public byte SOC { get; set; } + /// + /// DC-DC 状态 + /// + public byte DCStatus { get; set; } + /// + /// 档位 + /// + public byte Stall { get; set; } + /// + /// 绝缘电阻 + /// + public ushort Resistance { get; set; } + /// + /// 加速踏板行程值 + /// + public byte Accelerator { get; set; } + /// + /// 制动踏板状态 + /// + public byte Brakes { get; set; } + } +} diff --git a/src/JTNE.Protocol/MessageBody/JTNE_0x02_Body.cs b/src/JTNE.Protocol/MessageBody/JTNE_0x02_Body.cs new file mode 100644 index 0000000..6b2376c --- /dev/null +++ b/src/JTNE.Protocol/MessageBody/JTNE_0x02_Body.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace JTNE.Protocol.MessageBody +{ + public abstract class JTNE_0x02_Body + { + public abstract byte TypeCode { get; set; } + + /// + /// 整车数据 + /// + public const byte JTNE_0x02_0x01 = 0x01; + /// + /// 驱动电机数据 + /// + public const byte JTNE_0x02_0x02 = 0x02; + + static JTNE_0x02_Body() + { +// todo:实时位置信息上报 + Keys = new Dictionary(); + Keys.Add(JTNE_0x02_0x01, typeof(JTNE_0x02_0x01)); + + } + + internal static Dictionary Keys; + + + } +} diff --git a/src/JTNewEnergy.sln b/src/JTNewEnergy.sln index a2ab150..1808432 100644 --- a/src/JTNewEnergy.sln +++ b/src/JTNewEnergy.sln @@ -5,11 +5,9 @@ VisualStudioVersion = 15.0.28307.271 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JTNE.Protocol", "JTNE.Protocol\JTNE.Protocol.csproj", "{5B164F58-141D-474E-A82E-672A1C252029}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GBNewEnergy.Protocol", "GBNewEnergy.Protocol\GBNewEnergy.Protocol.csproj", "{2E21E873-F9EA-4B98-80D6-9719DC924350}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JTNE.Protocol.Test", "JTNE.Protocol.Test\JTNE.Protocol.Test.csproj", "{30BB532E-4E49-4BB4-A1D4-61DC40F320DB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JTNE.Protocol.Test", "JTNE.Protocol.Test\JTNE.Protocol.Test.csproj", "{30BB532E-4E49-4BB4-A1D4-61DC40F320DB}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GBNewEnergy.Protocol.Test", "GBNewEnergy.Protocol.Test\GBNewEnergy.Protocol.Test.csproj", "{E8A2DBC3-3384-408F-A9D0-247723EFE383}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GBNewEnergy.Protocol", "GBNewEnergy.Protocol\GBNewEnergy.Protocol.csproj", "{5F7A89EB-2417-420A-839D-A878A648BAE1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,18 +19,14 @@ Global {5B164F58-141D-474E-A82E-672A1C252029}.Debug|Any CPU.Build.0 = Debug|Any CPU {5B164F58-141D-474E-A82E-672A1C252029}.Release|Any CPU.ActiveCfg = Release|Any CPU {5B164F58-141D-474E-A82E-672A1C252029}.Release|Any CPU.Build.0 = Release|Any CPU - {2E21E873-F9EA-4B98-80D6-9719DC924350}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2E21E873-F9EA-4B98-80D6-9719DC924350}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2E21E873-F9EA-4B98-80D6-9719DC924350}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2E21E873-F9EA-4B98-80D6-9719DC924350}.Release|Any CPU.Build.0 = Release|Any CPU {30BB532E-4E49-4BB4-A1D4-61DC40F320DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {30BB532E-4E49-4BB4-A1D4-61DC40F320DB}.Debug|Any CPU.Build.0 = Debug|Any CPU {30BB532E-4E49-4BB4-A1D4-61DC40F320DB}.Release|Any CPU.ActiveCfg = Release|Any CPU {30BB532E-4E49-4BB4-A1D4-61DC40F320DB}.Release|Any CPU.Build.0 = Release|Any CPU - {E8A2DBC3-3384-408F-A9D0-247723EFE383}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8A2DBC3-3384-408F-A9D0-247723EFE383}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8A2DBC3-3384-408F-A9D0-247723EFE383}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8A2DBC3-3384-408F-A9D0-247723EFE383}.Release|Any CPU.Build.0 = Release|Any CPU + {5F7A89EB-2417-420A-839D-A878A648BAE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F7A89EB-2417-420A-839D-A878A648BAE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F7A89EB-2417-420A-839D-A878A648BAE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F7A89EB-2417-420A-839D-A878A648BAE1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE