@@ -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> | |||
<IsPackable>false</IsPackable> | |||
<LangVersion>7.1</LangVersion> | |||
</PropertyGroup> | |||
<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 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<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.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; | |||
} | |||
} | |||
@@ -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); | |||
@@ -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; | |||
} | |||
} | |||
/// <summary> | |||
/// 设备流水号 | |||
/// </summary> | |||
@@ -51,6 +50,18 @@ namespace JTNE.Protocol | |||
/// </summary> | |||
public bool SkipCRCCode { get; private set; } | |||
/// <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> | |||
/// <typeparam name="TJTNEBodies"></typeparam> | |||
@@ -105,5 +116,25 @@ namespace JTNE.Protocol | |||
instance.Value.PlatformMsgSNDistributed = platformMsgSNDistributed; | |||
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> | |||
[JTNEFormatter(typeof(JTNEHeaderPackageFormatter))] | |||
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 | |||
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 | |||