@@ -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> |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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;} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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 | |||
} | |||
} |
@@ -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 | |||
} | |||
} |
@@ -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, | |||
} | |||
} |
@@ -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 | |||
} | |||
} |
@@ -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; } | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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'"> | |||
@@ -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); | |||
} | |||
} | |||
} |
@@ -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 | |||
{ | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} | |||
} |
@@ -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(异或检验)法,校验范围从命令单元的第一个字节开始,同后一个字节异或,直到校验码前一个字节为止, | |||
/// 校验码占用一个字节,当数据单元存在加密时,应先加密后检验,先校验后解密 | |||
@@ -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); | |||
} | |||
} | |||
} |
@@ -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 | |||