@@ -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> | ||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | ||||
<DocumentationFile>D:\My Project\JTNewEnergy\src\JTNE.Protocol\JTNE.Protocol.xml</DocumentationFile> | |||||
<DocumentationFile></DocumentationFile> | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> | <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; | using System.IO; | ||||
namespace JTNE.Protocol | namespace JTNE.Protocol | ||||
@@ -6,23 +8,31 @@ namespace JTNE.Protocol | |||||
/// <summary> | /// <summary> | ||||
/// 新能源包 | /// 新能源包 | ||||
/// </summary> | /// </summary> | ||||
[JTNEFormatter(typeof(JTNEPackageFormatter))] | |||||
public class JTNEPackage | public class JTNEPackage | ||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// 固定为24个字节长度 | /// 固定为24个字节长度 | ||||
/// </summary> | /// </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> | /// <summary> | ||||
/// 起始符 | |||||
/// 0x23 | |||||
/// 起始符2 | |||||
/// </summary> | /// </summary> | ||||
public string BeginFlag { get; set; } = "##"; | |||||
public byte BeginFlag2 { get; set; } = 0x23; | |||||
/// <summary> | /// <summary> | ||||
/// 命令标识 | /// 命令标识 | ||||
/// <see cref="JTNE.Protocol.Enums.JTNEMsgId"/> | |||||
/// </summary> | /// </summary> | ||||
public byte MsgId { get; set; } | public byte MsgId { get; set; } | ||||
/// <summary> | /// <summary> | ||||
/// 应答标志 | /// 应答标志 | ||||
/// <see cref="JTNE.Protocol.Enums.JTNEAskId"/> | |||||
/// </summary> | /// </summary> | ||||
public byte AskId { get; set; } | public byte AskId { get; set; } | ||||
/// <summary> | /// <summary> | ||||
@@ -32,16 +42,17 @@ namespace JTNE.Protocol | |||||
/// <summary> | /// <summary> | ||||
/// 数据加密方式 | /// 数据加密方式 | ||||
/// 0x01:数据不加密;0x02:数据经过 RSA 算法加密;0x03:数据经过 AES128 位算法加密;“0xFE”表示异常,“0xFF”表示无效 | /// 0x01:数据不加密;0x02:数据经过 RSA 算法加密;0x03:数据经过 AES128 位算法加密;“0xFE”表示异常,“0xFF”表示无效 | ||||
/// <see cref="JTNE.Protocol.Enums.JTNEEncryptMethod"/> | |||||
/// </summary> | /// </summary> | ||||
public byte EncryptMethod { get; set; } | public byte EncryptMethod { get; set; } | ||||
/// <summary> | /// <summary> | ||||
/// 数据单元长度是数据单元的总字节数,有效值范围:0-65531 | /// 数据单元长度是数据单元的总字节数,有效值范围:0-65531 | ||||
/// </summary> | /// </summary> | ||||
public ushort DataUnitLength { get; set; } | public ushort DataUnitLength { get; set; } | ||||
///// <summary> | |||||
///// 数据体 | |||||
///// </summary> | |||||
//public NEBodies Bodies { get; protected set; } | |||||
/// <summary> | |||||
/// 数据体 | |||||
/// </summary> | |||||
public byte[] Bodies { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// 采用BCC(异或检验)法,校验范围从命令单元的第一个字节开始,同后一个字节异或,直到校验码前一个字节为止, | /// 采用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 | 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 | 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 | EndProject | ||||
Global | Global | ||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | 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}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
{5B164F58-141D-474E-A82E-672A1C252029}.Release|Any CPU.ActiveCfg = Release|Any CPU | {5B164F58-141D-474E-A82E-672A1C252029}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
{5B164F58-141D-474E-A82E-672A1C252029}.Release|Any CPU.Build.0 = Release|Any CPU | {5B164F58-141D-474E-A82E-672A1C252029}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
{2E21E873-F9EA-4B98-80D6-9719DC924350}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{2E21E873-F9EA-4B98-80D6-9719DC924350}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{2E21E873-F9EA-4B98-80D6-9719DC924350}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{2E21E873-F9EA-4B98-80D6-9719DC924350}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{30BB532E-4E49-4BB4-A1D4-61DC40F320DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{30BB532E-4E49-4BB4-A1D4-61DC40F320DB}.Debug|Any CPU.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 | EndGlobalSection | ||||
GlobalSection(SolutionProperties) = preSolution | GlobalSection(SolutionProperties) = preSolution | ||||
HideSolutionNode = FALSE | HideSolutionNode = FALSE | ||||