diff --git a/lib/ble/ble_service.dart b/lib/ble/ble_service.dart index 0d88698..f15ed34 100644 --- a/lib/ble/ble_service.dart +++ b/lib/ble/ble_service.dart @@ -47,6 +47,17 @@ class BleService { // 工厂构造函数,提供全局访问点 factory BleService() => _instance; + /// 用于存储最新的私钥,避免在解密时需要异步读取SharedPreferences + static List? _cachedPrivateKey; + + /// 获取缓存的私钥 + static List? get cachedPrivateKey => _cachedPrivateKey; + + /// 设置缓存的私钥 + static void setCachedPrivateKey(List privateKey) { + _cachedPrivateKey = List.from(privateKey); + } + /// 用来存储搜索到的设备,并用于去重和过滤 final Map _discoveredDevices = {}; @@ -281,23 +292,146 @@ class BleService { BaseBleCommand.MAX_PACKET_DATA_SIZE = mtu; } + /// 用于存储分包接收的数据 + final Map> _pendingPackets = {}; + /// 监听订阅值变化 - void _handleListenCharacteristicData(List data) async { + void _handleListenCharacteristicData(List data) { + AppLogger.highlight('✨✨✨ 📨 收到订阅数据:$data (长度: ${data.length}) ✨✨✨'); + // 解析数据 if (data.isNotEmpty) { - dynamic result = await bleCommandManager.handleResponse(data); - - if (result != null) { - // 触发命令响应等待器 - _triggerCommandResponseWaiters(result); - } else { - AppLogger.warn('⚠️ 数据包解析失败或不匹配任何命令'); + // ✨✨✨ 处理分包数据重组 ✨✨✨ + List? completePacket = _reassemblePacket(data); + + if (completePacket != null) { + // 如果有完整数据包,进行处理 + dynamic result = bleCommandManager.handleResponse(completePacket); + + if (result != null) { + // 触发命令响应等待器 + _triggerCommandResponseWaiters(result); + } else { + AppLogger.warn('⚠️ 数据包解析失败或不匹配任何命令'); + } } } else { AppLogger.warn('✨✨✨ ⚠️ 收到空数据 ✨✨✨'); } } + /// ✨✨✨ 重组分包数据 ✨✨✨ + /// [data] 接收到的数据片段 + /// 返回完整的数据包,如果数据不完整则返回null + List? _reassemblePacket(List data) { + try { + // 1. 检查是否是包头开始的数据 + if (data.length >= 4 && + data[0] == BaseBleCommand.PACKET_HEADER[0] && + data[1] == BaseBleCommand.PACKET_HEADER[1] && + data[2] == BaseBleCommand.PACKET_HEADER[2] && + data[3] == BaseBleCommand.PACKET_HEADER[3]) { + + // 这是一个新包的开始 + // 首先检查是否已有未完成的包,如果有则丢弃(超时处理) + if (_pendingPackets.isNotEmpty) { + AppLogger.warn('⚠️ 检测到新包开始,丢弃未完成的包'); + _pendingPackets.clear(); + } + + // 解析包头信息以获取预期长度 + if (data.length >= 12) { + // 解析包序号 (大端序) + int packetSequence = (data[5] << 8) | data[6]; + + // 解析数据长度 (大端序: 高16位加密后长度 + 低16位原始长度) + int combinedLength = (data[8] << 24) | (data[9] << 16) | (data[10] << 8) | data[11]; + int encryptedDataLength = (combinedLength >> 16) & 0xFFFF; + int originalDataLength = combinedLength & 0xFFFF; + + // 根据加密类型确定实际数据长度 + int encryptType = data[7] & 0x0F; + int actualDataLength = (encryptType == BaseBleCommand.ENCRYPT_TYPE_PLAIN) ? originalDataLength : encryptedDataLength; + + // 计算完整包的预期长度 + int expectedTotalLength = 12 + actualDataLength + 2; // 包头到数据长度(12) + 数据块 + CRC(2) + + AppLogger.debug('📦 新包开始: 序号=$packetSequence, 预期长度=$expectedTotalLength, 实际长度=${data.length}'); + + if (data.length == expectedTotalLength) { + // 数据完整,直接返回 + AppLogger.debug('✅ 数据包完整,无需重组'); + return data; + } else if (data.length < expectedTotalLength) { + // 数据不完整,存储到待处理队列 + AppLogger.debug('📥 数据包不完整,开始重组: 已接收${data.length}/$expectedTotalLength字节'); + _pendingPackets[packetSequence] = List.from(data); + return null; + } else { + // 数据长度超过预期,可能存在错误 + AppLogger.warn('⚠️ 数据包长度异常: 实际${data.length}字节 > 预期$expectedTotalLength字节'); + return data; + } + } else { + // 包头信息不完整 + AppLogger.warn('⚠️ 包头信息不完整,无法解析包长度'); + return data; + } + } else { + // 这是数据片段,尝试追加到现有未完成的包 + if (_pendingPackets.isNotEmpty) { + // 获取第一个(也是唯一一个)未完成的包 + int packetSequence = _pendingPackets.keys.first; + List pendingData = _pendingPackets[packetSequence]!; + + // 追加数据 + pendingData.addAll(data); + _pendingPackets[packetSequence] = pendingData; + + AppLogger.debug('📥 追加数据片段: 当前长度${pendingData.length}字节'); + + // 尝试解析包头信息以检查是否完整 + if (pendingData.length >= 12) { + // 解析数据长度 + int combinedLength = (pendingData[8] << 24) | (pendingData[9] << 16) | (pendingData[10] << 8) | pendingData[11]; + int encryptedDataLength = (combinedLength >> 16) & 0xFFFF; + int originalDataLength = combinedLength & 0xFFFF; + + // 根据加密类型确定实际数据长度 + int encryptType = pendingData[7] & 0x0F; + int actualDataLength = (encryptType == BaseBleCommand.ENCRYPT_TYPE_PLAIN) ? originalDataLength : encryptedDataLength; + + // 计算完整包的预期长度 + int expectedTotalLength = 12 + actualDataLength + 2; // 包头到数据长度(12) + 数据块 + CRC(2) + + if (pendingData.length == expectedTotalLength) { + // 数据完整,返回完整包并清理待处理队列 + AppLogger.debug('✅ 数据包重组完成: 序号=$packetSequence, 长度=$expectedTotalLength字节'); + List completePacket = _pendingPackets.remove(packetSequence)!; + return completePacket; + } else if (pendingData.length > expectedTotalLength) { + // 数据长度超过预期,可能存在错误 + AppLogger.warn('⚠️ 重组后数据包长度异常: 实际${pendingData.length}字节 > 预期$expectedTotalLength字节'); + _pendingPackets.clear(); + return pendingData; + } + // 如果长度还不够,继续等待更多数据 + } + // 如果包头信息还不完整,继续等待更多数据 + return null; + } else { + // 没有未完成的包,但收到的数据不是包头开始的数据 + AppLogger.warn('⚠️ 收到无法关联的数据片段'); + return data; + } + } + } catch (e, stackTrace) { + AppLogger.error('❌ 数据包重组异常', error: e, stackTrace: stackTrace); + _pendingPackets.clear(); // 清理异常状态 + return data; // 返回原始数据让后续处理尝试解析 + } + } + /// ✨✨✨ 触发命令响应等待器 ✨✨✨ void _triggerCommandResponseWaiters(dynamic response) { // 遍历所有等待中的命令,找到匹配的进行响应 diff --git a/lib/ble/command/ble_command_manager.dart b/lib/ble/command/ble_command_manager.dart index 5cbff88..7fc35fe 100644 --- a/lib/ble/command/ble_command_manager.dart +++ b/lib/ble/command/ble_command_manager.dart @@ -6,6 +6,7 @@ import 'package:starwork_flutter/ble/command/base/base_ble_command.dart'; import 'package:starwork_flutter/ble/command/base/base_ble_response_parser.dart'; import 'package:starwork_flutter/ble/command/response/ble_cmd_get_private_key_parser.dart'; import 'package:starwork_flutter/ble/command/response/ble_cmd_get_public_key_parser.dart'; +import 'package:starwork_flutter/ble/command/response/ble_cmd_read_lock_status_parser.dart'; import 'package:starwork_flutter/common/constant/cache_keys.dart'; import 'package:starwork_flutter/common/sm4_encipher/sm4.dart'; import 'package:starwork_flutter/common/utils/shared_preferences_utils.dart'; @@ -48,6 +49,7 @@ class BleCommandManager { // 注册获取公钥命令解析器 registerParser(BleCmdGetPublicKeyParser()); registerParser(BleCmdGetPrivateKeyParser()); + registerParser(BleCmdReadLockStatusParser()); // TODO: 在这里注册其他命令解析器 // registerParser(BleCmdSetPasswordParser()); @@ -72,7 +74,7 @@ class BleCommandManager { /// ✨✨✨ 处理接收到的应答数据包 ✨✨✨ /// [rawPacketData] 完整的原始数据包 /// 返回解析后的业务数据,如果没有匹配的解析器则返回null - dynamic handleResponse(List rawPacketData) async { + dynamic handleResponse(List rawPacketData) { AppLogger.debug('📊 收到蓝牙数据 (${rawPacketData.length}字节): ${rawPacketData.map((b) => '0x${b.toRadixString(16).padLeft(2, '0')}').join(' ')}'); try { @@ -92,7 +94,7 @@ class BleCommandManager { } // ✨✨✨ 根据加密类型进行解密处理 ✨✨✨ - List processedData = await _decryptDataIfNeeded(parsedPacket); + List processedData = _decryptDataIfNeededSync(parsedPacket); // ✨✨✨ 提取命令ID (大端序) ✨✨✨ int commandId = (processedData[0] << 8) | processedData[1]; @@ -132,6 +134,51 @@ class BleCommandManager { } } + /// ✨✨✨ 根据加密类型解密数据(同步版本) ✨✨✨ + /// [parsedPacket] 已解析的数据包 + /// 返回解密后的数据或原始数据(如果不需要解密) + List _decryptDataIfNeededSync(ParsedPacket parsedPacket) { + // 如果是明文,直接返回原始数据 + if (parsedPacket.encryptType == BaseBleCommand.ENCRYPT_TYPE_PLAIN) { + AppLogger.debug('🔓 数据未加密,直接使用原始数据'); + return parsedPacket.data; + } + + switch (parsedPacket.encryptType) { + case BaseBleCommand.ENCRYPT_TYPE_AES128: + return parsedPacket.data; + case BaseBleCommand.ENCRYPT_TYPE_SM4_PRESET: + var connectedDevice = BleService().connectedDevice; + var platformName = connectedDevice?.platformName ?? ''; + if (platformName.isEmpty) { + AppLogger.warn('⚠️ 解密数据时未找到设备名称'); + return parsedPacket.data; + } + var decrypt = SM4.decrypt( + parsedPacket.data, + key: utf8.encode(platformName), + mode: SM4CryptoMode.ECB, + ); + return decrypt; + case BaseBleCommand.ENCRYPT_TYPE_SM4_DEVICE: + // 使用缓存的私钥进行解密 + var cachedPrivateKey = BleService.cachedPrivateKey; + if (cachedPrivateKey == null || cachedPrivateKey.isEmpty) { + AppLogger.warn('⚠️ 解密数据时未找到缓存的私钥'); + return parsedPacket.data; + } + var decrypt = SM4.decrypt( + parsedPacket.data, + key: cachedPrivateKey, + mode: SM4CryptoMode.ECB, + ); + return decrypt; + default: + AppLogger.warn('⚠️ 未知的加密类型: ${parsedPacket.encryptType},使用原始数据'); + return parsedPacket.data; + } + } + /// ✨✨✨ 根据加密类型解密数据 ✨✨✨ /// [parsedPacket] 已解析的数据包 /// 返回解密后的数据或原始数据(如果不需要解密) diff --git a/lib/ble/command/response/ble_cmd_read_lock_status_parser.dart b/lib/ble/command/response/ble_cmd_read_lock_status_parser.dart index 4b087b8..c2c6729 100644 --- a/lib/ble/command/response/ble_cmd_read_lock_status_parser.dart +++ b/lib/ble/command/response/ble_cmd_read_lock_status_parser.dart @@ -2,6 +2,7 @@ import 'package:starwork_flutter/base/app_logger.dart'; import 'package:starwork_flutter/ble/command/base/base_ble_command.dart'; import 'package:starwork_flutter/ble/command/base/base_ble_response_parser.dart'; import 'package:starwork_flutter/ble/command/request/ble_cmd_get_public_key.dart'; +import 'package:starwork_flutter/ble/command/request/ble_cmd_read_lock_status.dart'; /// ✨✨✨ 读取锁状态命令应答数据结构 ✨✨✨ class ReadLockStatusResponse { @@ -94,7 +95,7 @@ class ReadLockStatusResponse { /// ✨✨✨ 读取锁状态命令应答解析器 ✨✨✨ class BleCmdReadLockStatusParser extends BaseBleResponseParser { @override - int get commandId => BleCmdGetPublicKey.cmdId; // 0x3090 + int get commandId => BleCmdReadLockStatus.cmdId; @override String get commandName => '读取锁状态命令'; diff --git a/lib/views/device/searchDevice/search_device_controller.dart b/lib/views/device/searchDevice/search_device_controller.dart index 6e17f7a..58f27dd 100644 --- a/lib/views/device/searchDevice/search_device_controller.dart +++ b/lib/views/device/searchDevice/search_device_controller.dart @@ -207,6 +207,9 @@ class SearchDeviceController extends BaseController { await SharedPreferencesUtils.saveIntList(CacheKeys.lockCommKey, privateKeyResponse.commKey); await SharedPreferencesUtils.saveIntList(CacheKeys.lockSignKey, privateKeyResponse.signKey); AppLogger.info('🎯 获取私钥成功: ${privateKeyResponse.toString()}'); + + // 更新缓存的私钥,以便在解密时使用 + BleService.setCachedPrivateKey(privateKeyResponse.commKey); //读取锁状态 BleCmdReadLockStatus readLockStatusCmd = BleCmdReadLockStatus(