import 'dart:async'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:starwork_flutter/base/app_logger.dart'; import 'package:starwork_flutter/ble/ble_config.dart'; import 'package:starwork_flutter/ble/command/base/base_ble_command.dart'; import 'package:starwork_flutter/ble/command/ble_command_manager.dart'; import 'package:starwork_flutter/ble/command/request/ble_cmd_add_admin.dart'; import 'package:starwork_flutter/ble/command/request/ble_cmd_get_private_key.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'; 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/model/scan_device_info.dart'; import 'package:starwork_flutter/common/constant/device_type.dart'; /// ✨✨✨ 待发送命令类 ✨✨✨ class _PendingCommand { final BaseBleCommand command; final String targetDeviceId; final String? targetDeviceName; final Duration timeout; final Completer completer; final DateTime createdAt; final int commandId; // 添加命令ID字段 _PendingCommand({ required this.command, required this.targetDeviceId, this.targetDeviceName, required this.timeout, required this.completer, required this.commandId, // 添加命令ID参数 }) : createdAt = DateTime.now(); } class BleService { // 私有构造函数 BleService._() { // ✅ 这里就是单例初始化的地方 // 只会执行一次(第一次获取实例时) _initialize(); } // 静态实例 static final BleService _instance = 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 = {}; // 使用命令管理器处理数据包 final BleCommandManager bleCommandManager = BleCommandManager(); /// mtu大小 int _mtu = 23; /// 用来监听蓝牙适配器状态的订阅流 StreamSubscription? _adapterStateSubscription; /// 用来监听搜索到的设备的订阅流 StreamSubscription>? _scanResultSubscription; /// 用来监听连接设备时的连接状态 StreamSubscription? _connectionStateSubscription; /// 用来监听mtu的变化 StreamSubscription? _mtuChangeSubscription; /// 用来监听订阅服务的数据 StreamSubscription>? _characteristicDataSubscription; /// 当前连接的设备 BluetoothDevice? _connectedDevice; /// 当前的写入特征值 BluetoothCharacteristic? _writeCharacteristic; /// 当前的订阅特征值 BluetoothCharacteristic? _subscriptionCharacteristic; /// ✨✨✨ 命令响应等待器映射 ✨✨✨ final Map> _commandResponseWaiters = {}; /// ✨✨✨ 命令超时定时器映射 ✨✨✨ final Map _commandTimeouts = {}; /// ✨✨✨ 命令ID到命令键的映射 ✨✨✨ final Map _commandIdToKeyMap = {}; /// 待发送命令队列 (用于自动连接后发送) final List<_PendingCommand> _pendingCommands = []; /// 搜索状态监控定时器 Timer? _scanningMonitorTimer; // 内部维护的蓝牙状态 BluetoothAdapterState _bluetoothAdapterState = BluetoothAdapterState.unknown; // 内部维护的设备连接状态 BluetoothConnectionState _bluetoothConnectionState = BluetoothConnectionState.disconnected; /// 提供外部获取蓝牙适配器方法 BluetoothAdapterState get bluetoothAdapterState => _bluetoothAdapterState; /// 搜索状态 bool get isScanningNow => FlutterBluePlus.isScanningNow; /// 获取当前连接的设备 BluetoothDevice? get connectedDevice => _connectedDevice; /// 初始化服务时执行的 Future _initialize() async { AppLogger.highlight('🚀 BleService 正在初始化...'); /// 监听蓝牙适配器状态 _adapterStateSubscription = FlutterBluePlus.adapterState.listen((BluetoothAdapterState state) { _bluetoothAdapterState = state; AppLogger.highlight('蓝牙适配器状态发送变化:${state}'); }); AppLogger.highlight('✅ BleService 初始化完成'); } /// 开启蓝牙搜索 void enableBluetoothSearch({ DeviceType deviceType = DeviceType.all, Duration searchTime = const Duration(seconds: 15), required void Function(ScanDeviceInfo device) onDeviceFound, }) async { // 检查蓝牙适配器状态 AppLogger.highlight('🔍 当前蓝牙适配器状态: $_bluetoothAdapterState'); // 如果状态是unknown,等待一下状态更新 if (_bluetoothAdapterState == BluetoothAdapterState.unknown) { AppLogger.highlight('⏳ 等待蓝牙适配器状态更新...'); await Future.delayed(const Duration(milliseconds: 500)); AppLogger.highlight('🔍 等待后蓝牙适配器状态: $_bluetoothAdapterState'); } if (_bluetoothConnectionState == BluetoothConnectionState.connected) { // 搜索时断开已有连接 List devs = FlutterBluePlus.connectedDevices; for (var d in devs) { d.disconnect(); } } if (_bluetoothAdapterState == BluetoothAdapterState.on) { AppLogger.highlight('🚀 开始启动蓝牙扫描,搜索时长: ${searchTime.inSeconds}秒'); try { FlutterBluePlus.startScan(timeout: searchTime); /// 取消旧的订阅,防止重复 _scanResultSubscription?.cancel(); _discoveredDevices.clear(); /// 监听搜索到的设备 _scanResultSubscription = FlutterBluePlus.onScanResults.listen( (List results) { for (var result in results) { var device = result.device; final deviceId = device.remoteId.toString(); final platformName = device.platformName; var serviceUuids = result.advertisementData.serviceUuids; // ✅ 只有新设备才回调 if (!_discoveredDevices.containsKey(deviceId) && platformName.isNotEmpty) { _discoveredDevices[deviceId] = result; bool pairStatus = false; bool hasNewEvent = false; for (var uuid in serviceUuids) { String uuidStr = uuid.toString().replaceAll('-', ''); if (uuidStr.length == 8) { var pairStatusStr = uuidStr.substring(4, 6); var hasNewEventStr = uuidStr.substring(6, 8); pairStatus = pairStatusStr == '01'; hasNewEvent = hasNewEventStr == '01'; var scanDeviceInfo = ScanDeviceInfo( isBinding: pairStatus, advName: device.platformName, rawDeviceInfo: result, hasNewEvent: hasNewEvent, ); onDeviceFound.call(scanDeviceInfo); } else if (uuidStr.length == 32) { var pairStatusStr = uuidStr.substring(26, 28); pairStatus = pairStatusStr == '00'; // 第4、5位(索引3和4) int statusValue = int.parse(pairStatusStr, radix: 16); // 提取 byte0(配对状态:第1位) int byte0 = (statusValue >> 0) & 0x01; // 取最低位 // 提取 byte1(事件状态:第2位) int byte1 = (statusValue >> 1) & 0x01; // 取次低位 // 判断是否未配对 pairStatus = (byte0 == 1); // 判断是否有新事件 hasNewEvent = (byte1 == 1); var scanDeviceInfo = ScanDeviceInfo( isBinding: pairStatus, advName: device.platformName, rawDeviceInfo: result, hasNewEvent: hasNewEvent, ); onDeviceFound.call(scanDeviceInfo); } } } else { // 可选:更新 RSSI _discoveredDevices[deviceId] = result; } } }, onError: (e) => AppLogger.error('搜索设备时遇到错误:' + e), ); AppLogger.highlight('✅ 蓝牙搜索已成功启动'); // 监听搜索状态变化 _monitorScanningState(); } catch (e, stackTrace) { AppLogger.error('启动蓝牙搜索失败', error: e, stackTrace: stackTrace); } } else { AppLogger.error('❌ 蓝牙适配器未开启,当前状态: $_bluetoothAdapterState'); } } /// 监控搜索状态变化 void _monitorScanningState() { // 取消之前的定时器 _scanningMonitorTimer?.cancel(); _scanningMonitorTimer = Timer.periodic(const Duration(seconds: 1), (timer) { bool currentlyScanning = FlutterBluePlus.isScanningNow; if (!currentlyScanning) { AppLogger.highlight('🔍 搜索已停止 (监控器检测)'); timer.cancel(); _scanningMonitorTimer = null; } }); } /// 停止扫描 void stopBluetoothSearch() async { var isScanningNow = FlutterBluePlus.isScanningNow; if (isScanningNow) { FlutterBluePlus.stopScan(); } // 取消搜索状态监控定时器 _scanningMonitorTimer?.cancel(); _scanningMonitorTimer = null; /// 清空搜索到的设备 _discoveredDevices.clear(); AppLogger.highlight('🛑 蓝牙搜索已停止'); } void seedData({ required BaseBleCommand command, }) async { // 搜索时断开已有连接 List devs = FlutterBluePlus.connectedDevices; for (var d in devs) { d.disconnect(); } } /// 监听连接设备时的状态 void _handleListenBluetoothConnectionState(BluetoothConnectionState state) { _bluetoothConnectionState = state; } /// 监听mtu变化 void _handleListenMtuChange(int mtu) { _mtu = mtu; // MTU包含了协议开销,需要减去包头和包尾的长度 // 包头: 4字节固定值 + 1字节包类型 + 2字节序号 + 1字节标识 + 4字节长度 = 12字节 // 包尾: 2字节CRC = 2字节 // 蓝牙协议还有额外开销,实际可用数据长度需要进一步减少 // 根据错误信息,MTU为185时,实际最大数据长度应该是182字节 // 所以我们保守计算,减去更多字节以确保兼容性 BaseBleCommand.MAX_PACKET_DATA_SIZE = mtu - 18; AppLogger.info('📏 MTU更新为: $mtu, 最大数据长度设置为: ${BaseBleCommand.MAX_PACKET_DATA_SIZE}'); } /// 用于存储分包接收的数据 final Map> _pendingPackets = {}; /// 监听订阅值变化 void _handleListenCharacteristicData(List data) { AppLogger.highlight('✨✨✨ 📨 收到订阅数据:$data (长度: ${data.length}) ✨✨✨'); // 解析数据 if (data.isNotEmpty) { // ✨✨✨ 处理分包数据重组 ✨✨✨ 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) { // 遍历所有等待中的命令,找到匹配的进行响应 List completedKeys = []; int commandId = response.commandId; String? commandKey = _commandIdToKeyMap[commandId]; if (commandKey != null) { // 精确匹配到特定命令 Completer? completer = _commandResponseWaiters[commandKey]; if (completer != null && !completer.isCompleted) { AppLogger.debug('🔔 精确匹配命令响应: 命令ID=0x${commandId.toRadixString(16).padLeft(4, '0')}, 键=$commandKey'); completer.complete(response); completedKeys.add(commandKey); // 清理已完成的等待器 _cleanupCommandWaiter(commandKey); _commandIdToKeyMap.remove(commandId); return; } else { // 如果找不到对应的completer或者已经完成,从映射中移除 _commandIdToKeyMap.remove(commandId); } } // 如果没有精确匹配,在重发场景中,我们应该完成所有匹配的等待器 // 而不是只完成最近的一个 if (_commandResponseWaiters.isNotEmpty) { // 查找所有与该命令ID相关的等待器 List matchingKeys = []; _commandIdToKeyMap.forEach((id, key) { if (id == commandId) { matchingKeys.add(key); } }); // 完成所有匹配的等待器 for (String key in matchingKeys) { Completer? completer = _commandResponseWaiters[key]; if (completer != null && !completer.isCompleted) { AppLogger.debug('🔔 触发命令响应(精确匹配模式): $key'); completer.complete(response); completedKeys.add(key); // 清理已完成的等待器 _cleanupCommandWaiter(key); } } // 从命令ID映射中移除已处理的键 _commandIdToKeyMap.removeWhere((id, key) => id == commandId && completedKeys.contains(key)); // 如果找到了匹配的等待器,直接返回 if (matchingKeys.isNotEmpty) { return; } // 如果仍然没有找到匹配的等待器,回退到原来的逻辑(完成所有未完成的等待器) // 但在重发场景中,我们只完成最近的等待器 // 获取最近添加的等待器(基于key的创建时间) String mostRecentKey = _commandResponseWaiters.keys.last; Completer? completer = _commandResponseWaiters[mostRecentKey]; if (completer != null && !completer.isCompleted) { AppLogger.debug('🔔 触发命令响应(最近模式): $mostRecentKey'); completer.complete(response); completedKeys.add(mostRecentKey); } } // 清理已完成的等待器 for (String key in completedKeys) { _cleanupCommandWaiter(key); } } /// ✨✨✨ 发送蓝牙命令 ✨✨✨ Future sendCommand({ required BaseBleCommand command, String? targetDeviceId, String? targetDeviceName, Duration timeout = const Duration(seconds: 10), bool autoConnectIfNeeded = true, Duration searchTimeoutIfNeeded = const Duration(seconds: 15), int maxRetries = 3, // 最大重试次数 int retryCount = 0, // 当前重试次数 String? existingCommandKey, // 用于重发时传递已有的命令键 }) async { AppLogger.highlight('✨✨✨ 🚀 开始发送蓝牙命令: ${command.runtimeType} (重试次数: $retryCount) ✨✨✨'); try { // 1. 检查连接状态 bool isConnected = await _ensureDeviceConnected( targetDeviceId: targetDeviceId, targetDeviceName: targetDeviceName, autoConnect: autoConnectIfNeeded, searchTimeout: searchTimeoutIfNeeded, ); if (!isConnected) { AppLogger.error('❌ 设备未连接,无法发送命令'); throw Exception('设备未连接,无法发送命令'); } // 1.1 检查设备是否仍然连接(在重发时特别重要) if (_connectedDevice != null) { try { // 检查设备连接状态 BluetoothConnectionState currentState = await _connectedDevice!.connectionState.first; if (currentState != BluetoothConnectionState.connected) { AppLogger.warn('⚠️ 设备连接状态异常,当前状态: $currentState'); // 尝试重新连接 isConnected = await _ensureDeviceConnected( targetDeviceId: targetDeviceId, targetDeviceName: targetDeviceName, autoConnect: true, // 强制重新连接 searchTimeout: searchTimeoutIfNeeded, ); if (!isConnected) { AppLogger.error('❌ 设备重新连接失败,无法发送命令'); throw Exception('设备重新连接失败,无法发送命令'); } } } catch (e) { AppLogger.warn('⚠️ 检查设备连接状态时出错: $e'); // 出错时也尝试重新连接 isConnected = await _ensureDeviceConnected( targetDeviceId: targetDeviceId, targetDeviceName: targetDeviceName, autoConnect: true, // 强制重新连接 searchTimeout: searchTimeoutIfNeeded, ); if (!isConnected) { AppLogger.error('❌ 设备重新连接失败,无法发送命令'); throw Exception('设备重新连接失败,无法发送命令'); } } } // 2. 检查写入特征值 if (_writeCharacteristic == null) { AppLogger.error('❌ 写入特征值未初始化'); // 尝试重新设置设备连接 if (_connectedDevice != null) { AppLogger.info('🔧 尝试重新设置设备连接...'); isConnected = await _setupDeviceConnection(_connectedDevice!); if (!isConnected || _writeCharacteristic == null) { AppLogger.error('❌ 重新设置设备连接失败'); throw Exception('写入特征值未初始化'); } } else { throw Exception('写入特征值未初始化'); } } // 3. 构建命令数据包 List> packets = command.build(); AppLogger.info('📦 命令数据包数量: ${packets.length}'); // 4. 设置命令响应等待器 String commandKey = existingCommandKey ?? _generateCommandKey(command); Completer responseCompleter = Completer(); _commandResponseWaiters[commandKey] = responseCompleter; // 5. 如果命令有cmdId静态字段,将其与命令键关联(仅在非重发时) if (existingCommandKey == null) { _registerCommandId(command, commandKey); } else if (command is BleCmdAddAdmin) { // 对于重发的命令,我们也需要更新命令ID映射 _registerCommandId(command, commandKey); } // 6. 设置超时定时器 Timer timeoutTimer = Timer(timeout, () { if (!responseCompleter.isCompleted) { AppLogger.warn('⏰ 命令响应超时: $commandKey'); _cleanupCommandWaiter(commandKey); responseCompleter.completeError(TimeoutException('命令响应超时', timeout)); } }); _commandTimeouts[commandKey] = timeoutTimer; // 7. 发送数据包 for (int i = 0; i < packets.length; i++) { List packet = packets[i]; AppLogger.debug('📤 发送第${i + 1}个数据包,数据包: (${packet.toString()},长度:${packet.length}字节)'); // 检查设备是否仍然连接 if (_connectedDevice != null) { BluetoothConnectionState currentState = await _connectedDevice!.connectionState.first; if (currentState != BluetoothConnectionState.connected) { AppLogger.error('❌ 设备在发送数据包时断开连接'); throw Exception('设备在发送数据包时断开连接'); } } await _writeCharacteristic!.write(packet, withoutResponse: false); // 在多包发送时稍微延迟 // if (i < packets.length - 1) { // await Future.delayed(const Duration(milliseconds: 50)); // } } AppLogger.info('✅ 所有数据包发送完成,等待应答...'); // 8. 等待响应 T? response = await responseCompleter.future; // 9. 检查是否需要重发(通用重发机制) // 只对实现了RetryableBleCommand接口的命令进行重发检查 // if (retryCount < maxRetries && command is RetryableBleCommand) { // // 明确转换类型以避免静态分析错误 // final retryableCommand = command as RetryableBleCommand; // if (retryableCommand.shouldRetry(response)) { // AppLogger.info('🔄 命令${command.runtimeType}需要重发,准备创建重发命令 (重试次数: ${retryCount + 1}/${maxRetries})'); // // try { // // 创建重发命令 // BaseBleCommand retryCommand = retryableCommand.createRetryCommand(response); // // // 重新发送命令,传递现有的命令键以确保响应正确处理 // AppLogger.info('🔄 使用重发机制重新发送命令'); // return await sendCommand( // command: retryCommand, // targetDeviceId: targetDeviceId, // targetDeviceName: targetDeviceName, // timeout: timeout, // autoConnectIfNeeded: true, // // 重发时也需要自动连接 // maxRetries: maxRetries, // retryCount: retryCount + 1, // // 增加重试次数 // existingCommandKey: commandKey, // 传递现有的命令键 // ); // } catch (retryError, retryStackTrace) { // AppLogger.error('❌ 创建或发送重发命令失败', error: retryError, stackTrace: retryStackTrace); // // 如果创建重发命令失败,返回原始响应 // AppLogger.highlight('✨✨✨ ✅ 命令发送完成,收到应答: $response ✨✨✨'); // return response; // } // } // } AppLogger.highlight('✨✨✨ ✅ 命令发送成功,收到应答: $response ✨✨✨'); return response; } catch (e, stackTrace) { AppLogger.error('❌ 发送蓝牙命令失败', error: e, stackTrace: stackTrace); rethrow; } } /// 将命令对象映射到 commandKey,特定命令需手动注册 cmdId void _registerCommandId(dynamic command, String commandKey) { try { final int? commandId = switch (command) { BleCmdGetPublicKey() => BleCmdGetPublicKey.cmdId, BleCmdGetPrivateKey() => BleCmdGetPrivateKey.cmdId, BleCmdReadLockStatus() => BleCmdReadLockStatus.cmdId, BleCmdAddAdmin() => BleCmdAddAdmin.cmdId, // 可在此添加更多命令类型 // BleCmdAnother() => BleCmdAnother.cmdId, _ => null, // 默认情况:无法识别的命令类型 }; if (commandId != null) { // 检查是否已存在该命令ID的映射 if (_commandIdToKeyMap.containsKey(commandId)) { // 如果已存在,我们需要更新映射而不是覆盖 // 这在重发场景中很重要,确保最新的命令键能正确映射 AppLogger.debug( '🔄 更新命令ID映射: 0x${commandId.toRadixString(16).padLeft(4, '0')} -> $commandKey (原键: ${_commandIdToKeyMap[commandId]})', ); } else { AppLogger.debug( '📝 命令ID映射: 0x${commandId.toRadixString(16).padLeft(4, '0')} -> $commandKey', ); } _commandIdToKeyMap[commandId] = commandKey; } else { AppLogger.debug('❓ 未知命令类型,无法注册 commandId: $commandKey'); } } catch (e) { AppLogger.warn('⚠️ 无法获取命令ID: $e'); } } void cancel() { /// 销毁蓝牙适配器监听 _adapterStateSubscription?.cancel(); _scanResultSubscription?.cancel(); _connectionStateSubscription?.cancel(); _mtuChangeSubscription?.cancel(); _characteristicDataSubscription?.cancel(); /// 取消搜索状态监控定时器 _scanningMonitorTimer?.cancel(); _scanningMonitorTimer = null; /// 清理命令等待器 for (String key in _commandResponseWaiters.keys) { _cleanupCommandWaiter(key); } _commandResponseWaiters.clear(); _commandTimeouts.clear(); _commandIdToKeyMap.clear(); // 清空命令ID映射 /// 清理待发送命令 for (_PendingCommand pendingCmd in _pendingCommands) { if (!pendingCmd.completer.isCompleted) { pendingCmd.completer.completeError(Exception('服务已关闭')); } } _pendingCommands.clear(); /// 重置连接状态 _bluetoothAdapterState = BluetoothAdapterState.unknown; _connectedDevice = null; _writeCharacteristic = null; _subscriptionCharacteristic = null; AppLogger.highlight('🗑️ BleService 资源已清理'); } /// ✨✨✨ 确保设备已连接 ✨✨✨ Future _ensureDeviceConnected({ String? targetDeviceId, String? targetDeviceName, bool autoConnect = true, Duration searchTimeout = const Duration(seconds: 15), }) async { AppLogger.debug('🔍 检查设备连接状态...'); // 1. 检查当前连接状态 if (_connectedDevice != null && _bluetoothConnectionState == BluetoothConnectionState.connected) { AppLogger.debug('📱 当前已连接设备: ${_connectedDevice!.platformName} (${_connectedDevice!.remoteId})'); // 如果指定了目标设备ID,检查是否匹配 if (targetDeviceId != null && _connectedDevice!.remoteId.toString() != targetDeviceId) { AppLogger.info('🔄 当前连接的设备与目标不匹配,需要切换连接'); AppLogger.info('📱 当前设备: ${_connectedDevice!.remoteId},目标设备: $targetDeviceId'); await _connectedDevice!.disconnect(); _connectedDevice = null; _writeCharacteristic = null; _subscriptionCharacteristic = null; } else { AppLogger.info('✅ 设备已连接,无需重新连接'); return true; } } else if (_connectedDevice != null) { AppLogger.warn('⚠️ 设备对象存在但连接状态异常: $_bluetoothConnectionState'); // 清理异常状态 _connectedDevice = null; _writeCharacteristic = null; _subscriptionCharacteristic = null; } // 2. 如果不允许自动连接,直接返回失败 if (!autoConnect) { AppLogger.warn('⚠️ 设备未连接且不允许自动连接'); return false; } // 3. 尝试查找已连接的设备 List connectedDevices = FlutterBluePlus.connectedDevices; AppLogger.debug('🔍 当前已连接设备数量: ${connectedDevices.length}'); if (targetDeviceId != null) { for (BluetoothDevice device in connectedDevices) { AppLogger.debug('📱 检查已连接设备: ${device.platformName} (${device.remoteId})'); if (device.remoteId.toString() == targetDeviceId) { AppLogger.info('🔗 找到已连接的目标设备: ${device.platformName}'); bool isConnected = await _setupDeviceConnection(device); if (isConnected) { return true; } else { AppLogger.warn('⚠️ 已连接设备设置失败,继续搜索...'); } } } } // 4. 需要搜索并连接设备 AppLogger.info('🔍 开始搜索目标设备...'); BluetoothDevice? foundDevice = await _searchForTargetDevice( targetDeviceId: targetDeviceId, targetDeviceName: targetDeviceName, searchTimeout: searchTimeout, ); if (foundDevice == null) { AppLogger.error('❌ 未找到目标设备'); return false; } // 5. 连接设备 AppLogger.info('🔗 开始连接设备: ${foundDevice.platformName}'); return await _connectToDevice(foundDevice); } /// ✨✨✨ 搜索目标设备 ✨✨✨ Future _searchForTargetDevice({ String? targetDeviceId, String? targetDeviceName, Duration searchTimeout = const Duration(seconds: 15), }) async { Completer searchCompleter = Completer(); // 启动搜索 enableBluetoothSearch( searchTime: searchTimeout, onDeviceFound: (ScanDeviceInfo deviceInfo) { BluetoothDevice device = deviceInfo.rawDeviceInfo.device; String deviceId = device.remoteId.toString(); String deviceName = device.platformName; AppLogger.debug('🔍 发现设备: $deviceName ($deviceId)'); // 检查是否匹配目标设备 bool isTargetDevice = false; if (targetDeviceId != null && deviceId == targetDeviceId) { isTargetDevice = true; AppLogger.info('🎯 通过ID匹配到目标设备: $deviceName'); } else if (targetDeviceName != null && deviceName.contains(targetDeviceName)) { isTargetDevice = true; AppLogger.info('🎯 通过名称匹配到目标设备: $deviceName'); } if (isTargetDevice && !searchCompleter.isCompleted) { searchCompleter.complete(device); } }, ); // 设置超时 Timer(searchTimeout, () { if (!searchCompleter.isCompleted) { searchCompleter.complete(null); } }); return await searchCompleter.future; } /// ✨✨✨ 连接到设备 ✨✨✨ Future _connectToDevice(BluetoothDevice device) async { try { AppLogger.info('🔗 正在连接设备: ${device.platformName}'); // 尝试连接设备,最多重试3次 int maxRetries = 3; for (int attempt = 1; attempt <= maxRetries; attempt++) { try { AppLogger.info('🔗 尝试连接设备 (第$attempt/$maxRetries次)'); await device.connect(timeout: const Duration(seconds: 10)); // 连接成功后设置设备连接 bool isConnected = await _setupDeviceConnection(device); if (isConnected) { return true; } else { AppLogger.warn('⚠️ 设备连接设置失败 (第$attempt/$maxRetries次)'); if (attempt < maxRetries) { AppLogger.info('⏳ 等待2秒后重试...'); await Future.delayed(const Duration(seconds: 2)); } } } catch (e) { AppLogger.warn('⚠️ 连接设备失败 (第$attempt/$maxRetries次): $e'); if (attempt < maxRetries) { AppLogger.info('⏳ 等待2秒后重试...'); await Future.delayed(const Duration(seconds: 2)); } else { rethrow; // 最后一次尝试失败,重新抛出异常 } } } return false; } catch (e) { AppLogger.error('❌ 连接设备失败: ${device.platformName}', error: e); return false; } } /// ✨✨✨ 设置设备连接 ✨✨✨ Future _setupDeviceConnection(BluetoothDevice device) async { try { AppLogger.info('🔧 设置设备连接: ${device.platformName}'); // 更新内部状态 _connectedDevice = device; // 监听连接状态 _connectionStateSubscription?.cancel(); _connectionStateSubscription = device.connectionState.listen(_handleListenBluetoothConnectionState); // 监听MTU变化 _mtuChangeSubscription?.cancel(); _mtuChangeSubscription = device.mtu.listen(_handleListenMtuChange); // 发现服务 List services = await device.discoverServices(); AppLogger.info('🔍 发现服务数量: ${services.length}'); // 查找目标服务 BluetoothService? targetService; for (BluetoothService service in services) { AppLogger.debug('📱 发现服务: ${service.uuid}'); if (service.uuid == BleConfig.serviceId) { targetService = service; break; } } if (targetService == null) { AppLogger.error('❌ 未找到目标服务: ${BleConfig.serviceId}'); // 打印所有发现的服务以便调试 for (BluetoothService service in services) { AppLogger.debug('📱 可用服务: ${service.uuid}'); } throw Exception('未找到目标服务: ${BleConfig.serviceId}'); } AppLogger.info('✅ 找到目标服务: ${targetService.uuid}'); // 查找特征值 BluetoothCharacteristic? writeChar; BluetoothCharacteristic? subscribeChar; AppLogger.debug('🔍 目标服务中的特征值数量: ${targetService.characteristics.length}'); for (BluetoothCharacteristic characteristic in targetService.characteristics) { AppLogger.debug('📱 发现特征值: ${characteristic.uuid}'); if (characteristic.uuid == BleConfig.characteristicIdWrite) { writeChar = characteristic; AppLogger.info('✅ 找到写入特征值: ${characteristic.uuid}'); } else if (characteristic.uuid == BleConfig.characteristicIdSubscription) { subscribeChar = characteristic; AppLogger.info('✅ 找到订阅特征值: ${characteristic.uuid}'); } } // 如果没有找到特征值,打印所有可用的特征值 if (writeChar == null || subscribeChar == null) { AppLogger.warn('⚠️ 未找到所需的特征值,当前服务中的所有特征值:'); for (BluetoothCharacteristic characteristic in targetService.characteristics) { AppLogger.warn(' 📱 特征值: ${characteristic.uuid}, 属性: ${characteristic.properties}'); } // 尝试使用找到的特征值 if (writeChar == null && subscribeChar != null) { writeChar = subscribeChar; AppLogger.warn('⚠️ 未找到写入特征值,使用订阅特征值作为写入特征值'); } else if (subscribeChar == null && writeChar != null) { subscribeChar = writeChar; AppLogger.warn('⚠️ 未找到订阅特征值,使用写入特征值作为订阅特征值'); } // 如果仍然没有找到所需的特征值,抛出异常 if (writeChar == null || subscribeChar == null) { throw Exception('未找到所需的特征值'); } } // 设置特征值 _writeCharacteristic = writeChar; _subscriptionCharacteristic = subscribeChar; // 订阅通知 try { await subscribeChar.setNotifyValue(true); AppLogger.info('✅ 已订阅通知'); } catch (e) { AppLogger.warn('⚠️ 订阅通知失败,尝试继续连接: $e'); // 即使订阅失败,也继续连接过程 } // 监听数据 _characteristicDataSubscription?.cancel(); _characteristicDataSubscription = subscribeChar.onValueReceived.listen(_handleListenCharacteristicData); AppLogger.highlight('✨✨✨ ✅ 设备连接设置完成 ✨✨✨'); // 处理待发送的命令 await _processPendingCommands(); return true; } catch (e, stackTrace) { AppLogger.error('❌ 设置设备连接失败', error: e, stackTrace: stackTrace); _connectedDevice = null; _writeCharacteristic = null; _subscriptionCharacteristic = null; return false; } } /// ✨✨✨ 处理待发送的命令 ✨✨✨ Future _processPendingCommands() async { if (_pendingCommands.isEmpty) return; AppLogger.info('📦 处理待发送命令: ${_pendingCommands.length}个'); List<_PendingCommand> commandsToProcess = List.from(_pendingCommands); _pendingCommands.clear(); for (_PendingCommand pendingCmd in commandsToProcess) { try { AppLogger.info('🚀 发送待处理命令: ${pendingCmd.commandId}'); dynamic result = await sendCommand( command: pendingCmd.command, targetDeviceId: pendingCmd.targetDeviceId, targetDeviceName: pendingCmd.targetDeviceName, timeout: pendingCmd.timeout, ); pendingCmd.completer.complete(result); } catch (e) { pendingCmd.completer.completeError(e); } } } /// ✨✨✨ 生成命令唯一标识 ✨✨✨ String _generateCommandKey(BaseBleCommand command) { return '${command.runtimeType}_${DateTime.now().millisecondsSinceEpoch}'; } /// ✨✨✨ 清理命令等待器 ✨✨✨ void _cleanupCommandWaiter(String commandKey) { // 从命令ID映射中移除 _commandIdToKeyMap.removeWhere((commandId, key) => key == commandKey); _commandResponseWaiters.remove(commandKey); Timer? timer = _commandTimeouts.remove(commandKey); timer?.cancel(); } }