兼容win7版应用
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

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
}
}