402 lines
13 KiB
Dart
402 lines
13 KiB
Dart
import 'dart:typed_data';
|
||
import 'package:starwork_flutter/base/app_logger.dart';
|
||
|
||
/// ✨✨✨ 数据包解析结果类 ✨✨✨
|
||
class ParsedPacket {
|
||
final bool isValid;
|
||
final int packetType;
|
||
final int packetSequence;
|
||
final int version;
|
||
final int encryptType;
|
||
final int encryptedDataLength;
|
||
final int originalDataLength;
|
||
final List<int> data;
|
||
final int crc16;
|
||
final String? errorMessage;
|
||
|
||
ParsedPacket({
|
||
required this.isValid,
|
||
required this.packetType,
|
||
required this.packetSequence,
|
||
required this.version,
|
||
required this.encryptType,
|
||
required this.encryptedDataLength,
|
||
required this.originalDataLength,
|
||
required this.data,
|
||
required this.crc16,
|
||
this.errorMessage,
|
||
});
|
||
|
||
@override
|
||
String toString() {
|
||
if (!isValid) {
|
||
return 'ParsedPacket(invalid: $errorMessage)';
|
||
}
|
||
return 'ParsedPacket(type: 0x${packetType.toRadixString(16).padLeft(2, '0')}, ' +
|
||
'seq: $packetSequence, version: ${version >> 4}, encrypt: $encryptType, ' +
|
||
'dataLen: $originalDataLength, crc: 0x${crc16.toRadixString(16).padLeft(4, '0')})';
|
||
}
|
||
}
|
||
|
||
/// ✨✨✨ 蓝牙命令基类 - 提供数据包组装功能 ✨✨✨
|
||
abstract class BaseBleCommand<T> {
|
||
/// 包头固定值: 0XEF01EE02
|
||
static const List<int> PACKET_HEADER = [0xEF, 0x01, 0xEE, 0x02];
|
||
|
||
/// 包类型
|
||
static const int PACKET_TYPE_REQUEST = 0x01; // 请求包
|
||
static const int PACKET_TYPE_RESPONSE = 0x11; // 应答包
|
||
|
||
/// 包版本 (高4位)
|
||
static const int PACKET_VERSION = 0x20; // 版本2.0
|
||
|
||
/// 加密类型 (低4位)
|
||
static const int ENCRYPT_TYPE_PLAIN = 0x00; // 明文
|
||
static const int ENCRYPT_TYPE_AES128 = 0x01; // AES128
|
||
static const int ENCRYPT_TYPE_SM4_PRESET = 0x02; // SM4(事先约定密钥)
|
||
static const int ENCRYPT_TYPE_SM4_DEVICE = 0x03; // SM4(设备指定密钥)
|
||
|
||
/// 最大单包数据长度 (可根据MTU调整)
|
||
static int MAX_PACKET_DATA_SIZE = 100;
|
||
|
||
/// 全局包序号计数器
|
||
static int _globalPacketSequence = 1;
|
||
|
||
/// ✨✨✨ 获取下一个包序号 ✨✨✨
|
||
static int getNextPacketSequence() {
|
||
int sequence = _globalPacketSequence;
|
||
_globalPacketSequence++;
|
||
if (_globalPacketSequence > 0xFFFF) {
|
||
_globalPacketSequence = 1; // 2字节范围内循环
|
||
}
|
||
return sequence;
|
||
}
|
||
|
||
/// ✨✨✨ 组装包头 ✨✨✨
|
||
/// [packetType] 包类型 (0x01请求包, 0x11应答包)
|
||
/// [packetSequence] 包序号 (2字节)
|
||
/// [encryptType] 加密类型 (0:明文, 1:AES128, 2:SM4事先约定, 3:SM4设备指定)
|
||
/// [dataLength] 数据长度 (高16位:加密后长度, 低16位:原始长度)
|
||
static List<int> buildPacketHeader({
|
||
required int packetType,
|
||
required int packetSequence,
|
||
int encryptType = ENCRYPT_TYPE_PLAIN,
|
||
required int encryptedDataLength,
|
||
required int originalDataLength,
|
||
}) {
|
||
List<int> header = [];
|
||
|
||
// 1. 包头 (4字节)
|
||
header.addAll(PACKET_HEADER);
|
||
|
||
// 2. 包类型 (1字节)
|
||
header.add(packetType);
|
||
|
||
// 3. 包序号 (2字节,大端序)
|
||
header.add((packetSequence >> 8) & 0xFF); // 高字节
|
||
header.add(packetSequence & 0xFF); // 低字节
|
||
|
||
// 4. 包标识 (1字节: 高4位版本 + 低4位加密类型)
|
||
int packetFlag = (PACKET_VERSION & 0xF0) | (encryptType & 0x0F);
|
||
header.add(packetFlag);
|
||
|
||
// 5. 数据长度 (4字节,大端序: 高16位加密后长度 + 低16位原始长度)
|
||
int combinedLength = (encryptedDataLength << 16) | originalDataLength;
|
||
header.add((combinedLength >> 24) & 0xFF); // 最高字节
|
||
header.add((combinedLength >> 16) & 0xFF); // 次高字节
|
||
header.add((combinedLength >> 8) & 0xFF); // 次低字节
|
||
header.add(combinedLength & 0xFF); // 最低字节
|
||
|
||
return header;
|
||
}
|
||
|
||
/// ✨✨✨ 组装包尾 (CRC16校验) ✨✨✨
|
||
/// [packetData] 完整的数据包(包头+数据块)
|
||
static List<int> buildPacketTail(List<int> packetData) {
|
||
int crc16 = calculateCRC16Kermit(packetData);
|
||
|
||
// CRC16校验位 (2字节,大端序)
|
||
List<int> tail = [
|
||
(crc16 >> 8) & 0xFF, // 高字节
|
||
crc16 & 0xFF, // 低字节
|
||
];
|
||
|
||
return tail;
|
||
}
|
||
|
||
/// ✨✨✨ 计算CRC16-KERMIT校验 ✨✨✨
|
||
static int calculateCRC16Kermit(List<int> data) {
|
||
const int polynomial = 0x1021; // CRC16-KERMIT多项式
|
||
const int initialValue = 0x0000;
|
||
|
||
int crc = initialValue;
|
||
|
||
for (int byte in data) {
|
||
crc ^= (byte << 8);
|
||
for (int i = 0; i < 8; i++) {
|
||
if ((crc & 0x8000) != 0) {
|
||
crc = (crc << 1) ^ polynomial;
|
||
} else {
|
||
crc <<= 1;
|
||
}
|
||
crc &= 0xFFFF; // 保持16位
|
||
}
|
||
}
|
||
|
||
// KERMIT CRC需要交换字节序
|
||
int result = ((crc & 0xFF) << 8) | ((crc >> 8) & 0xFF);
|
||
return result;
|
||
}
|
||
|
||
/// ✨✨✨ 分包处理 - 将大数据分割成多个包 ✨✨✨
|
||
/// [originalData] 原始数据
|
||
/// [encryptType] 加密类型
|
||
/// 返回多个完整的数据包
|
||
static List<List<int>> splitIntoPackets({
|
||
required List<int> originalData,
|
||
int encryptType = ENCRYPT_TYPE_PLAIN,
|
||
}) {
|
||
List<List<int>> packets = [];
|
||
|
||
// 计算需要分成多少包
|
||
int totalPackets = (originalData.length / MAX_PACKET_DATA_SIZE).ceil();
|
||
|
||
for (int i = 0; i < totalPackets; i++) {
|
||
int startIndex = i * MAX_PACKET_DATA_SIZE;
|
||
int endIndex = (startIndex + MAX_PACKET_DATA_SIZE > originalData.length) ? originalData.length : startIndex + MAX_PACKET_DATA_SIZE;
|
||
|
||
// 获取当前包的数据
|
||
List<int> currentPacketData = originalData.sublist(startIndex, endIndex);
|
||
int originalDataLength = currentPacketData.length;
|
||
|
||
// ✨✨✨ 根据加密类型计算加密后数据长度 ✨✨✨
|
||
int encryptedDataLength = originalDataLength; // 默认情况下加密后长度与原始长度相同
|
||
if (encryptType != ENCRYPT_TYPE_PLAIN) {
|
||
// TODO: 根据具体加密算法计算加密后长度
|
||
// 例如AES128加密通常会填充到16字节的倍数
|
||
switch (encryptType) {
|
||
case ENCRYPT_TYPE_AES128:
|
||
// AES128加密填充到16字节边界
|
||
encryptedDataLength = ((originalDataLength + 15) ~/ 16) * 16;
|
||
break;
|
||
case ENCRYPT_TYPE_SM4_PRESET:
|
||
case ENCRYPT_TYPE_SM4_DEVICE:
|
||
// SM4加密填充到16字节边界
|
||
encryptedDataLength = ((originalDataLength + 15) ~/ 16) * 16;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (i == 0) {
|
||
// 获取分包序号
|
||
int packetSequence = getNextPacketSequence();
|
||
|
||
// 组装包头
|
||
List<int> header = buildPacketHeader(
|
||
packetType: PACKET_TYPE_REQUEST,
|
||
packetSequence: packetSequence,
|
||
encryptType: encryptType,
|
||
encryptedDataLength: originalData.length,
|
||
originalDataLength: originalData.length,
|
||
);
|
||
// 组装完整包 (包头 + 数据)
|
||
List<int> packetWithHeader = [];
|
||
packetWithHeader.addAll(header);
|
||
packetWithHeader.addAll(currentPacketData);
|
||
|
||
// 计算并添加包尾
|
||
List<int> tail = buildPacketTail(packetWithHeader);
|
||
|
||
// 完整的数据包
|
||
List<int> completePacket = [];
|
||
completePacket.addAll(packetWithHeader);
|
||
completePacket.addAll(tail);
|
||
|
||
packets.add(completePacket);
|
||
} else {
|
||
packets.add(currentPacketData);
|
||
}
|
||
}
|
||
|
||
return packets;
|
||
}
|
||
|
||
/// ✨✨✨ 抽象方法 - 子类需要实现具体的数据构建逻辑 ✨✨✨
|
||
List<int> buildData();
|
||
|
||
/// ✨✨✨ 获取当前命令的加密类型 - 子类可以重写此方法 ✨✨✨
|
||
/// 默认返回明文类型,子类可以根据需要重写
|
||
int getEncryptType() {
|
||
return ENCRYPT_TYPE_PLAIN;
|
||
}
|
||
|
||
/// ✨✨✨ 构建完整的命令包 ✨✨✨
|
||
/// 使用子类定义的加密类型自动组装数据包
|
||
List<List<int>> build() {
|
||
// 获取子类实现的数据
|
||
List<int> commandData = buildData();
|
||
|
||
// 使用子类定义的加密类型
|
||
int encryptType = getEncryptType();
|
||
|
||
// 分包处理
|
||
List<List<int>> packets = splitIntoPackets(
|
||
originalData: commandData,
|
||
encryptType: encryptType,
|
||
);
|
||
|
||
return packets;
|
||
}
|
||
|
||
/// ✨✨✨ 解析接收到的数据包 ✨✨✨
|
||
/// [rawData] 接收到的原始数据包字节
|
||
/// 返回解析结果,包含包的各个字段信息
|
||
static ParsedPacket parsePacket(List<int> rawData) {
|
||
try {
|
||
// 1. 检查最小长度 (包头4 + 包类型1 + 包序号2 + 包标识1 + 数据长度4 + CRC2 = 14字节)
|
||
if (rawData.length < 14) {
|
||
return ParsedPacket(
|
||
isValid: false,
|
||
packetType: 0,
|
||
packetSequence: 0,
|
||
version: 0,
|
||
encryptType: 0,
|
||
encryptedDataLength: 0,
|
||
originalDataLength: 0,
|
||
data: [],
|
||
crc16: 0,
|
||
errorMessage: '数据包长度不足: ${rawData.length}字节 < 14字节',
|
||
);
|
||
}
|
||
|
||
int offset = 0;
|
||
|
||
// 2. 检查包头
|
||
List<int> header = rawData.sublist(offset, offset + 4);
|
||
offset += 4;
|
||
if (!_isValidHeader(header)) {
|
||
return ParsedPacket(
|
||
isValid: false,
|
||
packetType: 0,
|
||
packetSequence: 0,
|
||
version: 0,
|
||
encryptType: 0,
|
||
encryptedDataLength: 0,
|
||
originalDataLength: 0,
|
||
data: [],
|
||
crc16: 0,
|
||
errorMessage: '包头不正确: ${header.map((b) => '0x${b.toRadixString(16).padLeft(2, '0')}').join(' ')}',
|
||
);
|
||
}
|
||
|
||
// 3. 解析包类型
|
||
int packetType = rawData[offset++];
|
||
|
||
// 4. 解析包序号 (大端序)
|
||
int packetSequence = (rawData[offset] << 8) | rawData[offset + 1];
|
||
offset += 2;
|
||
|
||
// 5. 解析包标识 (高4位版本 + 低4位加密类型)
|
||
int packetFlag = rawData[offset++];
|
||
int version = (packetFlag & 0xF0);
|
||
int encryptType = (packetFlag & 0x0F);
|
||
AppLogger.highlight('加密类型:${encryptType}');
|
||
|
||
// 6. 解析数据长度 (大端序: 高16位加密后长度 + 低16位原始长度)
|
||
int combinedLength = (rawData[offset] << 24) | (rawData[offset + 1] << 16) | (rawData[offset + 2] << 8) | rawData[offset + 3];
|
||
offset += 4;
|
||
int encryptedDataLength = (combinedLength >> 16) & 0xFFFF;
|
||
int originalDataLength = combinedLength & 0xFFFF;
|
||
AppLogger.debug('📏 数据长度: 加密后=$encryptedDataLength, 原始=$originalDataLength');
|
||
|
||
// 7. 检查数据包完整性
|
||
// ✨✨✨ 根据加密类型确定实际数据长度 ✨✨✨
|
||
int actualDataLength = (encryptType == ENCRYPT_TYPE_PLAIN) ? originalDataLength : encryptedDataLength;
|
||
int expectedTotalLength = 12 + actualDataLength + 2; // 包头到数据长度(12) + 数据块 + CRC(2)
|
||
if (rawData.length != expectedTotalLength) {
|
||
return ParsedPacket(
|
||
isValid: false,
|
||
packetType: packetType,
|
||
packetSequence: packetSequence,
|
||
version: version,
|
||
encryptType: encryptType,
|
||
encryptedDataLength: encryptedDataLength,
|
||
originalDataLength: originalDataLength,
|
||
data: [],
|
||
crc16: 0,
|
||
errorMessage: '数据包长度不匹配: 实际${rawData.length}字节 != 期望${expectedTotalLength}字节',
|
||
);
|
||
}
|
||
|
||
// 8. 提取数据块
|
||
// ✨✨✨ 根据加密类型提取相应长度的数据 ✨✨✨
|
||
List<int> data = rawData.sublist(offset, offset + actualDataLength);
|
||
offset += actualDataLength;
|
||
|
||
// // 9. 提取CRC16校验位 (大端序)
|
||
int crc16 = (rawData[offset] << 8) | rawData[offset + 1];
|
||
|
||
//
|
||
// // 10. 验证CRC16校验
|
||
// List<int> dataToCheck = rawData.sublist(0, rawData.length - 2); // 除去CRC的数据
|
||
// int calculatedCRC = calculateCRC16Kermit(dataToCheck);
|
||
// bool crcValid = (calculatedCRC == crc16);
|
||
// AppLogger.debug('🔍 CRC16验证: 计算值=0x${calculatedCRC.toRadixString(16).padLeft(4, '0')}, 接收值=0x${crc16.toRadixString(16).padLeft(4, '0')}, ${crcValid ? '✅通过' : '❌失败'}');
|
||
//
|
||
// if (!crcValid) {
|
||
// return ParsedPacket(
|
||
// isValid: false,
|
||
// packetType: packetType, packetSequence: packetSequence, version: version, encryptType: encryptType,
|
||
// encryptedDataLength: encryptedDataLength, originalDataLength: originalDataLength, data: data,
|
||
// crc16: crc16, errorMessage: 'CRC16校验失败: 计算值=0x${calculatedCRC.toRadixString(16).padLeft(4, '0')}, 接收值=0x${crc16.toRadixString(16).padLeft(4, '0')}',
|
||
// );
|
||
// }
|
||
|
||
return ParsedPacket(
|
||
isValid: true,
|
||
packetType: packetType,
|
||
packetSequence: packetSequence,
|
||
version: version,
|
||
encryptType: encryptType,
|
||
encryptedDataLength: encryptedDataLength,
|
||
originalDataLength: originalDataLength,
|
||
data: data,
|
||
crc16: crc16,
|
||
);
|
||
} catch (e) {
|
||
AppLogger.error('❌ 数据包解析异常', error: e);
|
||
return ParsedPacket(
|
||
isValid: false,
|
||
packetType: 0,
|
||
packetSequence: 0,
|
||
version: 0,
|
||
encryptType: 0,
|
||
encryptedDataLength: 0,
|
||
originalDataLength: 0,
|
||
data: [],
|
||
crc16: 0,
|
||
errorMessage: '解析异常: $e',
|
||
);
|
||
}
|
||
}
|
||
|
||
/// ✨✨✨ 验证包头是否正确 ✨✨✨
|
||
static bool _isValidHeader(List<int> header) {
|
||
if (header.length != 4) return false;
|
||
for (int i = 0; i < 4; i++) {
|
||
if (header[i] != PACKET_HEADER[i]) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// 获取固定长度的数组
|
||
List<int> getFixedLengthList(List<int> data, int length) {
|
||
for (int i = 0; i < length; i++) {
|
||
data.add(0);
|
||
}
|
||
return data;
|
||
}
|
||
}
|