You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
476 lines
16 KiB
476 lines
16 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace AIK.Common.AIKProtocol
|
|
{
|
|
public static class K3ToolUSBProtocol
|
|
{
|
|
private static int _totalPackets;
|
|
private static byte[]? _fileData;
|
|
private static byte[]? _endEOTData;
|
|
private static byte[]? _connectData;
|
|
private static byte[]? _createRemoteData;
|
|
private static byte[]? _closetheUdiskData;
|
|
private static byte[]? _opentheUdiskData;
|
|
private static readonly object _lockObject = new object();
|
|
public static readonly string ConnectFileName = "usb connect.bin";
|
|
public static readonly string CreateRemoteFileName = "usb create remote.bin";
|
|
public static int TotalPackets
|
|
{
|
|
get => _totalPackets;
|
|
set => _totalPackets = value;
|
|
}
|
|
|
|
public static byte[] FileData
|
|
{
|
|
get => _fileData ?? Array.Empty<byte>();
|
|
set
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
_fileData = value;
|
|
// 当 FileData 不为空时,自动计算 TotalPackets
|
|
if (_fileData != null && _fileData.Length > 0)
|
|
{
|
|
_totalPackets = (int)Math.Ceiling(_fileData.Length / 1024.0);
|
|
}
|
|
else
|
|
{
|
|
_totalPackets = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static byte[] EndEOTData
|
|
{
|
|
get
|
|
{
|
|
if (_endEOTData == null || _endEOTData.Length == 0)
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
// 双重检查锁定
|
|
if (_endEOTData == null || _endEOTData.Length == 0)
|
|
{
|
|
_endEOTData = GetBinData(ResourceEOTName);
|
|
}
|
|
}
|
|
}
|
|
return _endEOTData;
|
|
}
|
|
set => _endEOTData = value;
|
|
}
|
|
|
|
public static byte[] ConnectData
|
|
{
|
|
get
|
|
{
|
|
if (_connectData == null || _connectData.Length == 0)
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
// 双重检查锁定
|
|
if (_connectData == null || _connectData.Length == 0)
|
|
{
|
|
_connectData = GetBinData(ResourceConnectName);
|
|
}
|
|
}
|
|
}
|
|
return _connectData;
|
|
}
|
|
set => _connectData = value;
|
|
}
|
|
|
|
public static byte[] CreateRemoteData
|
|
{
|
|
get
|
|
{
|
|
if (_createRemoteData == null || _createRemoteData.Length == 0)
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
// 双重检查锁定
|
|
if (_createRemoteData == null || _createRemoteData.Length == 0)
|
|
{
|
|
_createRemoteData = GetBinData(ResourceCreateRemoteName);
|
|
}
|
|
}
|
|
}
|
|
return _createRemoteData;
|
|
}
|
|
set => _createRemoteData = value;
|
|
}
|
|
|
|
public static byte[] ClosetheUdiskData
|
|
{
|
|
get
|
|
{
|
|
if (_closetheUdiskData == null || _closetheUdiskData.Length == 0)
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
// 双重检查锁定
|
|
if (_closetheUdiskData == null || _closetheUdiskData.Length == 0)
|
|
{
|
|
_closetheUdiskData = GetBinData(ResourceClosetheUdiskName);
|
|
}
|
|
}
|
|
}
|
|
return _closetheUdiskData;
|
|
}
|
|
set => _closetheUdiskData = value;
|
|
}
|
|
|
|
public static byte[] OpentheUdiskData
|
|
{
|
|
get
|
|
{
|
|
if (_opentheUdiskData == null || _opentheUdiskData.Length == 0)
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
// 双重检查锁定
|
|
if (_opentheUdiskData == null || _opentheUdiskData.Length == 0)
|
|
{
|
|
_opentheUdiskData = GetBinData(ResourceOpentheUdiskName);
|
|
}
|
|
}
|
|
}
|
|
return _opentheUdiskData;
|
|
}
|
|
set => _opentheUdiskData = value;
|
|
}
|
|
|
|
public static string ResourceEOTName { get; set; } = "AIK.Common.Resources.eot.bin";
|
|
public static string ResourceConnectName { get; set; } = "AIK.Common.Resources.usb connect.bin";
|
|
public static string ResourceCreateRemoteName { get; set; } = "AIK.Common.Resources.usb create remote.bin";
|
|
|
|
public static string ResourceClosetheUdiskName { get; set; } = "AIK.Common.Resources.Close the U disk.bin";
|
|
|
|
public static string ResourceOpentheUdiskName { get; set; } = "AIK.Common.Resources.Open the U disk.bin";
|
|
#region 头帧
|
|
public static byte[] GetSendHeader(string sourceitem)
|
|
{
|
|
// 保持原有实现
|
|
List<byte> bytes = new List<byte>();
|
|
bytes.Add(0x01);
|
|
bytes.Add(0x00);
|
|
bytes.Add(0xFF);
|
|
|
|
int lastSlashIndex = sourceitem.LastIndexOf('/');
|
|
string fileName = lastSlashIndex >= 0 ? sourceitem.Substring(lastSlashIndex + 1) : sourceitem;
|
|
byte[] filenamedata = Encoding.GetEncoding(936).GetBytes(fileName);
|
|
bytes.AddRange(filenamedata);
|
|
bytes.Add(0x00);
|
|
|
|
if (FileData == null) return new byte[0];
|
|
|
|
TotalPackets = (int)Math.Ceiling(FileData.Length / 1024.0);
|
|
byte[] dataLengthData = Encoding.GetEncoding(936).GetBytes(FileData.Length.ToString());
|
|
bytes.AddRange(dataLengthData);
|
|
bytes.Add(0x00);
|
|
|
|
// 填充到128字节
|
|
if (bytes.Count < 128 + 3)
|
|
{
|
|
bytes.AddRange(new byte[128 + 3 - bytes.Count]);
|
|
}
|
|
|
|
var cdata = new byte[128];
|
|
Array.Copy(bytes.ToArray(), 3, cdata, 0, 128);
|
|
var crc = ProtocolHelper.CalculateCRC16(cdata);
|
|
bytes.Add((byte)((crc >> 8) & 0xFF));
|
|
bytes.Add((byte)(crc & 0xFF));
|
|
|
|
return bytes.ToArray();
|
|
}
|
|
#endregion
|
|
|
|
#region 数据帧
|
|
public static List<byte> GetSendDataFrame(int page, byte[] filedata)
|
|
{
|
|
try
|
|
{
|
|
var bytes = new List<byte>();
|
|
if (page >= TotalPackets) return bytes;
|
|
|
|
bytes.Add(0x02);
|
|
bytes.Add((byte)(page + 1));
|
|
bytes.Add((byte)~(page + 1));
|
|
|
|
int dataStart = page * 1024;
|
|
int dataLength = Math.Min(1024, filedata.Length - dataStart);
|
|
var frameData = new byte[1024];
|
|
Array.Copy(filedata, dataStart, frameData, 0, dataLength);
|
|
|
|
var crc = ProtocolHelper.CalculateCRC16(frameData);
|
|
bytes.AddRange(frameData);
|
|
bytes.Add((byte)((crc >> 8) & 0xFF));
|
|
bytes.Add((byte)(crc & 0xFF));
|
|
|
|
return bytes;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"生成数据帧错误: {ex.Message}");
|
|
return new List<byte>();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region 资源文件帧
|
|
/// <summary>
|
|
/// 获取bin文件转成byte数组
|
|
/// </summary>
|
|
/// <param name="resourceName"></param>
|
|
/// <returns></returns>
|
|
private static byte[] GetBinData(string resourceName)
|
|
{
|
|
try
|
|
{
|
|
var assembly = Assembly.GetExecutingAssembly();
|
|
|
|
// 查看所有资源名称(调试用)
|
|
// var names = assembly.GetManifestResourceNames();
|
|
|
|
using var stream = assembly.GetManifestResourceStream(resourceName);
|
|
if (stream == null)
|
|
throw new FileNotFoundException($"未找到嵌入资源: {resourceName}");
|
|
using var ms = new MemoryStream();
|
|
stream.CopyTo(ms);
|
|
return ms.ToArray();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Array.Empty<byte>();
|
|
}
|
|
|
|
}
|
|
|
|
#region Connect帧
|
|
/// <summary>
|
|
/// 手动刷新 Connect 数据(强制重新加载)
|
|
/// </summary>
|
|
public static void RefreshConnectData()
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
_connectData = GetBinData(ResourceConnectName);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 清除缓存的 Connect 数据(下次访问时会重新加载)
|
|
/// </summary>
|
|
public static void ClearConnectDataCache()
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
_connectData = null;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region CreateRemote帧
|
|
/// <summary>
|
|
/// 手动刷新 CreateRemote 数据(强制重新加载)
|
|
/// </summary>
|
|
public static void RefreshCreateRemoteData()
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
_createRemoteData = GetBinData(ResourceCreateRemoteName);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 清除缓存的 CreateRemote数据(下次访问时会重新加载)
|
|
/// </summary>
|
|
public static void ClearCreateRemoteDataCache()
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
_createRemoteData = null;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region EOT帧
|
|
/// <summary>
|
|
/// 手动刷新 EOT 数据(强制重新加载)
|
|
/// </summary>
|
|
public static void RefreshEndEOTData()
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
_endEOTData = GetBinData(ResourceEOTName);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 清除缓存的 EOT 数据(下次访问时会重新加载)
|
|
/// </summary>
|
|
public static void ClearEndEOTDataCache()
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
_endEOTData = null;
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
#region 最终结束帧
|
|
/// <summary>
|
|
/// 最终结束帧
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static byte[] GetEndDataFrame()
|
|
{
|
|
var bytes = new byte[133];
|
|
bytes[0] = 0x01;
|
|
bytes[1] = 0x00;
|
|
bytes[2] = 0xFF;
|
|
// 其余128字节默认为0,无需手动设置
|
|
return bytes;
|
|
}
|
|
#endregion
|
|
|
|
#region 工具方法
|
|
/// <summary>
|
|
/// 设置文件数据(自动计算 TotalPackets)
|
|
/// </summary>
|
|
/// <param name="fileData">文件数据</param>
|
|
public static void SetFileData(byte[] fileData)
|
|
{
|
|
FileData = fileData; // 这会自动触发 TotalPackets 计算
|
|
}
|
|
|
|
/// <summary>
|
|
/// 从文件路径加载数据(自动计算 TotalPackets)
|
|
/// </summary>
|
|
/// <param name="filePath">文件路径</param>
|
|
public static void LoadFileData(string filePath)
|
|
{
|
|
if (!File.Exists(filePath))
|
|
throw new FileNotFoundException($"文件不存在: {filePath}");
|
|
|
|
var fileData = File.ReadAllBytes(filePath);
|
|
FileData = fileData; // 自动计算 TotalPackets
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取指定文件的总包数(不设置到全局状态)
|
|
/// </summary>
|
|
/// <param name="fileData">文件数据</param>
|
|
/// <returns>总包数</returns>
|
|
public static int GetTotalPackets(byte[] fileData)
|
|
{
|
|
return (int)Math.Ceiling(fileData.Length / 1024.0);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 重置所有状态
|
|
/// </summary>
|
|
public static void Reset()
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
_totalPackets = 0;
|
|
_fileData = null;
|
|
_endEOTData = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将 UTF-8 字符串转换为 GBK 编码的字节数组
|
|
/// </summary>
|
|
public static byte[] Utf8StringToGbkBytes(string utf8Text)
|
|
{
|
|
if (utf8Text == null) return null;
|
|
return Encoding.GetEncoding(936).GetBytes(utf8Text);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将 UTF-8 字节数组转换为 GBK 字节数组
|
|
/// </summary>
|
|
public static byte[] Utf8BytesToGbkBytes(byte[] utf8Bytes)
|
|
{
|
|
if (utf8Bytes == null || utf8Bytes.Length == 0) return utf8Bytes;
|
|
|
|
// 1. UTF-8 字节 → 字符串(Unicode)
|
|
string text = Encoding.UTF8.GetString(utf8Bytes);
|
|
|
|
// 2. 字符串 → GBK 字节
|
|
return Encoding.GetEncoding(936).GetBytes(text);
|
|
}
|
|
#endregion
|
|
|
|
#region 验证设备响应数据包
|
|
|
|
private static readonly byte[] NakSignature = { 0x6E, 0x61, 0x6B, 0x00 }; // "nak"
|
|
private static readonly byte[] AckSignature = { 0x61, 0x63, 0x6B, 0x00 }; // "ack"
|
|
private static readonly byte[] AckcSignature = { 0x61, 0x63, 0x6B, 0x63, 0x00 }; // "ackc"
|
|
/// <summary>
|
|
/// NAK响应结果
|
|
/// </summary>
|
|
/// <param name="buffer">usb hid 返回包</param>
|
|
/// <returns></returns>
|
|
public static bool IsNakResponse(List<byte> buffer) => ContainsSubsequence(buffer.ToArray(), NakSignature);
|
|
|
|
/// <summary>
|
|
/// ACKC响应结果
|
|
/// </summary>
|
|
/// <param name="buffer">usb hid 返回包</param>
|
|
/// <returns></returns>
|
|
public static bool IsAckcResponse(List<byte> buffer) => ContainsSubsequence(buffer.ToArray(), AckcSignature);
|
|
|
|
/// <summary>
|
|
/// ACK响应结果
|
|
/// </summary>
|
|
/// <param name="buffer">usb hid 返回包</param>
|
|
/// <returns></returns>
|
|
public static bool IsReadyForDataFrameResponse(List<byte> buffer) => ContainsSubsequence(buffer.ToArray(), AckSignature);
|
|
|
|
private static bool ContainsSubsequence(ReadOnlySpan<byte> buffer, ReadOnlySpan<byte> pattern)
|
|
{
|
|
if (buffer.Length != 195) return false;
|
|
if (pattern.Length == 0) return false;
|
|
if (buffer.Length < pattern.Length) return false;
|
|
|
|
for (int i = 0; i <= buffer.Length - pattern.Length; i++)
|
|
{
|
|
if (buffer.Slice(i, pattern.Length).SequenceEqual(pattern))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#endregion
|
|
|
|
#region 查询固件版本
|
|
/// <summary>
|
|
/// 查询固件版本version query
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static byte[] VersionQuery()
|
|
{
|
|
string str = "0100FF6765742076657200300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000CC85";
|
|
byte[] content = new byte[str.Length / 2];
|
|
for (int i = 0; i < content.Length; i++)
|
|
{
|
|
content[i] = Convert.ToByte(str.Substring(i * 2, 2), 16);
|
|
}
|
|
return content;
|
|
}
|
|
#endregion
|
|
|
|
|
|
}
|
|
}
|
|
|