@@ -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 |
@@ -1,2 +1,45 @@ | |||||
# GBNewEnergy | |||||
GBNewEnergy | |||||
# JTNewEnergy协议 | |||||
[](https://github.com/SmallChi/JTNewEnergy/blob/master/LICENSE)[](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 | - | - | 平台交换自定义数据 | |
@@ -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<string>() | |||||
{ | |||||
"1234", | |||||
"4567", | |||||
"9870" | |||||
} | |||||
}); | |||||
var hex = JTNESerializer.Serialize(jTNEPackage).ToHexString(); | |||||
Assert.Equal("232301FE313233343536373839000000000000000001002A130116173738000131323334353637383939383736353433323130300304313233343435363739383730FD", hex); | |||||
} | |||||
} | |||||
} |
@@ -4,6 +4,8 @@ | |||||
<TargetFramework>netcoreapp2.2</TargetFramework> | <TargetFramework>netcoreapp2.2</TargetFramework> | ||||
<IsPackable>false</IsPackable> | <IsPackable>false</IsPackable> | ||||
<LangVersion>7.1</LangVersion> | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
@@ -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<string>() | |||||
{ | |||||
"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<JTNEHeaderPackage>(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<JTNE_0x01>(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]); | |||||
} | |||||
} | |||||
} |
@@ -5,6 +5,7 @@ using System.Text; | |||||
using Xunit; | using Xunit; | ||||
using JTNE.Protocol.Enums; | using JTNE.Protocol.Enums; | ||||
using JTNE.Protocol.MessageBody; | using JTNE.Protocol.MessageBody; | ||||
using JTNE.Protocol.Internal; | |||||
namespace JTNE.Protocol.Test.Package | namespace JTNE.Protocol.Test.Package | ||||
{ | { | ||||
@@ -52,5 +53,70 @@ namespace JTNE.Protocol.Test.Package | |||||
Assert.Equal("4567", jTNE_0X01.BatteryNos[1]); | Assert.Equal("4567", jTNE_0X01.BatteryNos[1]); | ||||
Assert.Equal("9870", jTNE_0X01.BatteryNos[2]); | 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<string>() | |||||
{ | |||||
"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]); | |||||
} | |||||
} | } | ||||
} | } |
@@ -27,8 +27,6 @@ namespace JTNE.Protocol.Extensions | |||||
jTNEPackage.MsgId = msgId.ToByteValue(); | jTNEPackage.MsgId = msgId.ToByteValue(); | ||||
jTNEPackage.Bodies = bodies; | jTNEPackage.Bodies = bodies; | ||||
jTNEPackage.VIN = vin; | jTNEPackage.VIN = vin; | ||||
// todo:加密处理 | |||||
return jTNEPackage; | return jTNEPackage; | ||||
} | } | ||||
@@ -45,8 +43,6 @@ namespace JTNE.Protocol.Extensions | |||||
jTNEPackage.AskId = askId.ToByteValue(); | jTNEPackage.AskId = askId.ToByteValue(); | ||||
jTNEPackage.MsgId = msgId.ToByteValue(); | jTNEPackage.MsgId = msgId.ToByteValue(); | ||||
jTNEPackage.VIN = vin; | jTNEPackage.VIN = vin; | ||||
// todo:加密处理 | |||||
return jTNEPackage; | return jTNEPackage; | ||||
} | } | ||||
@@ -67,8 +63,6 @@ namespace JTNE.Protocol.Extensions | |||||
jTNEPackage.MsgId = msgId; | jTNEPackage.MsgId = msgId; | ||||
jTNEPackage.Bodies = bodies; | jTNEPackage.Bodies = bodies; | ||||
jTNEPackage.VIN = vin; | jTNEPackage.VIN = vin; | ||||
// todo:加密处理 | |||||
return jTNEPackage; | return jTNEPackage; | ||||
} | } | ||||
@@ -85,8 +79,6 @@ namespace JTNE.Protocol.Extensions | |||||
jTNEPackage.AskId = askId.ToByteValue(); | jTNEPackage.AskId = askId.ToByteValue(); | ||||
jTNEPackage.MsgId = msgId; | jTNEPackage.MsgId = msgId; | ||||
jTNEPackage.VIN = vin; | jTNEPackage.VIN = vin; | ||||
// todo:加密处理 | |||||
return jTNEPackage; | return jTNEPackage; | ||||
} | } | ||||
} | } | ||||
@@ -41,7 +41,7 @@ namespace JTNE.Protocol.Formatters | |||||
jTNEPackage.DataUnitLength = JTNEBinaryExtensions.ReadUInt16Little(bytes, ref offset); | jTNEPackage.DataUnitLength = JTNEBinaryExtensions.ReadUInt16Little(bytes, ref offset); | ||||
// 8.数据体 | // 8.数据体 | ||||
// 8.1.根据数据加密方式进行解码 | // 8.1.根据数据加密方式进行解码 | ||||
// todo: 8.2.解析出对应数据体 | |||||
// 8.2.解析出对应数据体 | |||||
if (jTNEPackage.DataUnitLength > 0) | if (jTNEPackage.DataUnitLength > 0) | ||||
{ | { | ||||
Type jTNEBodiesImplType = JTNEMsgIdFactory.GetBodiesImplTypeByMsgId(jTNEPackage.MsgId); | Type jTNEBodiesImplType = JTNEMsgIdFactory.GetBodiesImplTypeByMsgId(jTNEPackage.MsgId); | ||||
@@ -50,10 +50,25 @@ namespace JTNE.Protocol.Formatters | |||||
int bodyReadSize = 0; | int bodyReadSize = 0; | ||||
try | 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) | catch (Exception ex) | ||||
{ | { | ||||
@@ -82,9 +97,8 @@ namespace JTNE.Protocol.Formatters | |||||
offset += JTNEBinaryExtensions.WriteStringPadRightLittle(bytes, offset, value.VIN, 17); | offset += JTNEBinaryExtensions.WriteStringPadRightLittle(bytes, offset, value.VIN, 17); | ||||
// 6.数据加密方式 | // 6.数据加密方式 | ||||
offset += JTNEBinaryExtensions.WriteByteLittle(bytes, offset, value.EncryptMethod); | offset += JTNEBinaryExtensions.WriteByteLittle(bytes, offset, value.EncryptMethod); | ||||
// 7.记录存储数据长度的当前偏移量 | |||||
// 7.记录当前偏移量 | |||||
int tmpOffset = offset; | int tmpOffset = offset; | ||||
offset += FixedDataBodyLength; | |||||
// 8.数据体 | // 8.数据体 | ||||
Type jTNEBodiesImplType = JTNEMsgIdFactory.GetBodiesImplTypeByMsgId(value.MsgId); | Type jTNEBodiesImplType = JTNEMsgIdFactory.GetBodiesImplTypeByMsgId(value.MsgId); | ||||
int messageBodyOffset = 0; | int messageBodyOffset = 0; | ||||
@@ -92,17 +106,46 @@ namespace JTNE.Protocol.Formatters | |||||
{ | { | ||||
if (value.Bodies != null) | if (value.Bodies != null) | ||||
{ | { | ||||
// 8.1 处理数据体 | |||||
// todo: 8.2.判断是否有加密 | |||||
// 8.1.处理数据体 | |||||
// 8.2.判断是否有加密 | |||||
messageBodyOffset = JTNEFormatterResolverExtensions.JTNEDynamicSerialize( | messageBodyOffset = JTNEFormatterResolverExtensions.JTNEDynamicSerialize( | ||||
JTNEFormatterExtensions.GetFormatter(jTNEBodiesImplType), | JTNEFormatterExtensions.GetFormatter(jTNEBodiesImplType), | ||||
ref bytes, | ref bytes, | ||||
offset, | |||||
offset + FixedDataBodyLength, | |||||
value.Bodies); | 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.校验码 | // 10.校验码 | ||||
var bccCode = bytes.ToXor(2, offset); | var bccCode = bytes.ToXor(2, offset); | ||||
@@ -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<JTNE_0x02_0x01> | |||||
{ | |||||
public JTNE_0x02_0x01 Deserialize(ReadOnlySpan<byte> 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; | |||||
} | |||||
} | |||||
} |
@@ -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<JTNE_0x02> | |||||
{ | |||||
public JTNE_0x02 Deserialize(ReadOnlySpan<byte> 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; | |||||
} | |||||
} | |||||
} |
@@ -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); | |||||
} | |||||
} |
@@ -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(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -35,7 +35,6 @@ namespace JTNE.Protocol | |||||
return instance.Value; | return instance.Value; | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 设备流水号 | /// 设备流水号 | ||||
/// </summary> | /// </summary> | ||||
@@ -51,6 +50,18 @@ namespace JTNE.Protocol | |||||
/// </summary> | /// </summary> | ||||
public bool SkipCRCCode { get; private set; } | public bool SkipCRCCode { get; private set; } | ||||
/// <summary> | /// <summary> | ||||
/// 消息数据体加密算法 | |||||
/// RSA=>IJTNEEncryptImpl | |||||
/// AES=>IJTNEEncryptImpl | |||||
/// </summary> | |||||
public Func<byte,IJTNEEncrypt> DataBodiesEncrypt { get; private set; } | |||||
/// <summary> | |||||
/// 平台登入加密算法 | |||||
/// RSA=>IJTNEEncryptImpl | |||||
/// AES=>IJTNEEncryptImpl | |||||
/// </summary> | |||||
public Func<byte, IJTNEEncrypt> PlatformLoginEncrypt { get; private set; } | |||||
/// <summary> | |||||
/// 注册自定义消息 | /// 注册自定义消息 | ||||
/// </summary> | /// </summary> | ||||
/// <typeparam name="TJTNEBodies"></typeparam> | /// <typeparam name="TJTNEBodies"></typeparam> | ||||
@@ -105,5 +116,25 @@ namespace JTNE.Protocol | |||||
instance.Value.PlatformMsgSNDistributed = platformMsgSNDistributed; | instance.Value.PlatformMsgSNDistributed = platformMsgSNDistributed; | ||||
return instance.Value; | return instance.Value; | ||||
} | } | ||||
/// <summary> | |||||
/// 设置消息数据体加密算法 | |||||
/// </summary> | |||||
/// <param name="dataBodiesEncrypt"></param> | |||||
/// <returns></returns> | |||||
public JTNEGlobalConfigs SetDataBodiesEncrypt(Func<byte, IJTNEEncrypt> dataBodiesEncrypt) | |||||
{ | |||||
instance.Value.DataBodiesEncrypt = dataBodiesEncrypt; | |||||
return instance.Value; | |||||
} | |||||
/// <summary> | |||||
/// 设置平台登入加密算法 | |||||
/// </summary> | |||||
/// <param name="platformLoginEncrypt"></param> | |||||
/// <returns></returns> | |||||
public JTNEGlobalConfigs SetPlatformLoginEncrypt(Func<byte, IJTNEEncrypt> platformLoginEncrypt) | |||||
{ | |||||
instance.Value.PlatformLoginEncrypt = platformLoginEncrypt; | |||||
return instance.Value; | |||||
} | |||||
} | } | ||||
} | } |
@@ -7,6 +7,8 @@ namespace JTNE.Protocol | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// 新能源包 | /// 新能源包 | ||||
/// 只做简单的头部解析不做复杂的业务逻辑 | |||||
/// 例如:不同的厂商可能加密方式不同,所以消息数据体不做加解密的判断。 | |||||
/// </summary> | /// </summary> | ||||
[JTNEFormatter(typeof(JTNEHeaderPackageFormatter))] | [JTNEFormatter(typeof(JTNEHeaderPackageFormatter))] | ||||
public class JTNEHeaderPackage | public class JTNEHeaderPackage | ||||
@@ -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 | |||||
{ | |||||
/// <summary> | |||||
/// 实时信息上报 | |||||
/// </summary> | |||||
[JTNEFormatter(typeof(JTNE_0x02_Formatter))] | |||||
public class JTNE_0x02 : JTNEBodies | |||||
{ | |||||
/// <summary> | |||||
/// 公共值 | |||||
/// </summary> | |||||
public Dictionary<byte, JTNE_0x02_Body> Values { get; set; } | |||||
/// <summary> | |||||
/// 自定义值 | |||||
/// </summary> | |||||
public Dictionary<byte, JTNE_0x02_Body> CusotmValues { get; set; } | |||||
} | |||||
} |
@@ -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 | |||||
{ | |||||
/// <summary> | |||||
/// 整车数据 | |||||
/// </summary> | |||||
[JTNEFormatter(typeof(JTNE_0x02_0x01_Formatter))] | |||||
public class JTNE_0x02_0x01 : JTNE_0x02_Body | |||||
{ | |||||
public override byte TypeCode { get; set; } = 0x01; | |||||
/// <summary> | |||||
/// 车辆状态 | |||||
/// </summary> | |||||
public byte CarStatus { get; set; } | |||||
/// <summary> | |||||
/// 充放电状态 | |||||
/// </summary> | |||||
public byte ChargeStatus { get; set; } | |||||
/// <summary> | |||||
/// 运行模式 | |||||
/// </summary> | |||||
public byte OperationMode { get; set; } | |||||
/// <summary> | |||||
/// 车速 | |||||
/// </summary> | |||||
public ushort Speed { get; set; } | |||||
/// <summary> | |||||
/// 累计里程 | |||||
/// </summary> | |||||
public uint TotalDis { get; set; } | |||||
/// <summary> | |||||
/// 总电压 | |||||
/// </summary> | |||||
public ushort TotalVoltage { get; set; } | |||||
/// <summary> | |||||
/// 总电流 | |||||
/// </summary> | |||||
public ushort TotalTemp { get; set; } | |||||
/// <summary> | |||||
/// SOC | |||||
/// </summary> | |||||
public byte SOC { get; set; } | |||||
/// <summary> | |||||
/// DC-DC 状态 | |||||
/// </summary> | |||||
public byte DCStatus { get; set; } | |||||
/// <summary> | |||||
/// 档位 | |||||
/// </summary> | |||||
public byte Stall { get; set; } | |||||
/// <summary> | |||||
/// 绝缘电阻 | |||||
/// </summary> | |||||
public ushort Resistance { get; set; } | |||||
/// <summary> | |||||
/// 加速踏板行程值 | |||||
/// </summary> | |||||
public byte Accelerator { get; set; } | |||||
/// <summary> | |||||
/// 制动踏板状态 | |||||
/// </summary> | |||||
public byte Brakes { get; set; } | |||||
} | |||||
} |
@@ -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; } | |||||
/// <summary> | |||||
/// 整车数据 | |||||
/// </summary> | |||||
public const byte JTNE_0x02_0x01 = 0x01; | |||||
/// <summary> | |||||
/// 驱动电机数据 | |||||
/// </summary> | |||||
public const byte JTNE_0x02_0x02 = 0x02; | |||||
static JTNE_0x02_Body() | |||||
{ | |||||
// todo:实时位置信息上报 | |||||
Keys = new Dictionary<byte, Type>(); | |||||
Keys.Add(JTNE_0x02_0x01, typeof(JTNE_0x02_0x01)); | |||||
} | |||||
internal static Dictionary<byte, Type> Keys; | |||||
} | |||||
} |
@@ -5,11 +5,9 @@ VisualStudioVersion = 15.0.28307.271 | |||||
MinimumVisualStudioVersion = 10.0.40219.1 | MinimumVisualStudioVersion = 10.0.40219.1 | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JTNE.Protocol", "JTNE.Protocol\JTNE.Protocol.csproj", "{5B164F58-141D-474E-A82E-672A1C252029}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JTNE.Protocol", "JTNE.Protocol\JTNE.Protocol.csproj", "{5B164F58-141D-474E-A82E-672A1C252029}" | ||||
EndProject | 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 | 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 | EndProject | ||||
Global | Global | ||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | 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}.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.ActiveCfg = Release|Any CPU | ||||
{5B164F58-141D-474E-A82E-672A1C252029}.Release|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU | ||||
{30BB532E-4E49-4BB4-A1D4-61DC40F320DB}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU | ||||
{30BB532E-4E49-4BB4-A1D4-61DC40F320DB}.Release|Any CPU.Build.0 = 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 | EndGlobalSection | ||||
GlobalSection(SolutionProperties) = preSolution | GlobalSection(SolutionProperties) = preSolution | ||||
HideSolutionNode = FALSE | HideSolutionNode = FALSE | ||||