starwork_flutter/lib/ble/ble_service.dart

1079 lines
42 KiB
Dart
Raw Permalink Normal View History

2025-09-06 15:42:26 +08:00
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';
2025-09-09 15:42:31 +08:00
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';
2025-09-06 15:42:26 +08:00
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();
}
2025-09-06 15:42:26 +08:00
class BleService {
// 私有构造函数
BleService._() {
// ✅ 这里就是单例初始化的地方
// 只会执行一次(第一次获取实例时)
_initialize();
}
// 静态实例
static final BleService _instance = BleService._();
// 工厂构造函数,提供全局访问点
factory BleService() => _instance;
2025-09-08 18:16:48 +08:00
/// 用于存储最新的私钥避免在解密时需要异步读取SharedPreferences
static List<int>? _cachedPrivateKey;
/// 获取缓存的私钥
static List<int>? get cachedPrivateKey => _cachedPrivateKey;
/// 设置缓存的私钥
static void setCachedPrivateKey(List<int> privateKey) {
_cachedPrivateKey = List.from(privateKey);
}
2025-09-06 15:42:26 +08:00
/// 用来存储搜索到的设备,并用于去重和过滤
final Map<String, ScanResult> _discoveredDevices = {};
// 使用命令管理器处理数据包
final BleCommandManager bleCommandManager = BleCommandManager();
/// mtu大小
int _mtu = 23;
2025-09-06 15:42:26 +08:00
/// 用来监听蓝牙适配器状态的订阅流
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;
2025-09-06 15:42:26 +08:00
// 内部维护的蓝牙状态
BluetoothAdapterState _bluetoothAdapterState = BluetoothAdapterState.unknown;
// 内部维护的设备连接状态
BluetoothConnectionState _bluetoothConnectionState = BluetoothConnectionState.disconnected;
2025-09-06 15:42:26 +08:00
/// 提供外部获取蓝牙适配器方法
BluetoothAdapterState get bluetoothAdapterState => _bluetoothAdapterState;
/// 搜索状态
bool get isScanningNow => FlutterBluePlus.isScanningNow;
/// 获取当前连接的设备
BluetoothDevice? get connectedDevice => _connectedDevice;
2025-09-06 15:42:26 +08:00
/// 初始化服务时执行的
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),
2025-09-06 15:42:26 +08:00
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();
}
2025-09-06 15:42:26 +08:00
}
2025-09-06 15:42:26 +08:00
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);
}
2025-09-06 15:42:26 +08:00
}
} else {
// 可选:更新 RSSI
_discoveredDevices[deviceId] = result;
2025-09-06 15:42:26 +08:00
}
}
},
onError: (e) => AppLogger.error('搜索设备时遇到错误:' + e),
);
AppLogger.highlight('✅ 蓝牙搜索已成功启动');
// 监听搜索状态变化
_monitorScanningState();
} catch (e, stackTrace) {
AppLogger.error('启动蓝牙搜索失败', error: e, stackTrace: stackTrace);
}
} else {
AppLogger.error('❌ 蓝牙适配器未开启,当前状态: $_bluetoothAdapterState');
2025-09-06 15:42:26 +08:00
}
}
/// 监控搜索状态变化
void _monitorScanningState() {
// 取消之前的定时器
_scanningMonitorTimer?.cancel();
_scanningMonitorTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
bool currentlyScanning = FlutterBluePlus.isScanningNow;
if (!currentlyScanning) {
AppLogger.highlight('🔍 搜索已停止 (监控器检测)');
timer.cancel();
_scanningMonitorTimer = null;
}
});
}
2025-09-06 15:42:26 +08:00
/// 停止扫描
void stopBluetoothSearch() async {
var isScanningNow = FlutterBluePlus.isScanningNow;
if (isScanningNow) {
FlutterBluePlus.stopScan();
}
// 取消搜索状态监控定时器
_scanningMonitorTimer?.cancel();
_scanningMonitorTimer = null;
2025-09-06 15:42:26 +08:00
/// 清空搜索到的设备
_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;
2025-09-09 15:42:31 +08:00
// 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}');
}
2025-09-08 18:16:48 +08:00
/// 用于存储分包接收的数据
final Map<int, List<int>> _pendingPackets = {};
/// 监听订阅值变化
2025-09-08 18:16:48 +08:00
void _handleListenCharacteristicData(List<int> data) {
AppLogger.highlight('✨✨✨ 📨 收到订阅数据:$data (长度: ${data.length}) ✨✨✨');
// 解析数据
if (data.isNotEmpty) {
2025-09-08 18:16:48 +08:00
// ✨✨✨ 处理分包数据重组 ✨✨✨
List<int>? completePacket = _reassemblePacket(data);
2025-09-09 15:42:31 +08:00
2025-09-08 18:16:48 +08:00
if (completePacket != null) {
// 如果有完整数据包,进行处理
dynamic result = bleCommandManager.handleResponse(completePacket);
2025-09-09 15:42:31 +08:00
2025-09-08 18:16:48 +08:00
if (result != null) {
// 触发命令响应等待器
_triggerCommandResponseWaiters(result);
} else {
AppLogger.warn('⚠️ 数据包解析失败或不匹配任何命令');
}
}
} else {
AppLogger.warn('✨✨✨ ⚠️ 收到空数据 ✨✨✨');
}
}
2025-09-08 18:16:48 +08:00
/// ✨✨✨ 重组分包数据 ✨✨✨
/// [data] 接收到的数据片段
/// 返回完整的数据包如果数据不完整则返回null
List<int>? _reassemblePacket(List<int> data) {
try {
// 1. 检查是否是包头开始的数据
2025-09-09 15:42:31 +08:00
if (data.length >= 4 &&
data[0] == BaseBleCommand.PACKET_HEADER[0] &&
data[1] == BaseBleCommand.PACKET_HEADER[1] &&
data[2] == BaseBleCommand.PACKET_HEADER[2] &&
2025-09-08 18:16:48 +08:00
data[3] == BaseBleCommand.PACKET_HEADER[3]) {
// 这是一个新包的开始
// 首先检查是否已有未完成的包,如果有则丢弃(超时处理)
if (_pendingPackets.isNotEmpty) {
AppLogger.warn('⚠️ 检测到新包开始,丢弃未完成的包');
_pendingPackets.clear();
}
2025-09-09 15:42:31 +08:00
2025-09-08 18:16:48 +08:00
// 解析包头信息以获取预期长度
if (data.length >= 12) {
// 解析包序号 (大端序)
int packetSequence = (data[5] << 8) | data[6];
2025-09-09 15:42:31 +08:00
2025-09-08 18:16:48 +08:00
// 解析数据长度 (大端序: 高16位加密后长度 + 低16位原始长度)
int combinedLength = (data[8] << 24) | (data[9] << 16) | (data[10] << 8) | data[11];
int encryptedDataLength = (combinedLength >> 16) & 0xFFFF;
int originalDataLength = combinedLength & 0xFFFF;
2025-09-09 15:42:31 +08:00
2025-09-08 18:16:48 +08:00
// 根据加密类型确定实际数据长度
int encryptType = data[7] & 0x0F;
int actualDataLength = (encryptType == BaseBleCommand.ENCRYPT_TYPE_PLAIN) ? originalDataLength : encryptedDataLength;
2025-09-09 15:42:31 +08:00
2025-09-08 18:16:48 +08:00
// 计算完整包的预期长度
int expectedTotalLength = 12 + actualDataLength + 2; // 包头到数据长度(12) + 数据块 + CRC(2)
2025-09-09 15:42:31 +08:00
2025-09-08 18:16:48 +08:00
AppLogger.debug('📦 新包开始: 序号=$packetSequence, 预期长度=$expectedTotalLength, 实际长度=${data.length}');
2025-09-09 15:42:31 +08:00
2025-09-08 18:16:48 +08:00
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]!;
2025-09-09 15:42:31 +08:00
2025-09-08 18:16:48 +08:00
// 追加数据
pendingData.addAll(data);
_pendingPackets[packetSequence] = pendingData;
2025-09-09 15:42:31 +08:00
2025-09-08 18:16:48 +08:00
AppLogger.debug('📥 追加数据片段: 当前长度${pendingData.length}字节');
2025-09-09 15:42:31 +08:00
2025-09-08 18:16:48 +08:00
// 尝试解析包头信息以检查是否完整
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;
2025-09-09 15:42:31 +08:00
2025-09-08 18:16:48 +08:00
// 根据加密类型确定实际数据长度
int encryptType = pendingData[7] & 0x0F;
int actualDataLength = (encryptType == BaseBleCommand.ENCRYPT_TYPE_PLAIN) ? originalDataLength : encryptedDataLength;
2025-09-09 15:42:31 +08:00
2025-09-08 18:16:48 +08:00
// 计算完整包的预期长度
int expectedTotalLength = 12 + actualDataLength + 2; // 包头到数据长度(12) + 数据块 + CRC(2)
2025-09-09 15:42:31 +08:00
2025-09-08 18:16:48 +08:00
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;
2025-09-09 15:42:31 +08:00
} else {
// 如果找不到对应的completer或者已经完成从映射中移除
_commandIdToKeyMap.remove(commandId);
}
}
2025-09-09 15:42:31 +08:00
// 如果没有精确匹配,在重发场景中,我们应该完成所有匹配的等待器
// 而不是只完成最近的一个
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);
2025-09-09 15:42:31 +08:00
completedKeys.add(mostRecentKey);
}
2025-09-09 15:42:31 +08:00
}
// 清理已完成的等待器
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),
2025-09-09 15:42:31 +08:00
int maxRetries = 3, // 最大重试次数
int retryCount = 0, // 当前重试次数
String? existingCommandKey, // 用于重发时传递已有的命令键
}) async {
2025-09-09 15:42:31 +08:00
AppLogger.highlight('✨✨✨ 🚀 开始发送蓝牙命令: ${command.runtimeType} (重试次数: $retryCount) ✨✨✨');
try {
// 1. 检查连接状态
bool isConnected = await _ensureDeviceConnected(
targetDeviceId: targetDeviceId,
targetDeviceName: targetDeviceName,
autoConnect: autoConnectIfNeeded,
searchTimeout: searchTimeoutIfNeeded,
);
if (!isConnected) {
2025-09-09 15:42:31 +08:00
AppLogger.error('❌ 设备未连接,无法发送命令');
throw Exception('设备未连接,无法发送命令');
}
2025-09-09 15:42:31 +08:00
// 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) {
2025-09-09 15:42:31 +08:00
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. 设置命令响应等待器
2025-09-09 15:42:31 +08:00
String commandKey = existingCommandKey ?? _generateCommandKey(command);
Completer<T?> responseCompleter = Completer<T?>();
_commandResponseWaiters[commandKey] = responseCompleter;
2025-09-09 15:42:31 +08:00
// 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) {
2025-09-09 15:42:31 +08:00
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}字节)');
2025-09-09 15:42:31 +08:00
// 检查设备是否仍然连接
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;
2025-09-09 15:42:31 +08:00
// 9. 检查是否需要重发(通用重发机制)
// 只对实现了RetryableBleCommand接口的命令进行重发检查
2025-09-09 15:43:10 +08:00
// 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;
// }
// }
// }
2025-09-09 15:42:31 +08:00
AppLogger.highlight('✨✨✨ ✅ 命令发送成功,收到应答: $response ✨✨✨');
return response;
} catch (e, stackTrace) {
AppLogger.error('❌ 发送蓝牙命令失败', error: e, stackTrace: stackTrace);
rethrow;
2025-09-06 15:42:26 +08:00
}
}
/// 将命令对象映射到 commandKey特定命令需手动注册 cmdId
void _registerCommandId(dynamic command, String commandKey) {
try {
final int? commandId = switch (command) {
BleCmdGetPublicKey() => BleCmdGetPublicKey.cmdId,
BleCmdGetPrivateKey() => BleCmdGetPrivateKey.cmdId,
BleCmdReadLockStatus() => BleCmdReadLockStatus.cmdId,
2025-09-09 15:42:31 +08:00
BleCmdAddAdmin() => BleCmdAddAdmin.cmdId,
// 可在此添加更多命令类型
// BleCmdAnother() => BleCmdAnother.cmdId,
_ => null, // 默认情况:无法识别的命令类型
};
if (commandId != null) {
2025-09-09 15:42:31 +08:00
// 检查是否已存在该命令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');
}
}
2025-09-06 15:42:26 +08:00
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();
/// 重置连接状态
2025-09-06 15:42:26 +08:00
_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 {
2025-09-09 15:42:31 +08:00
AppLogger.debug('🔍 检查设备连接状态...');
// 1. 检查当前连接状态
if (_connectedDevice != null && _bluetoothConnectionState == BluetoothConnectionState.connected) {
2025-09-09 15:42:31 +08:00
AppLogger.debug('📱 当前已连接设备: ${_connectedDevice!.platformName} (${_connectedDevice!.remoteId})');
// 如果指定了目标设备ID检查是否匹配
if (targetDeviceId != null && _connectedDevice!.remoteId.toString() != targetDeviceId) {
AppLogger.info('🔄 当前连接的设备与目标不匹配,需要切换连接');
2025-09-09 15:42:31 +08:00
AppLogger.info('📱 当前设备: ${_connectedDevice!.remoteId},目标设备: $targetDeviceId');
await _connectedDevice!.disconnect();
_connectedDevice = null;
_writeCharacteristic = null;
_subscriptionCharacteristic = null;
} else {
AppLogger.info('✅ 设备已连接,无需重新连接');
return true;
}
2025-09-09 15:42:31 +08:00
} 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;
2025-09-09 15:42:31 +08:00
AppLogger.debug('🔍 当前已连接设备数量: ${connectedDevices.length}');
if (targetDeviceId != null) {
for (BluetoothDevice device in connectedDevices) {
2025-09-09 15:42:31 +08:00
AppLogger.debug('📱 检查已连接设备: ${device.platformName} (${device.remoteId})');
if (device.remoteId.toString() == targetDeviceId) {
2025-09-09 15:42:31 +08:00
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}');
2025-09-09 15:42:31 +08:00
// 尝试连接设备最多重试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; // 最后一次尝试失败,重新抛出异常
}
}
}
2025-09-09 15:42:31 +08:00
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) {
2025-09-09 15:42:31 +08:00
AppLogger.debug('📱 发现服务: ${service.uuid}');
if (service.uuid == BleConfig.serviceId) {
targetService = service;
break;
}
}
if (targetService == null) {
2025-09-09 15:42:31 +08:00
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;
2025-09-09 15:42:31 +08:00
AppLogger.debug('🔍 目标服务中的特征值数量: ${targetService.characteristics.length}');
for (BluetoothCharacteristic characteristic in targetService.characteristics) {
2025-09-09 15:42:31 +08:00
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}');
}
}
2025-09-09 15:42:31 +08:00
// 如果没有找到特征值,打印所有可用的特征值
if (writeChar == null || subscribeChar == null) {
2025-09-09 15:42:31 +08:00
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;
// 订阅通知
2025-09-09 15:42:31 +08:00
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();
2025-09-06 15:42:26 +08:00
}
}