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

402 lines
13 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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