starwork_flutter/lib/ble/command/base/base_ble_command.dart

402 lines
13 KiB
Dart
Raw Normal View History

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位)
2025-09-09 15:42:31 +08:00
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;
}
}
2025-09-09 15:42:31 +08:00
if (i == 0) {
// 获取分包序号
int packetSequence = getNextPacketSequence();
2025-09-09 15:42:31 +08:00
// 组装包头
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);
2025-09-09 15:42:31 +08:00
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;
}
// 获取固定长度的数组
2025-09-09 15:42:31 +08:00
List<int> getFixedLengthList(List<int> data, int length) {
for (int i = 0; i < length; i++) {
data.add(0);
}
return data;
}
}