@@ -12,7 +12,7 @@ jobs: | |||||
- name: Setup .NET Core | - name: Setup .NET Core | ||||
uses: actions/setup-dotnet@master | uses: actions/setup-dotnet@master | ||||
with: | with: | ||||
dotnet-version: 3.1.101 | |||||
dotnet-version: 3.1.401 | |||||
- name: dotnet info | - name: dotnet info | ||||
run: dotnet --info | run: dotnet --info | ||||
- name: dotnet restore | - name: dotnet restore | ||||
@@ -7,14 +7,13 @@ JTActiveSafety协议、道路运输车辆主动安全智能防控系统-主动 | |||||
[](https://github.com/SmallChi/JTActiveSafety/blob/master/LICENSE) | [](https://github.com/SmallChi/JTActiveSafety/blob/master/LICENSE) | ||||
## 基于JT808扩展的JTActiveSafety消息协议 | |||||
## NuGet安装 | ## NuGet安装 | ||||
| Package Name | Version | Downloads | | |||||
| --------------------- | -------------------------------------------------- | --------------------------------------------------- | | |||||
| Install-Package JT808 |  |  | | |||||
| Install-Package JT808.Protocol.Extensions.JTActiveSafety|  |  | | |||||
| Package Name | Version | Downloads| Remark | | |||||
| --------------------- | -------------------------------------------------- | --------------------------------------------------- |--------------------------------------------------- | | |||||
| Install-Package JTActiveSafety|  |  |主动安全的附件协议| | |||||
| Install-Package JT808 |  |  |基础JT808协议| | |||||
| Install-Package JT808.Protocol.Extensions.JTActiveSafety|  |  |基于JT808扩展的JTActiveSafety消息协议| | |||||
### JT808扩展协议消息对照表 | ### JT808扩展协议消息对照表 | ||||
@@ -7,10 +7,10 @@ | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.3" /> | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.8" /> | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" /> | |||||
<PackageReference Include="xunit" Version="2.4.1" /> | <PackageReference Include="xunit" Version="2.4.1" /> | ||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1"> | |||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> | |||||
<PrivateAssets>all</PrivateAssets> | <PrivateAssets>all</PrivateAssets> | ||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
</PackageReference> | </PackageReference> | ||||
@@ -15,7 +15,7 @@ | |||||
<licenseUrl>https://github.com/SmallChi/JTActiveSafety/blob/master/LICENSE</licenseUrl> | <licenseUrl>https://github.com/SmallChi/JTActiveSafety/blob/master/LICENSE</licenseUrl> | ||||
<license>https://github.com/SmallChi/JTActiveSafety/blob/master/LICENSE</license> | <license>https://github.com/SmallChi/JTActiveSafety/blob/master/LICENSE</license> | ||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | <GeneratePackageOnBuild>false</GeneratePackageOnBuild> | ||||
<Version>1.0.4</Version> | |||||
<Version>1.0.5</Version> | |||||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | <PackageLicenseFile>LICENSE</PackageLicenseFile> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
@@ -35,7 +35,7 @@ | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="JT808" Version="2.2.10" /> | |||||
<PackageReference Include="JT808" Version="2.2.12" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
@@ -7,9 +7,9 @@ | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" /> | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" /> | |||||
<PackageReference Include="xunit" Version="2.4.1" /> | <PackageReference Include="xunit" Version="2.4.1" /> | ||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1"> | |||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> | |||||
<PrivateAssets>all</PrivateAssets> | <PrivateAssets>all</PrivateAssets> | ||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
</PackageReference> | </PackageReference> | ||||
@@ -0,0 +1,172 @@ | |||||
using System; | |||||
namespace JTActiveSafety.Protocol.Extensions | |||||
{ | |||||
/// <summary> | |||||
/// | |||||
/// ref:"www.codeproject.com/tips/447938/high-performance-csharp-byte-array-to-hex-string-t" | |||||
/// </summary> | |||||
public static partial class HexExtensions | |||||
{ | |||||
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; | |||||
} | |||||
public static string ReadNumber(this byte value, string format = "X2") | |||||
{ | |||||
return value.ToString(format); | |||||
} | |||||
public static string ReadNumber(this int value, string format = "X8") | |||||
{ | |||||
return value.ToString(format); | |||||
} | |||||
public static string ReadNumber(this uint value, string format = "X8") | |||||
{ | |||||
return value.ToString(format); | |||||
} | |||||
public static string ReadNumber(this long value, string format = "X16") | |||||
{ | |||||
return value.ToString(format); | |||||
} | |||||
public static string ReadNumber(this ulong value, string format = "X16") | |||||
{ | |||||
return value.ToString(format); | |||||
} | |||||
public static string ReadNumber(this short value, string format = "X4") | |||||
{ | |||||
return value.ToString(format); | |||||
} | |||||
public static string ReadNumber(this ushort value, string format = "X4") | |||||
{ | |||||
return value.ToString(format); | |||||
} | |||||
public static ReadOnlySpan<char> ReadBinary(this ushort value) | |||||
{ | |||||
return System.Convert.ToString(value, 2).PadLeft(16, '0').AsSpan(); | |||||
} | |||||
public static ReadOnlySpan<char> ReadBinary(this short value) | |||||
{ | |||||
return System.Convert.ToString(value, 2).PadLeft(16, '0').AsSpan(); | |||||
} | |||||
public static ReadOnlySpan<char> ReadBinary(this uint value) | |||||
{ | |||||
return System.Convert.ToString(value, 2).PadLeft(32, '0').AsSpan(); | |||||
} | |||||
public static ReadOnlySpan<char> ReadBinary(this int value) | |||||
{ | |||||
return System.Convert.ToString(value, 2).PadLeft(32, '0').AsSpan(); | |||||
} | |||||
public static ReadOnlySpan<char> ReadBinary(this byte value) | |||||
{ | |||||
return System.Convert.ToString(value, 2).PadLeft(8, '0').AsSpan(); | |||||
} | |||||
} | |||||
public 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); | |||||
} | |||||
} | |||||
} |
@@ -19,7 +19,10 @@ | |||||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | <PackageLicenseFile>LICENSE</PackageLicenseFile> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' "> | <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' "> | ||||
<PackageReference Include="System.Memory" Version="4.5.3" /> | |||||
<PackageReference Include="System.Memory" Version="4.5.4" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="System.Text.Json" Version="4.7.2" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<None Include="..\..\LICENSE"> | <None Include="..\..\LICENSE"> | ||||
@@ -1,14 +1,18 @@ | |||||
using JTActiveSafety.Protocol.Buffers; | using JTActiveSafety.Protocol.Buffers; | ||||
using JTActiveSafety.Protocol.Extensions; | |||||
using JTActiveSafety.Protocol.MessagePack; | using JTActiveSafety.Protocol.MessagePack; | ||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Text; | using System.Text; | ||||
using System.Text.Json; | |||||
namespace JTActiveSafety.Protocol | namespace JTActiveSafety.Protocol | ||||
{ | { | ||||
public static class JTActiveSafetySerializer | public static class JTActiveSafetySerializer | ||||
{ | { | ||||
public static byte[] Serialize(JTActiveSafetyPackage package, int minBufferSize = 4096) | |||||
public static byte[] Serialize(JTActiveSafetyPackage package, int minBufferSize = 65 * 1024) | |||||
{ | { | ||||
byte[] buffer = JTActiveSafetyArrayPool.Rent(minBufferSize); | byte[] buffer = JTActiveSafetyArrayPool.Rent(minBufferSize); | ||||
try | try | ||||
@@ -17,7 +21,7 @@ namespace JTActiveSafety.Protocol | |||||
writer.WriteUInt32(package.FH_Flag); | writer.WriteUInt32(package.FH_Flag); | ||||
writer.WriteString(package.FileName); | writer.WriteString(package.FileName); | ||||
writer.WriteUInt32(package.Offset); | writer.WriteUInt32(package.Offset); | ||||
writer.WriteUInt32(package.Length); | |||||
writer.WriteUInt32((uint)package.Bodies.Length); | |||||
writer.WriteArray(package.Bodies); | writer.WriteArray(package.Bodies); | ||||
return writer.FlushAndGetArray(); | return writer.FlushAndGetArray(); | ||||
} | } | ||||
@@ -38,5 +42,33 @@ namespace JTActiveSafety.Protocol | |||||
jTActiveSafetyPackage.Bodies = reader.ReadRemainArray().ToArray(); | jTActiveSafetyPackage.Bodies = reader.ReadRemainArray().ToArray(); | ||||
return jTActiveSafetyPackage; | return jTActiveSafetyPackage; | ||||
} | } | ||||
public static byte[] AnalyzeJsonBuffer(ReadOnlySpan<byte> bytes, JsonWriterOptions options = default) | |||||
{ | |||||
JTActiveSafetyMessagePackReader reader = new JTActiveSafetyMessagePackReader(bytes); | |||||
using (MemoryStream memoryStream = new MemoryStream()) | |||||
using (Utf8JsonWriter writer = new Utf8JsonWriter(memoryStream, options)) | |||||
{ | |||||
writer.WriteStartObject(); | |||||
var header = reader.ReadUInt32(); | |||||
writer.WriteString($"[{header}]头部", header.ReadNumber()); | |||||
var FileName = reader.ReadString(50); | |||||
writer.WriteString($"[文件名称]", FileName); | |||||
var offset = reader.ReadUInt32(); | |||||
writer.WriteNumber($"{offset}[数据偏移量]", offset); | |||||
var length = reader.ReadUInt32(); | |||||
writer.WriteNumber($"{length}[数据长度]", length); | |||||
var bodies = reader.ReadRemainArray().ToArray(); | |||||
writer.WriteString("[数据体]", string.Join(" ", (bodies).Select(p => p.ToString("X2")))); | |||||
writer.WriteEndObject(); | |||||
writer.Flush(); | |||||
return memoryStream.ToArray(); | |||||
} | |||||
} | |||||
public static string Analyze(ReadOnlySpan<byte> bytes, JsonWriterOptions options = default) | |||||
{ | |||||
string json = Encoding.UTF8.GetString(AnalyzeJsonBuffer(bytes, options)); | |||||
return json; | |||||
} | |||||
} | } | ||||
} | } |