starwork_flutter/lib/ble/ble_service.dart

1079 lines
42 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: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<T> {
final BaseBleCommand<T> command;
final String targetDeviceId;
final String? targetDeviceName;
final Duration timeout;
final Completer<T?> 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<int>? _cachedPrivateKey;
/// 获取缓存的私钥
static List<int>? get cachedPrivateKey => _cachedPrivateKey;
/// 设置缓存的私钥
static void setCachedPrivateKey(List<int> privateKey) {
_cachedPrivateKey = List.from(privateKey);
}
/// 用来存储搜索到的设备,并用于去重和过滤
final Map<String, ScanResult> _discoveredDevices = {};
// 使用命令管理器处理数据包
final BleCommandManager bleCommandManager = BleCommandManager();
/// mtu大小
int _mtu = 23;
/// 用来监听蓝牙适配器状态的订阅流
StreamSubscription<BluetoothAdapterState>? _adapterStateSubscription;
/// 用来监听搜索到的设备的订阅流
StreamSubscription<List<ScanResult>>? _scanResultSubscription;
/// 用来监听连接设备时的连接状态
StreamSubscription<BluetoothConnectionState>? _connectionStateSubscription;
/// 用来监听mtu的变化
StreamSubscription<int>? _mtuChangeSubscription;
/// 用来监听订阅服务的数据
StreamSubscription<List<int>>? _characteristicDataSubscription;
/// 当前连接的设备
BluetoothDevice? _connectedDevice;
/// 当前的写入特征值
BluetoothCharacteristic? _writeCharacteristic;
/// 当前的订阅特征值
BluetoothCharacteristic? _subscriptionCharacteristic;
/// ✨✨✨ 命令响应等待器映射 ✨✨✨
final Map<String, Completer<dynamic>> _commandResponseWaiters = {};
/// ✨✨✨ 命令超时定时器映射 ✨✨✨
final Map<String, Timer> _commandTimeouts = {};
/// ✨✨✨ 命令ID到命令键的映射 ✨✨✨
final Map<int, String> _commandIdToKeyMap = {};
/// 待发送命令队列 (用于自动连接后发送)
final List<_PendingCommand<dynamic>> _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<void> _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<BluetoothDevice> 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<ScanResult> 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<BluetoothDevice> 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<int, List<int>> _pendingPackets = {};
/// 监听订阅值变化
void _handleListenCharacteristicData(List<int> data) {
AppLogger.highlight('✨✨✨ 📨 收到订阅数据:$data (长度: ${data.length}) ✨✨✨');
// 解析数据
if (data.isNotEmpty) {
// ✨✨✨ 处理分包数据重组 ✨✨✨
List<int>? 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<int>? _reassemblePacket(List<int> 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<int> 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<int> 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<String> 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<String> 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<T?> sendCommand<T>({
required BaseBleCommand<T> 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<List<int>> packets = command.build();
AppLogger.info('📦 命令数据包数量: ${packets.length}');
// 4. 设置命令响应等待器
String commandKey = existingCommandKey ?? _generateCommandKey(command);
Completer<T?> responseCompleter = Completer<T?>();
_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<int> 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<T>) {
// 明确转换类型以避免静态分析错误
final retryableCommand = command as RetryableBleCommand<T>;
if (retryableCommand.shouldRetry(response)) {
AppLogger.info('🔄 命令${command.runtimeType}需要重发,准备创建重发命令 (重试次数: ${retryCount + 1}/${maxRetries})');
try {
// 创建重发命令
BaseBleCommand<T> retryCommand = retryableCommand.createRetryCommand(response);
// 重新发送命令,传递现有的命令键以确保响应正确处理
AppLogger.info('🔄 使用重发机制重新发送命令');
return await sendCommand<T>(
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<dynamic> 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<bool> _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<BluetoothDevice> 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<BluetoothDevice?> _searchForTargetDevice({
String? targetDeviceId,
String? targetDeviceName,
Duration searchTimeout = const Duration(seconds: 15),
}) async {
Completer<BluetoothDevice?> searchCompleter = Completer<BluetoothDevice?>();
// 启动搜索
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<bool> _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<bool> _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<BluetoothService> 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<void> _processPendingCommands() async {
if (_pendingCommands.isEmpty) return;
AppLogger.info('📦 处理待发送命令: ${_pendingCommands.length}');
List<_PendingCommand<dynamic>> commandsToProcess = List.from(_pendingCommands);
_pendingCommands.clear();
for (_PendingCommand<dynamic> 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<T>(BaseBleCommand<T> 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();
}
}