diff --git a/src/JTNE.Protocol.Test/JTNE.Protocol.Test.csproj b/src/JTNE.Protocol.Test/JTNE.Protocol.Test.csproj
new file mode 100644
index 0000000..880d991
--- /dev/null
+++ b/src/JTNE.Protocol.Test/JTNE.Protocol.Test.csproj
@@ -0,0 +1,19 @@
+
+
+
+ netcoreapp2.2
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/JTNE.Protocol.Test/JTNEPackageTest.cs b/src/JTNE.Protocol.Test/JTNEPackageTest.cs
new file mode 100644
index 0000000..0b2450b
--- /dev/null
+++ b/src/JTNE.Protocol.Test/JTNEPackageTest.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Xunit;
+using JTNE.Protocol.Extensions;
+
+namespace JTNE.Protocol.Test
+{
+ public class JTNEPackageTest
+ {
+ [Fact]
+ public void Test1()
+ {
+ var hex = "23 23 05 FE 4C 47 48 43 34 56 31 44 33 48 45 32 30 32 36 35 32 01 00 08 12 06 08 12 06 3A 00 01 E9".ToHexBytes();
+ var package=JTNESerializer.Deserialize(hex);
+ }
+ }
+}
diff --git a/src/JTNE.Protocol/Attributes/JTNEBodiesTypeAttribute.cs b/src/JTNE.Protocol/Attributes/JTNEBodiesTypeAttribute.cs
new file mode 100644
index 0000000..65dff14
--- /dev/null
+++ b/src/JTNE.Protocol/Attributes/JTNEBodiesTypeAttribute.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace JTNE.Protocol.Attributes
+{
+ [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
+ public sealed class JTNEBodiesTypeAttribute : Attribute
+ {
+ public JTNEBodiesTypeAttribute(Type jT808BodiesType)
+ {
+ JT808BodiesType = jT808BodiesType;
+ }
+ public Type JT808BodiesType { get;}
+ }
+}
diff --git a/src/JTNE.Protocol/Attributes/JTNEFormatterAttribute.cs b/src/JTNE.Protocol/Attributes/JTNEFormatterAttribute.cs
new file mode 100644
index 0000000..3b6364a
--- /dev/null
+++ b/src/JTNE.Protocol/Attributes/JTNEFormatterAttribute.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace JTNE.Protocol.Attributes
+{
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = false, Inherited = true)]
+ public sealed class JTNEFormatterAttribute:Attribute
+ {
+ public Type FormatterType { get; private set; }
+
+ public object[] Arguments { get; private set; }
+
+ public JTNEFormatterAttribute(Type formatterType)
+ {
+ this.FormatterType = formatterType;
+ }
+
+ public JTNEFormatterAttribute(Type formatterType, params object[] arguments)
+ {
+ this.FormatterType = formatterType;
+ this.Arguments = arguments;
+ }
+ }
+}
diff --git a/src/JTNE.Protocol/Attributes/JTNEMsgIdDescriptionAttribute.cs b/src/JTNE.Protocol/Attributes/JTNEMsgIdDescriptionAttribute.cs
new file mode 100644
index 0000000..3503a9d
--- /dev/null
+++ b/src/JTNE.Protocol/Attributes/JTNEMsgIdDescriptionAttribute.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace JTNE.Protocol.Attributes
+{
+ [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
+ public sealed class JTNEMsgIdDescriptionAttribute : Attribute
+ {
+ public string Code { get; set; }
+
+ public string Name { get; set; }
+
+ public JTNEMsgIdDescriptionAttribute(string code,string name)
+ {
+ Code = code;
+ Name = name;
+ }
+ }
+}
diff --git a/src/JTNE.Protocol/Enums/JTNEAskId.cs b/src/JTNE.Protocol/Enums/JTNEAskId.cs
new file mode 100644
index 0000000..83972a5
--- /dev/null
+++ b/src/JTNE.Protocol/Enums/JTNEAskId.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace JTNE.Protocol.Enums
+{
+ ///
+ /// 应答标志
+ ///
+ public enum JTNEAskId:byte
+ {
+ ///
+ /// 接收到的信息正确
+ ///
+ Success=0x01,
+ ///
+ /// 设置未成功
+ ///
+ Error=0x02,
+ ///
+ /// VIN重复错误
+ ///
+ VinRepeatError=0x03,
+ ///
+ /// 数据包为命令包,而非应答包
+ ///
+ CMD=0xFE
+ }
+}
diff --git a/src/JTNE.Protocol/Enums/JTNEEncryptMethod.cs b/src/JTNE.Protocol/Enums/JTNEEncryptMethod.cs
new file mode 100644
index 0000000..3a83725
--- /dev/null
+++ b/src/JTNE.Protocol/Enums/JTNEEncryptMethod.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace JTNE.Protocol.Enums
+{
+ ///
+ /// 数据单元加密方式
+ /// 0x01:数据不加密;0x02:数据经过 RSA 算法加密;0x03:数据经过 AES128 位算法加密;“0xFE”表示异常,“0xFF”表示无效
+ ///
+ public enum JTNEEncryptMethod:byte
+ {
+ ///
+ /// 数据不加密
+ ///
+ None = 0x01,
+ ///
+ /// 数据经过 RSA 算法加密
+ ///
+ RSA = 0x02,
+ ///
+ /// 数据经过 AES128 位算法加密
+ ///
+ AES128 = 0x03,
+ ///
+ /// 表示异常
+ ///
+ Exception = 0xFE,
+ ///
+ /// 表示无效
+ ///
+ Invalid = 0xFF
+ }
+}
diff --git a/src/JTNE.Protocol/Enums/JTNEErrorCode.cs b/src/JTNE.Protocol/Enums/JTNEErrorCode.cs
new file mode 100644
index 0000000..02da28d
--- /dev/null
+++ b/src/JTNE.Protocol/Enums/JTNEErrorCode.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace JTNE.Protocol.Enums
+{
+ ///
+ /// 错误代码
+ ///
+ public enum JTNEErrorCode
+ {
+ ///
+ /// 开始标识错误
+ ///
+ BeginFlagError = 1001,
+ ///
+ /// BCC校验错误
+ ///
+ BCCCodeError = 1002,
+ ///
+ /// 没有标记
+ ///
+ ///
+ GetFormatterAttributeError = 1003,
+ }
+}
diff --git a/src/JTNE.Protocol/Enums/JTNEMsgId.cs b/src/JTNE.Protocol/Enums/JTNEMsgId.cs
new file mode 100644
index 0000000..d56a78f
--- /dev/null
+++ b/src/JTNE.Protocol/Enums/JTNEMsgId.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace JTNE.Protocol.Enums
+{
+ ///
+ /// 命令单元
+ ///
+ public enum JTNEMsgId:byte
+ {
+ ///
+ /// 车辆登入
+ ///
+ login = 0x01,
+ ///
+ /// 实时信息上传
+ ///
+ uploadim = 0x02,
+ ///
+ /// 补传信息上传
+ ///
+ uploadsup = 0x03,
+ ///
+ /// 车辆登出
+ ///
+ loginout = 0x04,
+ ///
+ /// 平台登入
+ ///
+ platformlogin = 0x05,
+ ///
+ /// 平台登出
+ ///
+ platformlogout = 0x06,
+ ///
+ /// 心跳
+ ///
+ heartbeat = 0x07,
+ ///
+ /// 终端校时
+ ///
+ checktime = 0x08,
+ ///
+ /// 查询命令
+ ///
+ query = 0x80,
+ ///
+ /// 设置命令
+ ///
+ settings = 0x81,
+ ///
+ /// 控制命令
+ ///
+ control = 0x82
+ }
+}
diff --git a/src/JTNE.Protocol/Exceptions/JTNEException.cs b/src/JTNE.Protocol/Exceptions/JTNEException.cs
new file mode 100644
index 0000000..48f6126
--- /dev/null
+++ b/src/JTNE.Protocol/Exceptions/JTNEException.cs
@@ -0,0 +1,22 @@
+using JTNE.Protocol.Enums;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace JTNE.Protocol.Exceptions
+{
+ public class JTNEException:Exception
+ {
+ public JTNEException(JTNEErrorCode errorCode) : base(errorCode.ToString())
+ {
+ this.ErrorCode = errorCode;
+ }
+
+ public JTNEException(JTNEErrorCode errorCode, string message) : base(message)
+ {
+ this.ErrorCode = errorCode;
+ }
+
+ public JTNEErrorCode ErrorCode { get; }
+ }
+}
diff --git a/src/JTNE.Protocol/Extensions/JTNEBCDExtensions.cs b/src/JTNE.Protocol/Extensions/JTNEBCDExtensions.cs
new file mode 100644
index 0000000..68f7405
--- /dev/null
+++ b/src/JTNE.Protocol/Extensions/JTNEBCDExtensions.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Buffers;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+
+namespace JTNE.Protocol.Extensions
+{
+ public static partial class JTNEBinaryExtensions
+ {
+ public static string ReadBCDLittle(ReadOnlySpan buf, ref int offset, int len)
+ {
+ int count = len / 2;
+ StringBuilder bcdSb = new StringBuilder(count);
+ for(int i = 0; i < count; i++)
+ {
+ bcdSb.Append(buf[offset + i].ToString("X2"));
+ }
+ offset = offset + count;
+ return bcdSb.ToString().TrimStart('0');
+ }
+
+ public static int WriteBCDLittle(byte[] bytes, int offset, string data,int len)
+ {
+ string bcdText = data == null ? "" : data;
+ int startIndex = 0;
+ int noOfZero = len - bcdText.Length;
+ if (noOfZero > 0)
+ {
+ bcdText = bcdText.Insert(startIndex, new string('0', noOfZero));
+ }
+ int byteIndex = 0;
+ int count = len / 2;
+ byte[] tempBytes = new byte[count];
+ while (startIndex < bcdText.Length && byteIndex < count)
+ {
+ tempBytes[byteIndex] = Convert.ToByte(bcdText.Substring(startIndex, 2), 16);
+ startIndex += 2;
+ byteIndex++;
+ }
+ Array.Copy(tempBytes, 0, bytes, offset, tempBytes.Length);
+ return count;
+ }
+ }
+}
diff --git a/src/JTNE.Protocol/Extensions/JTNEBinaryExtensions.cs b/src/JTNE.Protocol/Extensions/JTNEBinaryExtensions.cs
new file mode 100644
index 0000000..0634756
--- /dev/null
+++ b/src/JTNE.Protocol/Extensions/JTNEBinaryExtensions.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace JTNE.Protocol.Extensions
+{
+ public static partial class JTNEBinaryExtensions
+ {
+ 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 byte[] ReadBytesLittle(ReadOnlySpan read, ref int offset)
+ {
+ ReadOnlySpan temp = read.Slice(offset);
+ offset = offset + temp.Length;
+ return temp.ToArray();
+ }
+
+ public static int WriteUInt32Little(byte[] write, int offset, uint 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(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 WriteUInt16Little(byte[] write, int offset, ushort data)
+ {
+ write[offset] = (byte)(data >> 8);
+ write[offset + 1] = (byte)data;
+ return 2;
+ }
+
+ public static int WriteByteLittle(byte[] write, int offset, byte data)
+ {
+ write[offset] = data;
+ return 1;
+ }
+
+ public static int WriteBytesLittle(byte[] write, int offset, byte[] data)
+ {
+ Array.Copy(data, 0, write, offset, data.Length);
+ return data.Length;
+ }
+
+ public static IEnumerable ToBytes(this string data, Encoding coding)
+ {
+ return coding.GetBytes(data);
+ }
+
+ public static IEnumerable ToBytes(this string data)
+ {
+ return ToBytes(data, JTNEGlobalConfigs.Instance.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;
+ }
+
+ ///
+ /// 经纬度
+ ///
+ ///
+ ///
+ public static double ToLatLng(this int latlng)
+ {
+ return Math.Round(latlng / Math.Pow(10, 6), 6);
+ }
+ }
+}
diff --git a/src/JTNE.Protocol/Extensions/JTNEDateTimeExtensions.cs b/src/JTNE.Protocol/Extensions/JTNEDateTimeExtensions.cs
new file mode 100644
index 0000000..c6cccdb
--- /dev/null
+++ b/src/JTNE.Protocol/Extensions/JTNEDateTimeExtensions.cs
@@ -0,0 +1,170 @@
+using System;
+using System.Buffers;
+using System.Collections.Generic;
+using System.Text;
+
+namespace JTNE.Protocol.Extensions
+{
+ public static partial class JTNEBinaryExtensions
+ {
+ ///
+ /// 日期限制于2000年
+ ///
+ private const int DateLimitYear = 2000;
+
+ private static readonly DateTime UTCBaseTime = new DateTime(1970, 1, 1);
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// D2: 10 X2:16
+ ///
+ public static DateTime ReadDateTime6Little(ReadOnlySpan buf, ref int offset,string format= "X2")
+ {
+ DateTime d = UTCBaseTime;
+ try
+ {
+ int year = Convert.ToInt32(buf[offset].ToString(format)) + DateLimitYear;
+ int month = Convert.ToInt32(buf[offset + 1].ToString(format));
+ int day = Convert.ToInt32(buf[offset + 2].ToString(format));
+ int hour = Convert.ToInt32(buf[offset + 3].ToString(format));
+ int minute = Convert.ToInt32(buf[offset + 4].ToString(format));
+ int second = Convert.ToInt32(buf[offset + 5].ToString(format));
+ d = new DateTime(year, month, day, hour, minute, second);
+ }
+ catch (Exception ex)
+ {
+ d = UTCBaseTime;
+ }
+ offset = offset + 6;
+ return d;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// D2: 10 X2:16
+ ///
+ public static DateTime ReadDateTime4Little(ReadOnlySpan buf, ref int offset, string format = "X2")
+ {
+ DateTime d = UTCBaseTime;
+ try
+ {
+ d = new DateTime(
+ (Convert.ToInt32(buf[offset].ToString(format)) << 8) + Convert.ToByte(buf[offset + 1]),
+ Convert.ToInt32(buf[offset + 2].ToString(format)),
+ Convert.ToInt32(buf[offset + 3].ToString(format)));
+ }
+ catch (Exception)
+ {
+ d = UTCBaseTime;
+ }
+ offset = offset + 4;
+ return d;
+ }
+
+ public static DateTime ReadUTCDateTimeLittle(ReadOnlySpan buf, ref int offset)
+ {
+ ulong result = 0;
+ for (int i = 0; i < 8; i++)
+ {
+ ulong currentData = (ulong)buf[offset + i] << (8 * (8 - i - 1));
+ result += currentData;
+ }
+ offset += 8;
+ return UTCBaseTime.AddSeconds(result).AddHours(8);
+ }
+ ///
+ /// hh-mm-ss-msms
+ /// hh-mm-ss-fff
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static DateTime ReadDateTime5Little(ReadOnlySpan buf, ref int offset, string format = "X2")
+ {
+
+ DateTime dateTime = new DateTime(
+ DateTime.Now.Year,
+ DateTime.Now.Month,
+ DateTime.Now.Day,
+ Convert.ToInt32(buf[offset].ToString(format)),
+ Convert.ToInt32(buf[offset + 1].ToString(format)),
+ Convert.ToInt32(buf[offset + 2].ToString(format)),
+ ((buf[offset + 3] << 8) + buf[offset + 4]));
+ offset = offset + 5;
+ return dateTime;
+ }
+
+ public static int WriteUTCDateTimeLittle(byte[] bytes, int offset, DateTime date)
+ {
+ ulong totalSecends = (ulong)(date.AddHours(-8) - UTCBaseTime).TotalSeconds;
+ //高位在前
+ for (int i = 7; i >= 0; i--)
+ {
+ bytes[offset + i] = (byte)(totalSecends & 0xFF); //取低8位
+ totalSecends = totalSecends >> 8;
+ }
+ return 8;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// BCD:10 HEX:16
+ ///
+ public static int WriteDateTime6Little(byte[] bytes, int offset, DateTime date,int fromBase=16)
+ {
+ bytes[offset] = Convert.ToByte(date.ToString("yy"), fromBase);
+ bytes[offset + 1] = Convert.ToByte(date.ToString("MM"), fromBase);
+ bytes[offset + 2] = Convert.ToByte(date.ToString("dd"), fromBase);
+ bytes[offset + 3] = Convert.ToByte(date.ToString("HH"), fromBase);
+ bytes[offset + 4] = Convert.ToByte(date.ToString("mm"), fromBase);
+ bytes[offset + 5] = Convert.ToByte(date.ToString("ss"), fromBase);
+ return 6;
+ }
+
+ ///
+ /// YYYYMMDD
+ ///
+ ///
+ ///
+ ///
+ /// BCD:10 HEX:16
+ ///
+ public static int WriteDateTime4Little(byte[] bytes, int offset, DateTime date, int fromBase = 16)
+ {
+ bytes[offset] = (byte)(date.Year>>8);
+ bytes[offset + 1] = (byte)(date.Year);
+ bytes[offset + 2] = Convert.ToByte(date.ToString("MM"), fromBase);
+ bytes[offset + 3] = Convert.ToByte(date.ToString("dd"), fromBase);
+ return 4;
+ }
+ ///
+ /// hh-mm-ss-msms
+ /// hh-mm-ss-fff
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static int WriteDateTime5Little(byte[] bytes, int offset, DateTime date, int fromBase = 16)
+ {
+ bytes[offset] = Convert.ToByte(date.ToString("HH"), fromBase);
+ bytes[offset + 1] = Convert.ToByte(date.ToString("mm"), fromBase);
+ bytes[offset + 2] = Convert.ToByte(date.ToString("ss"), fromBase);
+ bytes[offset + 3] = (byte)(date.Millisecond >> 8);
+ bytes[offset + 4] = (byte)(date.Millisecond);
+ return 5;
+ }
+ }
+}
diff --git a/src/JTNE.Protocol/Extensions/JTNEEnumExtensions.cs b/src/JTNE.Protocol/Extensions/JTNEEnumExtensions.cs
new file mode 100644
index 0000000..26fab03
--- /dev/null
+++ b/src/JTNE.Protocol/Extensions/JTNEEnumExtensions.cs
@@ -0,0 +1,207 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Reflection;
+
+namespace JTNE.Protocol.Extensions
+{
+ ///
+ /// 枚举扩展
+ ///
+ public static class JT808EnumExtensions
+ {
+ ///
+ /// 转为整型
+ ///
+ ///
+ ///
+ ///
+ public static int ToValue(this T t) where T : struct
+ {
+ return Convert.ToInt32(t);
+ }
+
+ ///
+ /// 转为 u16 整型
+ ///
+ ///
+ ///
+ ///
+ public static ushort ToUInt16Value(this T t) where T : struct
+ {
+ return Convert.ToUInt16(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;
+ }
+ }
+
+ ///
+ /// 根据值获取对应枚举类型集合
+ ///
+ /// 具体枚举类型
+ /// 枚举值
+ /// 位数(8,16,32)
+ /// 是否忽略未知数据
+ ///
+ public static IEnumerable GetEnumTypes(this int value, int digit, bool ignoreUnknown=false) where T : Enum
+ {
+ List values = new List();
+ for (int i = 0; i < digit; i++)
+ {
+ if (Math.Pow(2, i) <= value) continue;
+ values.Add((T)Enum.ToObject(typeof(T), (int)Math.Pow(2, i - 1)));
+ value = value - (int)Math.Pow(2, i - 1);
+ i = 0;
+ if (value <= 0) break;
+ }
+ if (ignoreUnknown)
+ {
+ List results = new List();
+ foreach (var item in values)
+ {
+ foreach (string itemChild in Enum.GetNames(typeof(T)))
+ {
+ if (item.ToString() == itemChild)
+ {
+ results.Add(item);
+ break;
+ }
+ }
+ }
+ return results;
+ }
+ else
+ {
+ return values;
+ }
+ }
+ }
+}
diff --git a/src/JTNE.Protocol/Extensions/JTNEFormatterExtensions.cs b/src/JTNE.Protocol/Extensions/JTNEFormatterExtensions.cs
new file mode 100644
index 0000000..0b9a1e9
--- /dev/null
+++ b/src/JTNE.Protocol/Extensions/JTNEFormatterExtensions.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Reflection;
+using JTNE.Protocol.Formatters;
+using JTNE.Protocol.Enums;
+using JTNE.Protocol.Attributes;
+using JTNE.Protocol.Exceptions;
+
+namespace JTNE.Protocol.Extensions
+{
+ public static class JTNEFormatterExtensions
+ {
+ public static IJTNEFormatter GetFormatter()
+ {
+ IJTNEFormatter formatter;
+ var attr = typeof(T).GetTypeInfo().GetCustomAttribute();
+ if (attr == null)
+ {
+ return default;
+ }
+ if (attr.Arguments == null)
+ {
+ formatter = (IJTNEFormatter)Activator.CreateInstance(attr.FormatterType);
+ }
+ else
+ {
+ formatter = (IJTNEFormatter)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 JTNEException(JTNEErrorCode.GetFormatterAttributeError, formatterType.FullName);
+ }
+ if (attr.Arguments == null)
+ {
+ formatter = Activator.CreateInstance(attr.FormatterType);
+ }
+ else
+ {
+ formatter = Activator.CreateInstance(attr.FormatterType, attr.Arguments);
+ }
+ return formatter;
+ }
+ }
+}
diff --git a/src/JTNE.Protocol/Extensions/JTNEFormatterResolverExtensions.cs b/src/JTNE.Protocol/Extensions/JTNEFormatterResolverExtensions.cs
new file mode 100644
index 0000000..38f1263
--- /dev/null
+++ b/src/JTNE.Protocol/Extensions/JTNEFormatterResolverExtensions.cs
@@ -0,0 +1,81 @@
+using JTNE.Protocol.Formatters;
+using System;
+using System.Collections.Concurrent;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace JTNE.Protocol.Extensions
+{
+ ///
+ ///
+ /// 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 JTNEFormatterResolverExtensions
+ {
+ delegate int JTNESerializeMethod(object dynamicFormatter, ref byte[] bytes, int offset, object value);
+
+ delegate dynamic JTNEDeserializeMethod(object dynamicFormatter, ReadOnlySpan bytes, out int readSize);
+
+ static readonly ConcurrentDictionary jTNESerializers = new ConcurrentDictionary();
+
+ static readonly ConcurrentDictionary jTNEDeserializes = new ConcurrentDictionary();
+
+ public static int JTNEDynamicSerialize(object objFormatter, ref byte[] bytes, int offset, dynamic value)
+ {
+ Type type = value.GetType();
+ var ti = type.GetTypeInfo();
+ (object Value, JTNESerializeMethod SerializeMethod) formatterAndDelegate;
+ if (!jTNESerializers.TryGetValue(type, out formatterAndDelegate))
+ {
+ var t = type;
+ {
+ var formatterType = typeof(IJTNEFormatter<>).MakeGenericType(t);
+ var param0 = Expression.Parameter(typeof(object), "formatter");
+ var param1 = Expression.Parameter(typeof(byte[]).MakeByRefType(), "bytes");
+ var param2 = Expression.Parameter(typeof(int), "offset");
+ var param3 = Expression.Parameter(typeof(object), "value");
+ var serializeMethodInfo = formatterType.GetRuntimeMethod("Serialize", new[] { typeof(byte[]).MakeByRefType(), 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);
+ }
+ jTNESerializers.TryAdd(t, formatterAndDelegate);
+ }
+ return formatterAndDelegate.SerializeMethod(formatterAndDelegate.Value,ref bytes, offset, value);
+ }
+
+ public static dynamic JTNEDynamicDeserialize(object objFormatter,ReadOnlySpan bytes, out int readSize)
+ {
+ var type = objFormatter.GetType();
+ (object Value, JTNEDeserializeMethod DeserializeMethod) formatterAndDelegate;
+ if (!jTNEDeserializes.TryGetValue(type, out formatterAndDelegate))
+ {
+ var t = type;
+ {
+ var formatterType = typeof(IJTNEFormatter<>).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);
+ }
+ jTNEDeserializes.TryAdd(t, formatterAndDelegate);
+ }
+ return formatterAndDelegate.DeserializeMethod(formatterAndDelegate.Value, bytes, out readSize);
+ }
+ }
+}
diff --git a/src/JTNE.Protocol/Extensions/JTNEHexExtensions.cs b/src/JTNE.Protocol/Extensions/JTNEHexExtensions.cs
new file mode 100644
index 0000000..a3c0cd7
--- /dev/null
+++ b/src/JTNE.Protocol/Extensions/JTNEHexExtensions.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Buffers;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace JTNE.Protocol.Extensions
+{
+ ///
+ ///
+ /// ref:"www.codeproject.com/tips/447938/high-performance-csharp-byte-array-to-hex-string-t"
+ ///
+ public static partial class JTNEBinaryExtensions
+ {
+ public static string ToHexString(this byte[] source)
+ {
+ return HexUtil.DoHexDump(source,0, source.Length).ToUpper();
+ }
+
+ public static int WriteHexStringLittle(byte[] bytes, int offset, string data, int len)
+ {
+ if (data == null) data = "";
+ data = data.Replace(" ", "");
+ int startIndex = 0;
+ if (data.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
+ {
+ startIndex = 2;
+ }
+ int length = len;
+ if (length == -1)
+ {
+ length = (data.Length - startIndex) / 2;
+ }
+ int noOfZero = length * 2 + startIndex - data.Length;
+ if (noOfZero > 0)
+ {
+ data = data.Insert(startIndex, new string('0', noOfZero));
+ }
+ int byteIndex = 0;
+ while (startIndex < data.Length && byteIndex < length)
+ {
+ bytes[offset+byteIndex] = Convert.ToByte(data.Substring(startIndex, 2), 16);
+ startIndex += 2;
+ byteIndex++;
+ }
+ return length;
+ }
+
+ ///
+ /// 16进制字符串转16进制数组
+ ///
+ ///
+ ///
+ ///
+ public static byte[] ToHexBytes(this string hexString)
+ {
+ hexString = hexString.Replace(" ", "");
+ 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;
+ }
+
+ public static string ReadHexStringLittle(ReadOnlySpan read, ref int offset, int len)
+ {
+ ReadOnlySpan source = read.Slice(offset, len);
+ string hex = HexUtil.DoHexDump(read, offset, len);
+ offset += len;
+ return hex;
+ }
+
+ ///
+ /// ref dotnetty
+ ///
+ static class HexUtil
+ {
+ static readonly char[] HexdumpTable = new char[256 * 4];
+
+ static HexUtil()
+ {
+ char[] digits = "0123456789abcdef".ToCharArray();
+ for (int i = 0; i < 256; i++)
+ {
+ HexdumpTable[i << 1] = digits[(int)((uint)i >> 4 & 0x0F)];
+ HexdumpTable[(i << 1) + 1] = digits[i & 0x0F];
+ }
+ }
+
+ public static string DoHexDump(ReadOnlySpan buffer, int fromIndex, int length)
+ {
+ if (length == 0)
+ {
+ return "";
+ }
+ int endIndex = fromIndex + length;
+ var buf = new char[length << 1];
+ int srcIdx = fromIndex;
+ int dstIdx = 0;
+ for (; srcIdx < endIndex; srcIdx++, dstIdx += 2)
+ {
+ Array.Copy(HexdumpTable, buffer[srcIdx] << 1,buf, dstIdx, 2);
+ }
+ return new string(buf);
+ }
+
+ public static string DoHexDump(byte[] array, int fromIndex, int length)
+ {
+ if (length == 0)
+ {
+ return "";
+ }
+ int endIndex = fromIndex + length;
+ var buf = new char[length << 1];
+ int srcIdx = fromIndex;
+ int dstIdx = 0;
+ for (; srcIdx < endIndex; srcIdx++, dstIdx += 2)
+ {
+ Array.Copy(HexdumpTable, (array[srcIdx] & 0xFF) << 1, buf, dstIdx, 2);
+ }
+ return new string(buf);
+ }
+ }
+ }
+}
diff --git a/src/JTNE.Protocol/Extensions/JTNEStringExtensions.cs b/src/JTNE.Protocol/Extensions/JTNEStringExtensions.cs
new file mode 100644
index 0000000..6f91618
--- /dev/null
+++ b/src/JTNE.Protocol/Extensions/JTNEStringExtensions.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Buffers.Binary;
+using System.Buffers;
+
+namespace JTNE.Protocol.Extensions
+{
+ public static partial class JTNEBinaryExtensions
+ {
+ public static string ReadStringLittle(ReadOnlySpan read, ref int offset, int len)
+ {
+ string value = JTNEGlobalConfigs.Instance.Encoding.GetString(read.Slice(offset, len).ToArray());
+ offset += len;
+ return value.Trim('\0');
+ }
+
+ public static string ReadStringLittle(ReadOnlySpan read, ref int offset)
+ {
+ string value = JTNEGlobalConfigs.Instance.Encoding.GetString(read.Slice(offset).ToArray());
+ offset += value.Length;
+ return value.Trim('\0');
+ }
+
+ public static int WriteStringLittle(byte[] bytes, int offset, string data)
+ {
+ byte[] codeBytes = JTNEGlobalConfigs.Instance.Encoding.GetBytes(data);
+ Array.Copy(codeBytes, 0, bytes, offset, codeBytes.Length);
+ return codeBytes.Length;
+ }
+
+ public static int WriteStringLittle(byte[] bytes, int offset, string data, int len)
+ {
+ byte[] tempBytes = null;
+ if (string.IsNullOrEmpty(data))
+ {
+ tempBytes = new byte[0];
+ }
+ else
+ {
+ tempBytes = JTNEGlobalConfigs.Instance.Encoding.GetBytes(data);
+ }
+ byte[] rBytes = new byte[len];
+ for (int i = 0; i < tempBytes.Length; i++)
+ {
+ if (i >= len) break;
+ rBytes[i] = tempBytes[i];
+ }
+ Array.Copy(rBytes, 0, bytes, offset, rBytes.Length);
+ return rBytes.Length;
+ }
+
+ public static int WriteStringPadLeftLittle(byte[] bytes, int offset, string data, int len)
+ {
+ data = data.PadLeft(len, '\0');
+ byte[] codeBytes = JTNEGlobalConfigs.Instance.Encoding.GetBytes(data);
+ Array.Copy(codeBytes, 0, bytes, offset, codeBytes.Length);
+ return codeBytes.Length;
+ }
+
+ public static int WriteStringPadRightLittle(byte[] bytes, int offset, string data, int len)
+ {
+ data = data.PadRight(len, '\0');
+ byte[] codeBytes = JTNEGlobalConfigs.Instance.Encoding.GetBytes(data);
+ Array.Copy(codeBytes, 0, bytes, offset, codeBytes.Length);
+ return codeBytes.Length;
+ }
+ }
+}
diff --git a/src/JTNE.Protocol/Extensions/JTNEXorExtensions.cs b/src/JTNE.Protocol/Extensions/JTNEXorExtensions.cs
new file mode 100644
index 0000000..e764aea
--- /dev/null
+++ b/src/JTNE.Protocol/Extensions/JTNEXorExtensions.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Buffers;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+
+namespace JTNE.Protocol.Extensions
+{
+ public static partial class JTNEBinaryExtensions
+ {
+ ///
+ /// 异或
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static byte ToXor(this byte[] buf, int offset, int len)
+ {
+ byte result = buf[offset];
+ for (int i = offset + 1; i < len; i++)
+ {
+ result = (byte)(result ^ buf[i]);
+ }
+ return result;
+ }
+
+ ///
+ /// 异或
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static byte ToXor(this ReadOnlySpan buf, int offset, int len)
+ {
+ byte result = buf[offset];
+ for (int i = offset + 1; i < len; i++)
+ {
+ result = (byte)(result ^ buf[i]);
+ }
+ return result;
+ }
+ }
+}
diff --git a/src/JTNE.Protocol/Formatters/IJTNEFormatterOfT.cs b/src/JTNE.Protocol/Formatters/IJTNEFormatterOfT.cs
new file mode 100644
index 0000000..c772b5f
--- /dev/null
+++ b/src/JTNE.Protocol/Formatters/IJTNEFormatterOfT.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Buffers;
+
+namespace JTNE.Protocol.Formatters
+{
+ public interface IJTNEFormatter
+ {
+ T Deserialize(ReadOnlySpan bytes, out int readSize);
+
+ int Serialize(ref byte[] bytes, int offset, T value);
+ }
+}
diff --git a/src/JTNE.Protocol/Formatters/JTNEPackageFormatter.cs b/src/JTNE.Protocol/Formatters/JTNEPackageFormatter.cs
new file mode 100644
index 0000000..c80e4af
--- /dev/null
+++ b/src/JTNE.Protocol/Formatters/JTNEPackageFormatter.cs
@@ -0,0 +1,43 @@
+using JTNE.Protocol.Enums;
+using JTNE.Protocol.Exceptions;
+using JTNE.Protocol.Extensions;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace JTNE.Protocol.Formatters
+{
+ public class JTNEPackageFormatter : IJTNEFormatter
+ {
+ public JTNEPackage Deserialize(ReadOnlySpan bytes, out int readSize)
+ {
+ int offset = 0;
+ // 1.进行固定头校验
+ if (bytes[offset] != JTNEPackage.BeginFlag && bytes[offset+1] == JTNEPackage.BeginFlag)
+ throw new JTNEException(JTNEErrorCode.BeginFlagError, $"{bytes[offset]},{bytes[offset + 1]}");
+ // 2.进行BCC校验码
+ // 校验位 = 报文长度 - 最后一位(校验位)
+ byte bCCCode = bytes[bytes.Length - 1];
+ byte bCCCode2 = bytes.ToXor(2, bytes.Length - 1);
+ if (bCCCode != bCCCode2)
+ throw new JTNEException(JTNEErrorCode.BCCCodeError, $"request:{bCCCode}!=calculate:{bCCCode2}");
+ JTNEPackage jTNEPackage = new JTNEPackage();
+ offset += 2;
+ jTNEPackage.MsgId = JTNEBinaryExtensions.ReadByteLittle(bytes, ref offset);
+ jTNEPackage.AskId = JTNEBinaryExtensions.ReadByteLittle(bytes, ref offset);
+ jTNEPackage.VIN = JTNEBinaryExtensions.ReadStringLittle(bytes, ref offset,17);
+ jTNEPackage.EncryptMethod = JTNEBinaryExtensions.ReadByteLittle(bytes, ref offset);
+ jTNEPackage.DataUnitLength = JTNEBinaryExtensions.ReadUInt16Little(bytes, ref offset);
+ //todo:解码
+ jTNEPackage.Bodies = JTNEBinaryExtensions.ReadBytesLittle(bytes, ref offset, jTNEPackage.DataUnitLength);
+ jTNEPackage.BCCCode = JTNEBinaryExtensions.ReadByteLittle(bytes, ref offset);
+ readSize = offset;
+ return jTNEPackage;
+ }
+
+ public int Serialize(ref byte[] bytes, int offset, JTNEPackage value)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/JTNE.Protocol/JTNE.Protocol.csproj b/src/JTNE.Protocol/JTNE.Protocol.csproj
index 6c50f01..f77f861 100644
--- a/src/JTNE.Protocol/JTNE.Protocol.csproj
+++ b/src/JTNE.Protocol/JTNE.Protocol.csproj
@@ -18,7 +18,7 @@
- D:\My Project\JTNewEnergy\src\JTNE.Protocol\JTNE.Protocol.xml
+
diff --git a/src/JTNE.Protocol/JTNEArrayPool.cs b/src/JTNE.Protocol/JTNEArrayPool.cs
new file mode 100644
index 0000000..57e1965
--- /dev/null
+++ b/src/JTNE.Protocol/JTNEArrayPool.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Buffers;
+using System.Collections.Generic;
+using System.Text;
+
+namespace JTNE.Protocol
+{
+ internal static class JTNEArrayPool
+ {
+ private readonly static ArrayPool ArrayPool;
+
+ static JTNEArrayPool()
+ {
+ ArrayPool = ArrayPool.Create();
+ }
+
+ public static byte[] Rent(int minimumLength)
+ {
+ return ArrayPool.Rent(minimumLength);
+ }
+
+ public static void Return(byte[] array, bool clearArray = false)
+ {
+ ArrayPool.Return(array, clearArray);
+ }
+ }
+}
diff --git a/src/JTNE.Protocol/JTNEBodies.cs b/src/JTNE.Protocol/JTNEBodies.cs
new file mode 100644
index 0000000..50e4958
--- /dev/null
+++ b/src/JTNE.Protocol/JTNEBodies.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Text;
+
+namespace JTNE.Protocol
+{
+ ///
+ /// 数据体
+ ///
+ public abstract class JTNEBodies
+ {
+ }
+}
diff --git a/src/JTNE.Protocol/JTNEGlobalConfigs.cs b/src/JTNE.Protocol/JTNEGlobalConfigs.cs
new file mode 100644
index 0000000..a0943ab
--- /dev/null
+++ b/src/JTNE.Protocol/JTNEGlobalConfigs.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Text;
+
+namespace JTNE.Protocol
+{
+ ///
+ ///
+ ///
+ public class JTNEGlobalConfigs
+ {
+ private static readonly Lazy instance = new Lazy(() => new JTNEGlobalConfigs());
+
+ private JTNEGlobalConfigs()
+ {
+ Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+ Encoding = Encoding.UTF8;
+ }
+ ///
+ /// 字符串编码
+ ///
+ public Encoding Encoding { get; }
+
+ ///
+ ///
+ ///
+ public static JTNEGlobalConfigs Instance
+ {
+ get
+ {
+ return instance.Value;
+ }
+ }
+ }
+}
diff --git a/src/JTNE.Protocol/JTNEPackage.cs b/src/JTNE.Protocol/JTNEPackage.cs
index df5ff63..30b475a 100644
--- a/src/JTNE.Protocol/JTNEPackage.cs
+++ b/src/JTNE.Protocol/JTNEPackage.cs
@@ -1,4 +1,6 @@
-using System;
+using JTNE.Protocol.Attributes;
+using JTNE.Protocol.Formatters;
+using System;
using System.IO;
namespace JTNE.Protocol
@@ -6,23 +8,31 @@ namespace JTNE.Protocol
///
/// 新能源包
///
+ [JTNEFormatter(typeof(JTNEPackageFormatter))]
public class JTNEPackage
{
///
/// 固定为24个字节长度
///
- public const int HeaderFixedByteLength = 24;
+ //public const int HeaderFixedByteLength = 24;
+
+ public const byte BeginFlag = 0x23;
+ ///
+ /// 起始符1
+ ///
+ public byte BeginFlag1 { get; set; } = 0x23;
///
- /// 起始符
- /// 0x23
+ /// 起始符2
///
- public string BeginFlag { get; set; } = "##";
+ public byte BeginFlag2 { get; set; } = 0x23;
///
/// 命令标识
+ ///
///
public byte MsgId { get; set; }
///
/// 应答标志
+ ///
///
public byte AskId { get; set; }
///
@@ -32,16 +42,17 @@ namespace JTNE.Protocol
///
/// 数据加密方式
/// 0x01:数据不加密;0x02:数据经过 RSA 算法加密;0x03:数据经过 AES128 位算法加密;“0xFE”表示异常,“0xFF”表示无效
+ ///
///
public byte EncryptMethod { get; set; }
///
/// 数据单元长度是数据单元的总字节数,有效值范围:0-65531
///
public ushort DataUnitLength { get; set; }
- /////
- ///// 数据体
- /////
- //public NEBodies Bodies { get; protected set; }
+ ///
+ /// 数据体
+ ///
+ public byte[] Bodies { get; set; }
///
/// 采用BCC(异或检验)法,校验范围从命令单元的第一个字节开始,同后一个字节异或,直到校验码前一个字节为止,
/// 校验码占用一个字节,当数据单元存在加密时,应先加密后检验,先校验后解密
diff --git a/src/JTNE.Protocol/JTNESerializer.cs b/src/JTNE.Protocol/JTNESerializer.cs
new file mode 100644
index 0000000..b5165e5
--- /dev/null
+++ b/src/JTNE.Protocol/JTNESerializer.cs
@@ -0,0 +1,49 @@
+using JTNE.Protocol.Extensions;
+using System;
+
+namespace JTNE.Protocol
+{
+ ///
+ ///
+ ///
+ public static class JTNESerializer
+ {
+ public static byte[] Serialize(JTNEPackage jTNEPackage, int minBufferSize = 1024)
+ {
+ return Serialize(jTNEPackage, minBufferSize);
+ }
+
+ public static JTNEPackage Deserialize(ReadOnlySpan bytes)
+ {
+ return Deserialize(bytes);
+ }
+
+ public static byte[] Serialize(T obj, int minBufferSize = 1024)
+ {
+ var formatter = JTNEFormatterExtensions.GetFormatter();
+ byte[] buffer = JTNEArrayPool.Rent(minBufferSize);
+ try
+ {
+ var len = formatter.Serialize(ref buffer, 0, obj);
+ return buffer.AsSpan(0, len).ToArray();
+ }
+ finally
+ {
+ JTNEArrayPool.Return(buffer);
+ }
+ }
+
+ public static T Deserialize(ReadOnlySpan bytes)
+ {
+ var formatter = JTNEFormatterExtensions.GetFormatter();
+ int readSize;
+ return formatter.Deserialize(bytes,out readSize);
+ }
+
+ public static dynamic Deserialize(ReadOnlySpan bytes,Type type)
+ {
+ var formatter = JTNEFormatterExtensions.GetFormatter(type);
+ return JTNEFormatterResolverExtensions.JTNEDynamicDeserialize(formatter,bytes,out int readSize);
+ }
+ }
+}
diff --git a/src/JTNewEnergy.sln b/src/JTNewEnergy.sln
index 1d97cf2..a2ab150 100644
--- a/src/JTNewEnergy.sln
+++ b/src/JTNewEnergy.sln
@@ -1,9 +1,15 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.28407.52
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.271
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "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
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GBNewEnergy.Protocol", "GBNewEnergy.Protocol\GBNewEnergy.Protocol.csproj", "{2E21E873-F9EA-4B98-80D6-9719DC924350}"
+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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -15,6 +21,18 @@ 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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE