From 58a67ed5e4da58995fd45a12adea0d992acb9e48 Mon Sep 17 00:00:00 2001 From: SmallChi <564952747@qq.com> Date: Mon, 21 Jan 2019 20:10:55 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=95=B4=E6=96=B0=E8=83=BD=E6=BA=90?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../JTNE.Protocol.Test.csproj | 19 ++ src/JTNE.Protocol.Test/JTNEPackageTest.cs | 18 ++ .../Attributes/JTNEBodiesTypeAttribute.cs | 14 ++ .../Attributes/JTNEFormatterAttribute.cs | 25 +++ .../JTNEMsgIdDescriptionAttribute.cs | 20 ++ src/JTNE.Protocol/Enums/JTNEAskId.cs | 29 +++ src/JTNE.Protocol/Enums/JTNEEncryptMethod.cs | 34 +++ src/JTNE.Protocol/Enums/JTNEErrorCode.cs | 26 +++ src/JTNE.Protocol/Enums/JTNEMsgId.cs | 57 +++++ src/JTNE.Protocol/Exceptions/JTNEException.cs | 22 ++ .../Extensions/JTNEBCDExtensions.cs | 45 ++++ .../Extensions/JTNEBinaryExtensions.cs | 120 ++++++++++ .../Extensions/JTNEDateTimeExtensions.cs | 170 ++++++++++++++ .../Extensions/JTNEEnumExtensions.cs | 207 ++++++++++++++++++ .../Extensions/JTNEFormatterExtensions.cs | 50 +++++ .../JTNEFormatterResolverExtensions.cs | 81 +++++++ .../Extensions/JTNEHexExtensions.cs | 130 +++++++++++ .../Extensions/JTNEStringExtensions.cs | 70 ++++++ .../Extensions/JTNEXorExtensions.cs | 45 ++++ .../Formatters/IJTNEFormatterOfT.cs | 12 + .../Formatters/JTNEPackageFormatter.cs | 43 ++++ src/JTNE.Protocol/JTNE.Protocol.csproj | 2 +- src/JTNE.Protocol/JTNEArrayPool.cs | 27 +++ src/JTNE.Protocol/JTNEBodies.cs | 14 ++ src/JTNE.Protocol/JTNEGlobalConfigs.cs | 34 +++ src/JTNE.Protocol/JTNEPackage.cs | 29 ++- src/JTNE.Protocol/JTNESerializer.cs | 49 +++++ src/JTNewEnergy.sln | 24 +- 28 files changed, 1403 insertions(+), 13 deletions(-) create mode 100644 src/JTNE.Protocol.Test/JTNE.Protocol.Test.csproj create mode 100644 src/JTNE.Protocol.Test/JTNEPackageTest.cs create mode 100644 src/JTNE.Protocol/Attributes/JTNEBodiesTypeAttribute.cs create mode 100644 src/JTNE.Protocol/Attributes/JTNEFormatterAttribute.cs create mode 100644 src/JTNE.Protocol/Attributes/JTNEMsgIdDescriptionAttribute.cs create mode 100644 src/JTNE.Protocol/Enums/JTNEAskId.cs create mode 100644 src/JTNE.Protocol/Enums/JTNEEncryptMethod.cs create mode 100644 src/JTNE.Protocol/Enums/JTNEErrorCode.cs create mode 100644 src/JTNE.Protocol/Enums/JTNEMsgId.cs create mode 100644 src/JTNE.Protocol/Exceptions/JTNEException.cs create mode 100644 src/JTNE.Protocol/Extensions/JTNEBCDExtensions.cs create mode 100644 src/JTNE.Protocol/Extensions/JTNEBinaryExtensions.cs create mode 100644 src/JTNE.Protocol/Extensions/JTNEDateTimeExtensions.cs create mode 100644 src/JTNE.Protocol/Extensions/JTNEEnumExtensions.cs create mode 100644 src/JTNE.Protocol/Extensions/JTNEFormatterExtensions.cs create mode 100644 src/JTNE.Protocol/Extensions/JTNEFormatterResolverExtensions.cs create mode 100644 src/JTNE.Protocol/Extensions/JTNEHexExtensions.cs create mode 100644 src/JTNE.Protocol/Extensions/JTNEStringExtensions.cs create mode 100644 src/JTNE.Protocol/Extensions/JTNEXorExtensions.cs create mode 100644 src/JTNE.Protocol/Formatters/IJTNEFormatterOfT.cs create mode 100644 src/JTNE.Protocol/Formatters/JTNEPackageFormatter.cs create mode 100644 src/JTNE.Protocol/JTNEArrayPool.cs create mode 100644 src/JTNE.Protocol/JTNEBodies.cs create mode 100644 src/JTNE.Protocol/JTNEGlobalConfigs.cs create mode 100644 src/JTNE.Protocol/JTNESerializer.cs 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 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netcoreapp2.2</TargetFramework> + + <IsPackable>false</IsPackable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" /> + <PackageReference Include="xunit" Version="2.4.0" /> + <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\JTNE.Protocol\JTNE.Protocol.csproj" /> + </ItemGroup> + +</Project> 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 +{ + /// <summary> + /// 应答标志 + /// </summary> + public enum JTNEAskId:byte + { + /// <summary> + /// 接收到的信息正确 + /// </summary> + Success=0x01, + /// <summary> + /// 设置未成功 + /// </summary> + Error=0x02, + /// <summary> + /// VIN重复错误 + /// </summary> + VinRepeatError=0x03, + /// <summary> + /// 数据包为命令包,而非应答包 + /// </summary> + 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 +{ + /// <summary> + /// 数据单元加密方式 + /// 0x01:数据不加密;0x02:数据经过 RSA 算法加密;0x03:数据经过 AES128 位算法加密;“0xFE”表示异常,“0xFF”表示无效 + /// </summary> + public enum JTNEEncryptMethod:byte + { + /// <summary> + /// 数据不加密 + /// </summary> + None = 0x01, + /// <summary> + /// 数据经过 RSA 算法加密 + /// </summary> + RSA = 0x02, + /// <summary> + /// 数据经过 AES128 位算法加密 + /// </summary> + AES128 = 0x03, + /// <summary> + /// 表示异常 + /// </summary> + Exception = 0xFE, + /// <summary> + /// 表示无效 + /// </summary> + 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 +{ + /// <summary> + /// 错误代码 + /// </summary> + public enum JTNEErrorCode + { + /// <summary> + /// 开始标识错误 + /// </summary> + BeginFlagError = 1001, + /// <summary> + /// BCC校验错误 + /// </summary> + BCCCodeError = 1002, + /// <summary> + /// 没有标记 + /// <see cref="JT808.Protocol.Attributes.JT808FormatterAttribute"/> + /// </summary> + 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 +{ + /// <summary> + /// 命令单元 + /// </summary> + public enum JTNEMsgId:byte + { + /// <summary> + /// 车辆登入 + /// </summary> + login = 0x01, + /// <summary> + /// 实时信息上传 + /// </summary> + uploadim = 0x02, + /// <summary> + /// 补传信息上传 + /// </summary> + uploadsup = 0x03, + /// <summary> + /// 车辆登出 + /// </summary> + loginout = 0x04, + /// <summary> + /// 平台登入 + /// </summary> + platformlogin = 0x05, + /// <summary> + /// 平台登出 + /// </summary> + platformlogout = 0x06, + /// <summary> + /// 心跳 + /// </summary> + heartbeat = 0x07, + /// <summary> + /// 终端校时 + /// </summary> + checktime = 0x08, + /// <summary> + /// 查询命令 + /// </summary> + query = 0x80, + /// <summary> + /// 设置命令 + /// </summary> + settings = 0x81, + /// <summary> + /// 控制命令 + /// </summary> + 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<byte> 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<byte> 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<byte> 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<byte> read, ref int offset) + { + ushort value = (ushort)((read[offset] << 8) | (read[offset + 1])); + offset = offset + 2; + return value; + } + + public static byte ReadByteLittle(ReadOnlySpan<byte> read, ref int offset) + { + byte value = read[offset]; + offset = offset + 1; + return value; + } + + public static byte[] ReadBytesLittle(ReadOnlySpan<byte> read, ref int offset,int len) + { + ReadOnlySpan<byte> temp = read.Slice(offset, len); + offset = offset + len; + return temp.ToArray(); + } + + public static byte[] ReadBytesLittle(ReadOnlySpan<byte> read, ref int offset) + { + ReadOnlySpan<byte> 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<byte> ToBytes(this string data, Encoding coding) + { + return coding.GetBytes(data); + } + + public static IEnumerable<byte> ToBytes(this string data) + { + return ToBytes(data, JTNEGlobalConfigs.Instance.Encoding); + } + + public static IEnumerable<byte> ToBytes(this int data, int len) + { + List<byte> bytes = new List<byte>(); + int n = 1; + for (int i = 0; i < len; i++) + { + bytes.Add((byte)(data >> 8 * (len - n))); + n++; + } + return bytes; + } + + /// <summary> + /// 经纬度 + /// </summary> + /// <param name="latlng"></param> + /// <returns></returns> + 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 + { + /// <summary> + /// 日期限制于2000年 + /// </summary> + private const int DateLimitYear = 2000; + + private static readonly DateTime UTCBaseTime = new DateTime(1970, 1, 1); + + /// <summary> + /// + /// </summary> + /// <param name="buf"></param> + /// <param name="offset"></param> + /// <param name="format">D2: 10 X2:16</param> + /// <returns></returns> + public static DateTime ReadDateTime6Little(ReadOnlySpan<byte> 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; + } + + /// <summary> + /// + /// </summary> + /// <param name="buf"></param> + /// <param name="offset"></param> + /// <param name="format">D2: 10 X2:16</param> + /// <returns></returns> + public static DateTime ReadDateTime4Little(ReadOnlySpan<byte> 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<byte> 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); + } + /// <summary> + /// hh-mm-ss-msms + /// hh-mm-ss-fff + /// </summary> + /// <param name="buf"></param> + /// <param name="offset"></param> + /// <param name="format"></param> + /// <returns></returns> + public static DateTime ReadDateTime5Little(ReadOnlySpan<byte> 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; + } + + /// <summary> + /// + /// </summary> + /// <param name="memoryOwner"></param> + /// <param name="offset"></param> + /// <param name="date"></param> + /// <param name="fromBase">BCD:10 HEX:16</param> + /// <returns></returns> + 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; + } + + /// <summary> + /// YYYYMMDD + /// </summary> + /// <param name="bytes"></param> + /// <param name="offset"></param> + /// <param name="date"></param> + /// <param name="fromBase">BCD:10 HEX:16</param> + /// <returns></returns> + 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; + } + /// <summary> + /// hh-mm-ss-msms + /// hh-mm-ss-fff + /// </summary> + /// <param name="bytes"></param> + /// <param name="offset"></param> + /// <param name="date"></param> + /// <param name="fromBase"></param> + /// <returns></returns> + 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 +{ + /// <summary> + /// 枚举扩展 + /// </summary> + public static class JT808EnumExtensions + { + /// <summary> + /// 转为整型 + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="t"></param> + /// <returns></returns> + public static int ToValue<T>(this T t) where T : struct + { + return Convert.ToInt32(t); + } + + /// <summary> + /// 转为 u16 整型 + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="t"></param> + /// <returns></returns> + public static ushort ToUInt16Value<T>(this T t) where T : struct + { + return Convert.ToUInt16(t); + } + + /// <summary> + /// 转为Byte + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="t"></param> + /// <returns></returns> + public static byte ToByteValue<T>(this T t) where T : struct + { + return Convert.ToByte(t); + } + + /// <summary> + /// 转为整型 + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="t"></param> + /// <returns></returns> + public static string ToValueString<T>(this T t) where T : struct + { + return Convert.ToInt32(t).ToString(); + } + + /// <summary> + /// 字符转枚举 + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="value"></param> + /// <returns></returns> + public static T ToEnum<T>(this string value) where T : struct + { + return (T)Enum.Parse(typeof(T), value); + } + + /// <summary> + /// 获取枚举字符串 + /// </summary> + /// <param name="valueEnum"></param> + public static string GetName(this Enum valueEnum) + { + return valueEnum.ToString(); + } + + /// <summary> + /// 获取DescriptionAttribute特性枚举值的描述 + /// </summary> + /// <param name="value"></param> + /// <returns></returns> + public static string GetDescription(this Enum value) + { + var attribute = value.GetAttribute<DescriptionAttribute>(); + return attribute == null ? value.ToString() : attribute.Description; + } + + /// <summary> + /// 验证是否是枚举类型 + /// </summary> + /// <typeparam name="TEnum"></typeparam> + /// <param name="enumValue"></param> + /// <returns></returns> + public static bool IsEnumValid<TEnum>(this int enumValue) + { + return Enum.IsDefined(typeof(TEnum), enumValue); + } + + /// <summary> + /// 获取DescriptionAttribute特性枚举及描述 + /// </summary> + /// <param name="type"></param> + /// <returns></returns> + public static Dictionary<string, string> GetDescriptionAttributeDictionary(this Enum value) + { + Dictionary<string, string> dictionary = new Dictionary<string, string>(); + 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; + } + + /// <summary> + /// 获取DisplayNameAttribute特性枚举值的描述 + /// </summary> + /// <param name="obj">枚举值</param> + /// <returns></returns> + public static string GetDisplayName(this Enum value) + { + var attribute = value.GetAttribute<DisplayNameAttribute>(); + return attribute == null ? value.ToString() : attribute.DisplayName; + } + + /// <summary> + /// 获取DisplayNameAttribute特性枚举及描述 + /// </summary> + /// <param name="type"></param> + /// <returns></returns> + public static Dictionary<string, string> GetDisplayNameAttributeDictionary(this Enum value) + { + Dictionary<string, string> dictionary = new Dictionary<string, string>(); + 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; + } + + /// <summary> + /// 获取枚举对应特性 + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="value"></param> + /// <returns></returns> + public static T GetAttribute<T>(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; + } + } + + /// <summary> + /// 根据值获取对应枚举类型集合 + /// </summary> + /// <typeparam name="T">具体枚举类型</typeparam> + /// <param name="value">枚举值</param> + /// <param name="digit">位数(8,16,32)</param> + /// <param name="ignoreUnknown">是否忽略未知数据</param> + /// <returns></returns> + public static IEnumerable<T> GetEnumTypes<T>(this int value, int digit, bool ignoreUnknown=false) where T : Enum + { + List<T> values = new List<T>(); + 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<T> results = new List<T>(); + 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<T> GetFormatter<T>() + { + IJTNEFormatter<T> formatter; + var attr = typeof(T).GetTypeInfo().GetCustomAttribute<JTNEFormatterAttribute>(); + if (attr == null) + { + return default; + } + if (attr.Arguments == null) + { + formatter = (IJTNEFormatter<T>)Activator.CreateInstance(attr.FormatterType); + } + else + { + formatter = (IJTNEFormatter<T>)Activator.CreateInstance(attr.FormatterType, attr.Arguments); + } + return formatter; + } + + public static object GetFormatter(Type formatterType) + { + object formatter; + var attr = formatterType.GetTypeInfo().GetCustomAttribute<JTNEFormatterAttribute>(); + 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 +{ + /// <summary> + /// + /// ref http://adamsitnik.com/Span/#span-must-not-be-a-generic-type-argument + /// ref http://adamsitnik.com/Span/ + /// ref:MessagePack.Formatters.DynamicObjectTypeFallbackFormatter + /// </summary> + public static class JTNEFormatterResolverExtensions + { + delegate int JTNESerializeMethod(object dynamicFormatter, ref byte[] bytes, int offset, object value); + + delegate dynamic JTNEDeserializeMethod(object dynamicFormatter, ReadOnlySpan<byte> bytes, out int readSize); + + static readonly ConcurrentDictionary<Type, (object Value, JTNESerializeMethod SerializeMethod)> jTNESerializers = new ConcurrentDictionary<Type, (object Value, JTNESerializeMethod SerializeMethod)>(); + + static readonly ConcurrentDictionary<Type, (object Value, JTNEDeserializeMethod DeserializeMethod)> jTNEDeserializes = new ConcurrentDictionary<Type, (object Value, JTNEDeserializeMethod DeserializeMethod)>(); + + 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<JTNESerializeMethod>(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<byte> 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<byte>), "bytes"); + ParameterExpression param2 = Expression.Parameter(typeof(int).MakeByRefType(), "readSize"); + var deserializeMethodInfo = type.GetRuntimeMethod("Deserialize", new[] { typeof(ReadOnlySpan<byte>), typeof(int).MakeByRefType() }); + var body = Expression.Call( + Expression.Convert(param0, type), + deserializeMethodInfo, + param1, + param2 + ); + var lambda = Expression.Lambda<JTNEDeserializeMethod>(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 +{ + /// <summary> + /// + /// ref:"www.codeproject.com/tips/447938/high-performance-csharp-byte-array-to-hex-string-t" + /// </summary> + 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; + } + + /// <summary> + /// 16进制字符串转16进制数组 + /// </summary> + /// <param name="hexString"></param> + /// <param name="separator"></param> + /// <returns></returns> + public static byte[] ToHexBytes(this string hexString) + { + hexString = hexString.Replace(" ", ""); + byte[] buf = new byte[hexString.Length / 2]; + ReadOnlySpan<char> 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<byte> read, ref int offset, int len) + { + ReadOnlySpan<byte> source = read.Slice(offset, len); + string hex = HexUtil.DoHexDump(read, offset, len); + offset += len; + return hex; + } + + /// <summary> + /// ref dotnetty + /// </summary> + 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<byte> 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<byte> 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<byte> 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 + { + /// <summary> + /// 异或 + /// </summary> + /// <param name="buf"></param> + /// <param name="offset"></param> + /// <param name="len"></param> + /// <returns></returns> + 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; + } + + /// <summary> + /// 异或 + /// </summary> + /// <param name="buf"></param> + /// <param name="offset"></param> + /// <param name="len"></param> + /// <returns></returns> + public static byte ToXor(this ReadOnlySpan<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; + } + } +} 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> + { + T Deserialize(ReadOnlySpan<byte> 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<JTNEPackage> + { + public JTNEPackage Deserialize(ReadOnlySpan<byte> 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 @@ </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> - <DocumentationFile>D:\My Project\JTNewEnergy\src\JTNE.Protocol\JTNE.Protocol.xml</DocumentationFile> + <DocumentationFile></DocumentationFile> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> 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<byte> ArrayPool; + + static JTNEArrayPool() + { + ArrayPool = ArrayPool<byte>.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 +{ + /// <summary> + /// 数据体 + /// </summary> + 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 +{ + /// <summary> + /// + /// </summary> + public class JTNEGlobalConfigs + { + private static readonly Lazy<JTNEGlobalConfigs> instance = new Lazy<JTNEGlobalConfigs>(() => new JTNEGlobalConfigs()); + + private JTNEGlobalConfigs() + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + Encoding = Encoding.UTF8; + } + /// <summary> + /// 字符串编码 + /// </summary> + public Encoding Encoding { get; } + + /// <summary> + /// + /// </summary> + 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 /// <summary> /// 新能源包 /// </summary> + [JTNEFormatter(typeof(JTNEPackageFormatter))] public class JTNEPackage { /// <summary> /// 固定为24个字节长度 /// </summary> - public const int HeaderFixedByteLength = 24; + //public const int HeaderFixedByteLength = 24; + + public const byte BeginFlag = 0x23; + /// <summary> + /// 起始符1 + /// </summary> + public byte BeginFlag1 { get; set; } = 0x23; /// <summary> - /// 起始符 - /// 0x23 + /// 起始符2 /// </summary> - public string BeginFlag { get; set; } = "##"; + public byte BeginFlag2 { get; set; } = 0x23; /// <summary> /// 命令标识 + /// <see cref="JTNE.Protocol.Enums.JTNEMsgId"/> /// </summary> public byte MsgId { get; set; } /// <summary> /// 应答标志 + /// <see cref="JTNE.Protocol.Enums.JTNEAskId"/> /// </summary> public byte AskId { get; set; } /// <summary> @@ -32,16 +42,17 @@ namespace JTNE.Protocol /// <summary> /// 数据加密方式 /// 0x01:数据不加密;0x02:数据经过 RSA 算法加密;0x03:数据经过 AES128 位算法加密;“0xFE”表示异常,“0xFF”表示无效 + /// <see cref="JTNE.Protocol.Enums.JTNEEncryptMethod"/> /// </summary> public byte EncryptMethod { get; set; } /// <summary> /// 数据单元长度是数据单元的总字节数,有效值范围:0-65531 /// </summary> public ushort DataUnitLength { get; set; } - ///// <summary> - ///// 数据体 - ///// </summary> - //public NEBodies Bodies { get; protected set; } + /// <summary> + /// 数据体 + /// </summary> + public byte[] Bodies { get; set; } /// <summary> /// 采用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 +{ + /// <summary> + /// + /// </summary> + public static class JTNESerializer + { + public static byte[] Serialize(JTNEPackage jTNEPackage, int minBufferSize = 1024) + { + return Serialize<JTNEPackage>(jTNEPackage, minBufferSize); + } + + public static JTNEPackage Deserialize(ReadOnlySpan<byte> bytes) + { + return Deserialize<JTNEPackage>(bytes); + } + + public static byte[] Serialize<T>(T obj, int minBufferSize = 1024) + { + var formatter = JTNEFormatterExtensions.GetFormatter<T>(); + 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<T>(ReadOnlySpan<byte> bytes) + { + var formatter = JTNEFormatterExtensions.GetFormatter<T>(); + int readSize; + return formatter.Deserialize(bytes,out readSize); + } + + public static dynamic Deserialize(ReadOnlySpan<byte> 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