From 7b57957e5b74e266258abcb9dac6bd854b2ccfe7 Mon Sep 17 00:00:00 2001 From: liyi Date: Fri, 12 Sep 2025 10:35:07 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=9B=A2=E9=98=9F?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/api/api_path.dart | 2 + lib/api/base_api_service.dart | 3 +- .../bind_team_star_cloud_account_request.dart | 36 + .../request/change_current_team_request.dart | 24 + lib/api/service/team_api_service.dart | 28 + lib/ble/ble_config.dart | 12 - lib/ble/ble_service.dart | 1078 ----------------- lib/ble/command/base/base_ble_command.dart | 401 ------ .../base/base_ble_response_parser.dart | 153 --- .../command/base/retryable_ble_command.dart | 0 lib/ble/command/ble_command_manager.dart | 270 ----- .../command/request/ble_cmd_add_admin.dart | 230 ---- .../request/ble_cmd_get_private_key.dart | 113 -- .../request/ble_cmd_get_public_key.dart | 64 - .../request/ble_cmd_read_lock_status.dart | 87 -- .../response/ble_cmd_add_admin_parser.dart | 112 -- .../ble_cmd_get_private_key_parser.dart | 145 --- .../ble_cmd_get_public_key_parser.dart | 131 -- .../ble_cmd_read_lock_status_parser.dart | 299 ----- .../model/ble_add_admin_response.dart | 43 - lib/ble/constant/lock_ble_constant.dart | 19 - .../exceptional/ble_connected_exception.dart | 3 - lib/ble/model/scan_device_info.dart | 26 - .../search_device_controller.dart | 11 - .../searchDevice/search_device_view.dart | 2 - lib/views/main/main_controller.dart | 19 + lib/views/main/main_view.dart | 4 +- .../use_case_setting_controller.dart | 32 +- 28 files changed, 141 insertions(+), 3206 deletions(-) create mode 100644 lib/api/model/team/request/bind_team_star_cloud_account_request.dart create mode 100644 lib/api/model/team/request/change_current_team_request.dart delete mode 100644 lib/ble/ble_config.dart delete mode 100644 lib/ble/ble_service.dart delete mode 100644 lib/ble/command/base/base_ble_command.dart delete mode 100644 lib/ble/command/base/base_ble_response_parser.dart delete mode 100644 lib/ble/command/base/retryable_ble_command.dart delete mode 100644 lib/ble/command/ble_command_manager.dart delete mode 100644 lib/ble/command/request/ble_cmd_add_admin.dart delete mode 100644 lib/ble/command/request/ble_cmd_get_private_key.dart delete mode 100644 lib/ble/command/request/ble_cmd_get_public_key.dart delete mode 100644 lib/ble/command/request/ble_cmd_read_lock_status.dart delete mode 100644 lib/ble/command/response/ble_cmd_add_admin_parser.dart delete mode 100644 lib/ble/command/response/ble_cmd_get_private_key_parser.dart delete mode 100644 lib/ble/command/response/ble_cmd_get_public_key_parser.dart delete mode 100644 lib/ble/command/response/ble_cmd_read_lock_status_parser.dart delete mode 100644 lib/ble/command/response/model/ble_add_admin_response.dart delete mode 100644 lib/ble/constant/lock_ble_constant.dart delete mode 100644 lib/ble/exceptional/ble_connected_exception.dart delete mode 100644 lib/ble/model/scan_device_info.dart diff --git a/lib/api/api_path.dart b/lib/api/api_path.dart index bf193cd..34721cc 100644 --- a/lib/api/api_path.dart +++ b/lib/api/api_path.dart @@ -5,4 +5,6 @@ class ApiPath { static const String allTeamList = "/v1/team/teamListAll"; static const String sceneList = "/v1/team/sceneList"; static const String createTeam = "/v1/team/createTeam"; + static const String changeTeam = "/v1/team/changeTeam"; + static const String bindTeamStarCloudAccount = "/v1/team/bindStarCloudAccount"; } diff --git a/lib/api/base_api_service.dart b/lib/api/base_api_service.dart index ff15368..df62f8d 100644 --- a/lib/api/base_api_service.dart +++ b/lib/api/base_api_service.dart @@ -18,11 +18,12 @@ class BaseApiService { dio.options.baseUrl = F.apiHost; dio.options.connectTimeout = const Duration(seconds: 30); dio.options.receiveTimeout = const Duration(seconds: 30); + dio.options.headers['content-type'] = 'application/json'; + // 添加拦截器 dio.interceptors.add(InterceptorsWrapper( onRequest: (options, handler) { var token = SharedPreferencesUtils.getString(CacheKeys.token); - AppLogger.info('token:${token}'); if (token != null) { options.headers['Authorization'] = 'Bearer $token'; } diff --git a/lib/api/model/team/request/bind_team_star_cloud_account_request.dart b/lib/api/model/team/request/bind_team_star_cloud_account_request.dart new file mode 100644 index 0000000..f80ca11 --- /dev/null +++ b/lib/api/model/team/request/bind_team_star_cloud_account_request.dart @@ -0,0 +1,36 @@ +class BindTeamStarCloudAccountRequest { + String teamId; + String teamNo; + String username; + String password; + + BindTeamStarCloudAccountRequest({ + required this.teamId, + required this.teamNo, + required this.username, + required this.password, + }); + + Map toJson() { + final Map data = {}; + data['teamId'] = teamId; + data['teamNo'] = teamNo; + data['username'] = username; + data['password'] = password; + return data; + } + + factory BindTeamStarCloudAccountRequest.fromJson(Map json) { + return BindTeamStarCloudAccountRequest( + teamId: json['teamId'] as String, + teamNo: json['teamNo'] as String, + username: json['username'] as String, + password: json['password'] as String, + ); + } + + @override + String toString() { + return 'BindTeamStarCloudAccountRequest{teamId: $teamId, teamNo: $teamNo, username: $username, password: $password}'; + } +} diff --git a/lib/api/model/team/request/change_current_team_request.dart b/lib/api/model/team/request/change_current_team_request.dart new file mode 100644 index 0000000..3089a74 --- /dev/null +++ b/lib/api/model/team/request/change_current_team_request.dart @@ -0,0 +1,24 @@ +class ChangeCurrentTeamRequest { + String teamNo; + + ChangeCurrentTeamRequest({ + required this.teamNo, + }); + + Map toJson() { + return { + "teamNo": teamNo, + }; + } + + factory ChangeCurrentTeamRequest.fromJson(Map json) { + return ChangeCurrentTeamRequest( + teamNo: json["teamNo"], + ); + } + + @override + String toString() { + return 'ChangeCurrentTeamRequest{teamNo: $teamNo}'; + } +} diff --git a/lib/api/service/team_api_service.dart b/lib/api/service/team_api_service.dart index 154f657..8ad960f 100644 --- a/lib/api/service/team_api_service.dart +++ b/lib/api/service/team_api_service.dart @@ -2,6 +2,8 @@ import 'package:get/get.dart'; import 'package:starwork_flutter/api/api_path.dart'; import 'package:starwork_flutter/api/api_response.dart'; import 'package:starwork_flutter/api/base_api_service.dart'; +import 'package:starwork_flutter/api/model/team/request/bind_team_star_cloud_account_request.dart'; +import 'package:starwork_flutter/api/model/team/request/change_current_team_request.dart'; import 'package:starwork_flutter/api/model/team/request/create_team_request.dart'; import 'package:starwork_flutter/api/model/team/response/all_team_list_response.dart'; import 'package:starwork_flutter/api/model/team/response/create_team_response.dart'; @@ -47,4 +49,30 @@ class TeamApiService { fromJson: (data) => SceneInfoResponseList.fromJson(data), ); } + + // 切换当前团队 + Future> requestChangeCurrentTeam({ + required ChangeCurrentTeamRequest request, + }) { + return _api.makeRequest( + // 通过实例调用 + path: ApiPath.changeTeam, + method: HttpConstant.post, + data: request, + fromJson: (data) {}, + ); + } + + // 团队绑定星云账号 + Future> requestBindTeamStarCloudAccount({ + required BindTeamStarCloudAccountRequest request, + }) { + return _api.makeRequest( + // 通过实例调用 + path: ApiPath.bindTeamStarCloudAccount, + method: HttpConstant.post, + data: request, + fromJson: (data) => SceneInfoResponseList.fromJson(data), + ); + } } diff --git a/lib/ble/ble_config.dart b/lib/ble/ble_config.dart deleted file mode 100644 index 30e1452..0000000 --- a/lib/ble/ble_config.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:flutter_blue_plus/flutter_blue_plus.dart'; - -class BleConfig { - - static Guid serviceId = Guid('fff0'); - - // 用来订阅的特征id - static Guid characteristicIdSubscription = Guid('fff1'); - - // 用来写入的特征id - static Guid characteristicIdWrite = Guid('fff2'); -} diff --git a/lib/ble/ble_service.dart b/lib/ble/ble_service.dart deleted file mode 100644 index 3cb6f12..0000000 --- a/lib/ble/ble_service.dart +++ /dev/null @@ -1,1078 +0,0 @@ -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(); - } -} diff --git a/lib/ble/command/base/base_ble_command.dart b/lib/ble/command/base/base_ble_command.dart deleted file mode 100644 index bd8a305..0000000 --- a/lib/ble/command/base/base_ble_command.dart +++ /dev/null @@ -1,401 +0,0 @@ -import 'dart:typed_data'; -import 'package:starwork_flutter/base/app_logger.dart'; - -/// ✨✨✨ 数据包解析结果类 ✨✨✨ -class ParsedPacket { - final bool isValid; - final int packetType; - final int packetSequence; - final int version; - final int encryptType; - final int encryptedDataLength; - final int originalDataLength; - final List data; - final int crc16; - final String? errorMessage; - - ParsedPacket({ - required this.isValid, - required this.packetType, - required this.packetSequence, - required this.version, - required this.encryptType, - required this.encryptedDataLength, - required this.originalDataLength, - required this.data, - required this.crc16, - this.errorMessage, - }); - - @override - String toString() { - if (!isValid) { - return 'ParsedPacket(invalid: $errorMessage)'; - } - return 'ParsedPacket(type: 0x${packetType.toRadixString(16).padLeft(2, '0')}, ' + - 'seq: $packetSequence, version: ${version >> 4}, encrypt: $encryptType, ' + - 'dataLen: $originalDataLength, crc: 0x${crc16.toRadixString(16).padLeft(4, '0')})'; - } -} - -/// ✨✨✨ 蓝牙命令基类 - 提供数据包组装功能 ✨✨✨ -abstract class BaseBleCommand { - /// 包头固定值: 0XEF01EE02 - static const List PACKET_HEADER = [0xEF, 0x01, 0xEE, 0x02]; - - /// 包类型 - static const int PACKET_TYPE_REQUEST = 0x01; // 请求包 - static const int PACKET_TYPE_RESPONSE = 0x11; // 应答包 - - /// 包版本 (高4位) - static const int PACKET_VERSION = 0x20; // 版本2.0 - - /// 加密类型 (低4位) - static const int ENCRYPT_TYPE_PLAIN = 0x00; // 明文 - static const int ENCRYPT_TYPE_AES128 = 0x01; // AES128 - static const int ENCRYPT_TYPE_SM4_PRESET = 0x02; // SM4(事先约定密钥) - static const int ENCRYPT_TYPE_SM4_DEVICE = 0x03; // SM4(设备指定密钥) - - /// 最大单包数据长度 (可根据MTU调整) - static int MAX_PACKET_DATA_SIZE = 100; - - /// 全局包序号计数器 - static int _globalPacketSequence = 1; - - /// ✨✨✨ 获取下一个包序号 ✨✨✨ - static int getNextPacketSequence() { - int sequence = _globalPacketSequence; - _globalPacketSequence++; - if (_globalPacketSequence > 0xFFFF) { - _globalPacketSequence = 1; // 2字节范围内循环 - } - return sequence; - } - - /// ✨✨✨ 组装包头 ✨✨✨ - /// [packetType] 包类型 (0x01请求包, 0x11应答包) - /// [packetSequence] 包序号 (2字节) - /// [encryptType] 加密类型 (0:明文, 1:AES128, 2:SM4事先约定, 3:SM4设备指定) - /// [dataLength] 数据长度 (高16位:加密后长度, 低16位:原始长度) - static List buildPacketHeader({ - required int packetType, - required int packetSequence, - int encryptType = ENCRYPT_TYPE_PLAIN, - required int encryptedDataLength, - required int originalDataLength, - }) { - List header = []; - - // 1. 包头 (4字节) - header.addAll(PACKET_HEADER); - - // 2. 包类型 (1字节) - header.add(packetType); - - // 3. 包序号 (2字节,大端序) - header.add((packetSequence >> 8) & 0xFF); // 高字节 - header.add(packetSequence & 0xFF); // 低字节 - - // 4. 包标识 (1字节: 高4位版本 + 低4位加密类型) - int packetFlag = (PACKET_VERSION & 0xF0) | (encryptType & 0x0F); - header.add(packetFlag); - - // 5. 数据长度 (4字节,大端序: 高16位加密后长度 + 低16位原始长度) - int combinedLength = (encryptedDataLength << 16) | originalDataLength; - header.add((combinedLength >> 24) & 0xFF); // 最高字节 - header.add((combinedLength >> 16) & 0xFF); // 次高字节 - header.add((combinedLength >> 8) & 0xFF); // 次低字节 - header.add(combinedLength & 0xFF); // 最低字节 - - return header; - } - - /// ✨✨✨ 组装包尾 (CRC16校验) ✨✨✨ - /// [packetData] 完整的数据包(包头+数据块) - static List buildPacketTail(List packetData) { - int crc16 = calculateCRC16Kermit(packetData); - - // CRC16校验位 (2字节,大端序) - List tail = [ - (crc16 >> 8) & 0xFF, // 高字节 - crc16 & 0xFF, // 低字节 - ]; - - return tail; - } - - /// ✨✨✨ 计算CRC16-KERMIT校验 ✨✨✨ - static int calculateCRC16Kermit(List data) { - const int polynomial = 0x1021; // CRC16-KERMIT多项式 - const int initialValue = 0x0000; - - int crc = initialValue; - - for (int byte in data) { - crc ^= (byte << 8); - for (int i = 0; i < 8; i++) { - if ((crc & 0x8000) != 0) { - crc = (crc << 1) ^ polynomial; - } else { - crc <<= 1; - } - crc &= 0xFFFF; // 保持16位 - } - } - - // KERMIT CRC需要交换字节序 - int result = ((crc & 0xFF) << 8) | ((crc >> 8) & 0xFF); - return result; - } - - /// ✨✨✨ 分包处理 - 将大数据分割成多个包 ✨✨✨ - /// [originalData] 原始数据 - /// [encryptType] 加密类型 - /// 返回多个完整的数据包 - static List> splitIntoPackets({ - required List originalData, - int encryptType = ENCRYPT_TYPE_PLAIN, - }) { - List> packets = []; - - // 计算需要分成多少包 - int totalPackets = (originalData.length / MAX_PACKET_DATA_SIZE).ceil(); - - for (int i = 0; i < totalPackets; i++) { - int startIndex = i * MAX_PACKET_DATA_SIZE; - int endIndex = (startIndex + MAX_PACKET_DATA_SIZE > originalData.length) ? originalData.length : startIndex + MAX_PACKET_DATA_SIZE; - - // 获取当前包的数据 - List currentPacketData = originalData.sublist(startIndex, endIndex); - int originalDataLength = currentPacketData.length; - - // ✨✨✨ 根据加密类型计算加密后数据长度 ✨✨✨ - int encryptedDataLength = originalDataLength; // 默认情况下加密后长度与原始长度相同 - if (encryptType != ENCRYPT_TYPE_PLAIN) { - // TODO: 根据具体加密算法计算加密后长度 - // 例如AES128加密通常会填充到16字节的倍数 - switch (encryptType) { - case ENCRYPT_TYPE_AES128: - // AES128加密填充到16字节边界 - encryptedDataLength = ((originalDataLength + 15) ~/ 16) * 16; - break; - case ENCRYPT_TYPE_SM4_PRESET: - case ENCRYPT_TYPE_SM4_DEVICE: - // SM4加密填充到16字节边界 - encryptedDataLength = ((originalDataLength + 15) ~/ 16) * 16; - break; - } - } - - if (i == 0) { - // 获取分包序号 - int packetSequence = getNextPacketSequence(); - - // 组装包头 - List header = buildPacketHeader( - packetType: PACKET_TYPE_REQUEST, - packetSequence: packetSequence, - encryptType: encryptType, - encryptedDataLength: originalData.length, - originalDataLength: originalData.length, - ); - // 组装完整包 (包头 + 数据) - List packetWithHeader = []; - packetWithHeader.addAll(header); - packetWithHeader.addAll(currentPacketData); - - // 计算并添加包尾 - List tail = buildPacketTail(packetWithHeader); - - // 完整的数据包 - List completePacket = []; - completePacket.addAll(packetWithHeader); - completePacket.addAll(tail); - - packets.add(completePacket); - } else { - packets.add(currentPacketData); - } - } - - return packets; - } - - /// ✨✨✨ 抽象方法 - 子类需要实现具体的数据构建逻辑 ✨✨✨ - List buildData(); - - /// ✨✨✨ 获取当前命令的加密类型 - 子类可以重写此方法 ✨✨✨ - /// 默认返回明文类型,子类可以根据需要重写 - int getEncryptType() { - return ENCRYPT_TYPE_PLAIN; - } - - /// ✨✨✨ 构建完整的命令包 ✨✨✨ - /// 使用子类定义的加密类型自动组装数据包 - List> build() { - // 获取子类实现的数据 - List commandData = buildData(); - - // 使用子类定义的加密类型 - int encryptType = getEncryptType(); - - // 分包处理 - List> packets = splitIntoPackets( - originalData: commandData, - encryptType: encryptType, - ); - - return packets; - } - - /// ✨✨✨ 解析接收到的数据包 ✨✨✨ - /// [rawData] 接收到的原始数据包字节 - /// 返回解析结果,包含包的各个字段信息 - static ParsedPacket parsePacket(List rawData) { - try { - // 1. 检查最小长度 (包头4 + 包类型1 + 包序号2 + 包标识1 + 数据长度4 + CRC2 = 14字节) - if (rawData.length < 14) { - return ParsedPacket( - isValid: false, - packetType: 0, - packetSequence: 0, - version: 0, - encryptType: 0, - encryptedDataLength: 0, - originalDataLength: 0, - data: [], - crc16: 0, - errorMessage: '数据包长度不足: ${rawData.length}字节 < 14字节', - ); - } - - int offset = 0; - - // 2. 检查包头 - List header = rawData.sublist(offset, offset + 4); - offset += 4; - if (!_isValidHeader(header)) { - return ParsedPacket( - isValid: false, - packetType: 0, - packetSequence: 0, - version: 0, - encryptType: 0, - encryptedDataLength: 0, - originalDataLength: 0, - data: [], - crc16: 0, - errorMessage: '包头不正确: ${header.map((b) => '0x${b.toRadixString(16).padLeft(2, '0')}').join(' ')}', - ); - } - - // 3. 解析包类型 - int packetType = rawData[offset++]; - - // 4. 解析包序号 (大端序) - int packetSequence = (rawData[offset] << 8) | rawData[offset + 1]; - offset += 2; - - // 5. 解析包标识 (高4位版本 + 低4位加密类型) - int packetFlag = rawData[offset++]; - int version = (packetFlag & 0xF0); - int encryptType = (packetFlag & 0x0F); - AppLogger.highlight('加密类型:${encryptType}'); - - // 6. 解析数据长度 (大端序: 高16位加密后长度 + 低16位原始长度) - int combinedLength = (rawData[offset] << 24) | (rawData[offset + 1] << 16) | (rawData[offset + 2] << 8) | rawData[offset + 3]; - offset += 4; - int encryptedDataLength = (combinedLength >> 16) & 0xFFFF; - int originalDataLength = combinedLength & 0xFFFF; - AppLogger.debug('📏 数据长度: 加密后=$encryptedDataLength, 原始=$originalDataLength'); - - // 7. 检查数据包完整性 - // ✨✨✨ 根据加密类型确定实际数据长度 ✨✨✨ - int actualDataLength = (encryptType == ENCRYPT_TYPE_PLAIN) ? originalDataLength : encryptedDataLength; - int expectedTotalLength = 12 + actualDataLength + 2; // 包头到数据长度(12) + 数据块 + CRC(2) - if (rawData.length != expectedTotalLength) { - return ParsedPacket( - isValid: false, - packetType: packetType, - packetSequence: packetSequence, - version: version, - encryptType: encryptType, - encryptedDataLength: encryptedDataLength, - originalDataLength: originalDataLength, - data: [], - crc16: 0, - errorMessage: '数据包长度不匹配: 实际${rawData.length}字节 != 期望${expectedTotalLength}字节', - ); - } - - // 8. 提取数据块 - // ✨✨✨ 根据加密类型提取相应长度的数据 ✨✨✨ - List data = rawData.sublist(offset, offset + actualDataLength); - offset += actualDataLength; - - // // 9. 提取CRC16校验位 (大端序) - int crc16 = (rawData[offset] << 8) | rawData[offset + 1]; - - // - // // 10. 验证CRC16校验 - // List dataToCheck = rawData.sublist(0, rawData.length - 2); // 除去CRC的数据 - // int calculatedCRC = calculateCRC16Kermit(dataToCheck); - // bool crcValid = (calculatedCRC == crc16); - // AppLogger.debug('🔍 CRC16验证: 计算值=0x${calculatedCRC.toRadixString(16).padLeft(4, '0')}, 接收值=0x${crc16.toRadixString(16).padLeft(4, '0')}, ${crcValid ? '✅通过' : '❌失败'}'); - // - // if (!crcValid) { - // return ParsedPacket( - // isValid: false, - // packetType: packetType, packetSequence: packetSequence, version: version, encryptType: encryptType, - // encryptedDataLength: encryptedDataLength, originalDataLength: originalDataLength, data: data, - // crc16: crc16, errorMessage: 'CRC16校验失败: 计算值=0x${calculatedCRC.toRadixString(16).padLeft(4, '0')}, 接收值=0x${crc16.toRadixString(16).padLeft(4, '0')}', - // ); - // } - - return ParsedPacket( - isValid: true, - packetType: packetType, - packetSequence: packetSequence, - version: version, - encryptType: encryptType, - encryptedDataLength: encryptedDataLength, - originalDataLength: originalDataLength, - data: data, - crc16: crc16, - ); - } catch (e) { - AppLogger.error('❌ 数据包解析异常', error: e); - return ParsedPacket( - isValid: false, - packetType: 0, - packetSequence: 0, - version: 0, - encryptType: 0, - encryptedDataLength: 0, - originalDataLength: 0, - data: [], - crc16: 0, - errorMessage: '解析异常: $e', - ); - } - } - - /// ✨✨✨ 验证包头是否正确 ✨✨✨ - static bool _isValidHeader(List header) { - if (header.length != 4) return false; - for (int i = 0; i < 4; i++) { - if (header[i] != PACKET_HEADER[i]) { - return false; - } - } - return true; - } - - // 获取固定长度的数组 - List getFixedLengthList(List data, int length) { - for (int i = 0; i < length; i++) { - data.add(0); - } - return data; - } -} diff --git a/lib/ble/command/base/base_ble_response_parser.dart b/lib/ble/command/base/base_ble_response_parser.dart deleted file mode 100644 index 92b324a..0000000 --- a/lib/ble/command/base/base_ble_response_parser.dart +++ /dev/null @@ -1,153 +0,0 @@ -import 'dart:convert'; - -import 'package:starwork_flutter/base/app_logger.dart'; -import 'package:starwork_flutter/ble/command/base/base_ble_command.dart'; - -/// ✨✨✨ 蓝牙命令应答解析基类 ✨✨✨ -abstract class BaseBleResponseParser { - /// 命令ID - 子类必须定义 - int get commandId; - - /// 命令名称 - 子类可以重写,用于日志显示 - String get commandName => '未知命令(0x${commandId.toRadixString(16).padLeft(4, '0')})'; - - /// ✨✨✨ 解析命令应答数据的抽象方法 ✨✨✨ - /// [parsedPacket] 已解析的数据包基本信息 - /// [rawResponseData] 原始应答数据(仅数据块部分) - /// 返回解析后的业务数据对象 - dynamic parseResponse(ParsedPacket parsedPacket, List rawResponseData); - - /// ✨✨✨ 验证应答数据是否匹配当前命令 ✨✨✨ - /// [parsedPacket] 已解析的数据包 - bool isMatchingResponse(ParsedPacket parsedPacket) { - // 检查包是否有效 - if (!parsedPacket.isValid) { - AppLogger.warn('⚠️ 数据包无效,不匹配任何命令'); - return false; - } - - // 检查是否为应答包 - if (parsedPacket.packetType != BaseBleCommand.PACKET_TYPE_RESPONSE) { - AppLogger.debug('📋 非应答包,类型: 0x${parsedPacket.packetType.toRadixString(16).padLeft(2, '0')}'); - return false; - } - - // 检查数据长度是否足够包含命令ID - if (parsedPacket.data.length < 2) { - AppLogger.warn('⚠️ 应答数据长度不足,无法包含命令ID: ${parsedPacket.data.length}字节'); - return false; - } - - // 提取命令ID (大端序) - int responseCommandId = (parsedPacket.data[0] << 8) | parsedPacket.data[1]; - - // 检查命令ID是否匹配 - bool isMatch = (responseCommandId == commandId); - AppLogger.debug('🔍 命令ID匹配检查: 期望=0x${commandId.toRadixString(16).padLeft(4, '0')}, ' + - '实际=0x${responseCommandId.toRadixString(16).padLeft(4, '0')}, 匹配=${isMatch ? '✅' : '❌'}'); - - return isMatch; - } - - /// ✨✨✨ 完整的应答处理流程 ✨✨✨ - /// [rawPacketData] 接收到的完整数据包 - /// 返回解析后的业务数据,如果不匹配或解析失败则返回null - dynamic handleResponse(List rawPacketData) { - AppLogger.highlight('✨✨✨ 🔄 开始处理${commandName}应答 ✨✨✨'); - - try { - // 1. 解析数据包基本信息 - ParsedPacket parsedPacket = BaseBleCommand.parsePacket(rawPacketData); - - // 2. 检查是否匹配当前命令 - if (!isMatchingResponse(parsedPacket)) { - AppLogger.debug('📝 应答不匹配${commandName},跳过处理'); - return null; - } - - AppLogger.info('🎯 ${commandName}应答匹配成功,开始解析业务数据'); - - // 3. 调用子类的具体解析逻辑 - dynamic result = parseResponse(parsedPacket, parsedPacket.data); - - AppLogger.highlight('✨✨✨ ✅ ${commandName}应答处理完成 ✨✨✨'); - return result; - } catch (e, stackTrace) { - AppLogger.error('❌ ${commandName}应答处理异常', error: e, stackTrace: stackTrace); - return null; - } - } - - /// ✨✨✨ 辅助方法 - 从数据中提取整数(大端序) ✨✨✨ - /// [data] 数据字节数组 - /// [offset] 起始偏移 - /// [length] 字节长度(1, 2, 4, 8) - static int extractInt(List data, int offset, int length) { - if (offset + length > data.length) { - throw ArgumentError('数据长度不足: offset=$offset, length=$length, dataLength=${data.length}'); - } - - int result = 0; - for (int i = 0; i < length; i++) { - result = (result << 8) | data[offset + i]; - } - return result; - } - - /// ✨✨✨ 辅助方法 - 从数据中提取字节数组 ✨✨✨ - /// [data] 源数据 - /// [offset] 起始偏移 - /// [length] 提取长度 - static List extractBytes(List data, int offset, int length) { - if (offset + length > data.length) { - throw ArgumentError('数据长度不足: offset=$offset, length=$length, dataLength=${data.length}'); - } - return data.sublist(offset, offset + length); - } - - /// ✨✨✨ 辅助方法 - 从数据中提取字符串 ✨✨✨ - /// [data] 数据字节数组 - /// [offset] 起始偏移 - /// [length] 字符串字节长度 - /// [removeNullTerminator] 是否移除空终止符 - static String extractString(List data, int offset, int length, {bool removeNullTerminator = true}) { - List stringBytes = extractBytes(data, offset, length); - - if (removeNullTerminator) { - // 找到第一个0字节并截断 - int nullIndex = stringBytes.indexOf(0); - if (nullIndex >= 0) { - stringBytes = stringBytes.sublist(0, nullIndex); - } - } - - return String.fromCharCodes(stringBytes); - } - - /// ✨✨✨ 辅助方法 - 将字节数组转换为十六进制字符串 ✨✨✨ - /// [data] 字节数组 - /// [separator] 分隔符,默认为空格 - static String bytesToHex(List data, {String separator = ' '}) { - return data.map((b) => '0x${b.toRadixString(16).padLeft(2, '0')}').join(separator); - } - - String utf8String(List codeUnits) { - codeUnits.reversed; - final List uniqueList = []; - for (int i = 0; i < codeUnits.length; i++) { - if (codeUnits[i] != 0) { - uniqueList.add(codeUnits[i]); - } - } - uniqueList.reversed; - return utf8.decode(uniqueList).toString(); - } - - String asciiString(List codeUnits) { - String result = ''; - for (final int value in codeUnits) { - result += String.fromCharCode(value); - } - return result; - } -} diff --git a/lib/ble/command/base/retryable_ble_command.dart b/lib/ble/command/base/retryable_ble_command.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/ble/command/ble_command_manager.dart b/lib/ble/command/ble_command_manager.dart deleted file mode 100644 index 140252c..0000000 --- a/lib/ble/command/ble_command_manager.dart +++ /dev/null @@ -1,270 +0,0 @@ -import 'dart:convert'; - -import 'package:starwork_flutter/base/app_logger.dart'; -import 'package:starwork_flutter/ble/ble_service.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/response/ble_cmd_add_admin_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'; - -/// ✨✨✨ 蓝牙命令管理器 - 统一管理所有命令解析器 ✨✨✨ -class BleCommandManager { - // 私有构造函数 - BleCommandManager._() { - // ✅ 这里就是单例初始化的地方 - // 只会执行一次(第一次获取实例时) - _initialize(); - } - - // 单例实例 - static final BleCommandManager _instance = BleCommandManager._(); - - // 工厂构造函数,提供全局访问点 - factory BleCommandManager() => _instance; - - /// 所有已注册的命令解析器 - final Map _parsers = {}; - - /// ✨✨✨ 初始化命令管理器 ✨✨✨ - void _initialize() { - AppLogger.highlight('✨✨✨ 🚀 初始化蓝牙命令管理器 ✨✨✨'); - - // 注册所有命令解析器 - _registerParsers(); - - AppLogger.info('📋 已注册命令解析器数量: ${_parsers.length}'); - _parsers.forEach((commandId, parser) { - AppLogger.debug(' • 0x${commandId.toRadixString(16).padLeft(4, '0')}: ${parser.commandName}'); - }); - - AppLogger.highlight('✨✨✨ ✅ 蓝牙命令管理器初始化完成 ✨✨✨'); - } - - /// ✨✨✨ 注册所有命令解析器 ✨✨✨ - void _registerParsers() { - // 注册获取公钥命令解析器 - registerParser(BleCmdGetPublicKeyParser()); - registerParser(BleCmdGetPrivateKeyParser()); - registerParser(BleCmdReadLockStatusParser()); - registerParser(BleCmdAddAdminParser()); - - // TODO: 在这里注册其他命令解析器 - // registerParser(BleCmdSetPasswordParser()); - // registerParser(BleCmdUnlockParser()); - // 等等... - } - - /// ✨✨✨ 注册命令解析器 ✨✨✨ - void registerParser(BaseBleResponseParser parser) { - _parsers[parser.commandId] = parser; - AppLogger.debug('📝 注册命令解析器: 0x${parser.commandId.toRadixString(16).padLeft(4, '0')} - ${parser.commandName}'); - } - - /// ✨✨✨ 移除命令解析器 ✨✨✨ - void unregisterParser(int commandId) { - BaseBleResponseParser? removed = _parsers.remove(commandId); - if (removed != null) { - AppLogger.debug('🗑️ 移除命令解析器: 0x${commandId.toRadixString(16).padLeft(4, '0')} - ${removed.commandName}'); - } - } - - /// ✨✨✨ 处理接收到的应答数据包 ✨✨✨ - /// [rawPacketData] 完整的原始数据包 - /// 返回解析后的业务数据,如果没有匹配的解析器则返回null - dynamic handleResponse(List rawPacketData) { - AppLogger.debug('📊 收到蓝牙数据 (${rawPacketData.length}字节): ${rawPacketData.map((b) => '0x${b.toRadixString(16).padLeft(2, '0')}').join(' ')}'); - - try { - // ✨✨✨ 首先解析数据包以获取命令ID ✨✨✨ - var parsedPacket = BaseBleCommand.parsePacket(rawPacketData); - - // 检查数据包是否有效 - if (!parsedPacket.isValid) { - AppLogger.warn('⚠️ 数据包无效: ${parsedPacket.errorMessage}'); - return null; - } - - // 检查数据长度是否足够包含命令ID - if (parsedPacket.data.length < 2) { - AppLogger.warn('⚠️ 应答数据长度不足,无法包含命令ID: ${parsedPacket.data.length}字节'); - return null; - } - - // ✨✨✨ 根据加密类型进行解密处理 ✨✨✨ - List processedData = _decryptDataIfNeededSync(parsedPacket); - - // ✨✨✨ 提取命令ID (大端序) ✨✨✨ - int commandId = (processedData[0] << 8) | processedData[1]; - AppLogger.debug('🔍 提取命令ID: 0x${commandId.toRadixString(16).padLeft(4, '0')}'); - - // ✨✨✨ 直接通过命令ID查找对应的解析器 ✨✨✨ - BaseBleResponseParser? parser = _parsers[commandId]; - if (parser != null) { - AppLogger.debug('🎯 找到匹配的解析器: ${parser.commandName}'); - - // 创建一个新的ParsedPacket对象,使用解密后的数据 - ParsedPacket decryptedPacket = ParsedPacket( - isValid: parsedPacket.isValid, - packetType: parsedPacket.packetType, - packetSequence: parsedPacket.packetSequence, - version: parsedPacket.version, - encryptType: parsedPacket.encryptType, - encryptedDataLength: parsedPacket.encryptedDataLength, - originalDataLength: parsedPacket.originalDataLength, - data: processedData, - crc16: parsedPacket.crc16, - errorMessage: parsedPacket.errorMessage, - ); - - // 传递解密后的数据给解析器 - dynamic result = parser.parseResponse(decryptedPacket, processedData); - return result; - } else { - // 没有找到匹配的解析器 - AppLogger.warn('⚠️ 未找到命令ID 0x${commandId.toRadixString(16).padLeft(4, '0')} 对应的解析器'); - _logUnknownPacket(rawPacketData); - return null; - } - } catch (e, stackTrace) { - AppLogger.error('❌ 处理蓝牙应答异常', error: e, stackTrace: stackTrace); - return null; - } - } - - /// ✨✨✨ 根据加密类型解密数据(同步版本) ✨✨✨ - /// [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] 已解析的数据包 - /// 返回解密后的数据或原始数据(如果不需要解密) - Future> _decryptDataIfNeeded(ParsedPacket parsedPacket) async { - // 如果是明文,直接返回原始数据 - 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; - var decrypt = SM4.decrypt( - parsedPacket.data, - key: utf8.encode(platformName!), - mode: SM4CryptoMode.ECB, - ); - return decrypt; - case BaseBleCommand.ENCRYPT_TYPE_SM4_DEVICE: - var lockCommKey = await SharedPreferencesUtils.getString(CacheKeys.lockCommKey); - if (lockCommKey == null) { - AppLogger.warn('⚠️ 解密订阅数据时未找到私钥'); - return parsedPacket.data; - } - var decrypt = SM4.decrypt( - parsedPacket.data, - key: utf8.encode(lockCommKey), - mode: SM4CryptoMode.ECB, - ); - return decrypt; - default: - AppLogger.warn('⚠️ 未知的加密类型: ${parsedPacket.encryptType},使用原始数据'); - return parsedPacket.data; - } - } - - /// ✨✨✨ 根据命令ID获取解析器 ✨✨✨ - BaseBleResponseParser? getParser(int commandId) { - return _parsers[commandId]; - } - - /// ✨✨✨ 获取所有已注册的命令ID ✨✨✨ - List getRegisteredCommandIds() { - return _parsers.keys.toList(); - } - - /// ✨✨✨ 获取所有已注册的解析器信息 ✨✨✨ - Map getRegisteredParsersInfo() { - return _parsers.map((commandId, parser) => MapEntry(commandId, parser.commandName)); - } - - /// ✨✨✨ 记录未知数据包信息 ✨✨✨ - void _logUnknownPacket(List rawPacketData) { - try { - // 尝试基本解析以获取更多信息 - var parsedPacket = BaseBleCommand.parsePacket(rawPacketData); - - if (parsedPacket.isValid && parsedPacket.data.length >= 2) { - int commandId = (parsedPacket.data[0] << 8) | parsedPacket.data[1]; - AppLogger.warn('🔍 未知命令应答: 命令ID=0x${commandId.toRadixString(16).padLeft(4, '0')}, ' + - '包类型=0x${parsedPacket.packetType.toRadixString(16).padLeft(2, '0')}, ' + - '数据长度=${parsedPacket.data.length}字节'); - } else { - AppLogger.warn('🔍 无效或无法识别的数据包'); - } - } catch (e) { - AppLogger.warn('🔍 无法解析的数据包: $e'); - } - } - - /// ✨✨✨ 清除所有解析器 ✨✨✨ - void clear() { - AppLogger.info('🧹 清除所有命令解析器'); - _parsers.clear(); - } - - /// ✨✨✨ 重新初始化 ✨✨✨ - void reinitialize() { - AppLogger.info('🔄 重新初始化命令管理器'); - clear(); - _initialize(); - } -} diff --git a/lib/ble/command/request/ble_cmd_add_admin.dart b/lib/ble/command/request/ble_cmd_add_admin.dart deleted file mode 100644 index 6a5cb18..0000000 --- a/lib/ble/command/request/ble_cmd_add_admin.dart +++ /dev/null @@ -1,230 +0,0 @@ -import 'dart:convert'; - -import 'package:crypto/crypto.dart'; -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/response/ble_cmd_get_private_key_parser.dart'; -import 'package:starwork_flutter/ble/command/response/model/ble_add_admin_response.dart'; -import 'package:starwork_flutter/ble/constant/lock_ble_constant.dart'; -import 'package:starwork_flutter/common/sm4_encipher/sm4.dart'; - -/// ✨✨✨ 增加超级管理员命令类 - 继承BaseBleCommand ✨✨✨ -class BleCmdAddAdmin extends BaseBleCommand { - final String lockId; - final String authUserId; - final String keyId; - final String userId; - final int openMode; - final int keyType; - final int startDate; - final int expireDate; - final int useCountLimit; - final int isRound; - final int weekRound; - final int startHour; - final int startMin; - final int endHour; - final int endMin; - final int role; - final String password; - final List token; - final List publicKey; - final List privateKey; - final int _encryptType; - - /// 指令 ID: 0x3001 - static const int cmdId = 0x3001; - - /// 构造函数 - BleCmdAddAdmin({ - int encryptType = BaseBleCommand.ENCRYPT_TYPE_SM4_DEVICE, - required this.lockId, - required this.authUserId, - required this.keyId, - required this.userId, - this.openMode = 1, // 缺少定义,默认传1 - this.keyType = LockBleConstant.keyTypeTemporary, - required this.startDate, - required this.expireDate, - this.useCountLimit = 0xFFFF, - this.isRound = 0, - this.weekRound = 0, - this.startHour = 0, - this.startMin = 0, - this.endHour = 0, - this.endMin = 0, - this.role = 255, - required this.password, - required this.token, - required this.publicKey, - required this.privateKey, - }) : _encryptType = encryptType { - AppLogger.debug('🔑 BleCmdAddAdmin 创建: LockID="$lockId", 加密类型=$_encryptType'); - } - - /// ✨✨✨ 重写基类方法 - 返回当前命令的加密类型 ✨✨✨ - @override - int getEncryptType() { - return _encryptType; - } - - /// ✨✨✨ 实现BaseBleCommand的抽象方法 - 构建命令数据 ✨✨✨ - @override - List buildData() { - List data = []; - List ebcData = []; - - // 指令类型 - final int type = cmdId; - final double typeDouble = type / 256; - final int type1 = typeDouble.toInt(); - final int type2 = type % 256; - data.add(type1); - data.add(type2); - - // lockId 40 - final int lockIDLength = utf8.encode(lockId).length; - data.addAll(utf8.encode(lockId)); - data = getFixedLengthList(data, 40 - lockIDLength); - - // authUserId 20 - final int authUserIdLength = utf8.encode(authUserId).length; - data.addAll(utf8.encode(authUserId)); - data = getFixedLengthList(data, 20 - authUserIdLength); - - //KeyID 40 - final int keyIDLength = utf8.encode(keyId).length; - data.addAll(utf8.encode(keyId)); - data = getFixedLengthList(data, 40 - keyIDLength); - - //userId 20 - final int userIdLength = utf8.encode(userId).length; - data.addAll(utf8.encode(userId)); - data = getFixedLengthList(data, 20 - userIdLength); - - // openModel 1 - data.add(openMode); - - // keyType 1 - data.add(keyType); - - int? d1, d2; - if (role == LockBleConstant.roleSuperAdmin) { - d1 = 0; - d2 = 0xffffffff; - } else { - d1 = startDate; - d2 = expireDate; - } - - // StartDate 4 - data.add((d1 & 0xff000000) >> 24); - data.add((d1 & 0xff0000) >> 16); - data.add((d1 & 0xff00) >> 8); - data.add(d1 & 0xff); - - // expireDate 4 - data.add((d2 & 0xff000000) >> 24); - data.add((d2 & 0xff0000) >> 16); - data.add((d2 & 0xff00) >> 8); - data.add(d2 & 0xff); - - //useCountLimit 2 - final double useCountLimitDouble = useCountLimit / 256; - final int useCountLimit1 = useCountLimitDouble.toInt(); - final int useCountLimit2 = useCountLimit % 256; - data.add(useCountLimit1); - data.add(useCountLimit2); - - data.add(isRound); - data.add(weekRound); - data.add(startHour); - data.add(startMin); - data.add(endHour); - data.add(endMin); - - // role 1 - data.add(role); - - //password 20 - final int passwordLength = utf8.encode(password).length; - data.addAll(utf8.encode(password)); - data = getFixedLengthList(data, 20 - passwordLength); - - // token 长度4 首次请求 Token 填 0,如果锁需要鉴权 操作者身份,则会分配动态口令并在应答消息中返回,二次请求时带上。 当token失效或者第一次发送的时候token为0 - data.addAll(token); - - final List authCodeData = []; - authCodeData.addAll(utf8.encode(authUserId)); - authCodeData.addAll(utf8.encode(keyId)); - authCodeData.addAll(token); - authCodeData.addAll(publicKey); - - // 把authUserID、KeyID、token、公钥通过md5加密之后就是authCode - var authCode = md5.convert(authCodeData); - - data.add(authCode.bytes.length); - data.addAll(authCode.bytes); - - if ((data.length % 16) != 0) { - final int add = 16 - data.length % 16; - for (int i = 0; i < add; i++) { - data.add(0); - } - } - AppLogger.debug("完整明文数据: $data"); - - // 拿到数据之后通过privateKey进行SM4 ECB加密 - ebcData = SM4.encrypt(data, key: privateKey, mode: SM4CryptoMode.ECB); - return ebcData; - } - - /// ✨✨✨ 检查是否需要重发 ✨✨✨ - @override - bool shouldRetry(dynamic response) { - // 检查响应是否为BleAddAdminResponse类型且状态码为6(需要token) - if (response is BleAddAdminResponse) { - return response.statusCode == 0x06; // 需要token - } - return false; - } - - /// ✨✨✨ 创建重发命令实例 ✨✨✨ - @override - BaseBleCommand createRetryCommand(dynamic response) { - if (response is BleAddAdminResponse) { - // 当状态码为0x06(需要token)时,响应中的token字段是有效的,应该使用这个token - List token = response.token; - AppLogger.debug('🔐 获取到token用于重发: $token'); - - // 创建新的命令实例,携带token - return BleCmdAddAdmin( - encryptType: this.getEncryptType(), - lockId: this.lockId, - authUserId: this.authUserId, - keyId: this.keyId, - userId: this.userId, - openMode: this.openMode, - keyType: this.keyType, - startDate: this.startDate, - expireDate: this.expireDate, - useCountLimit: this.useCountLimit, - isRound: this.isRound, - weekRound: this.weekRound, - startHour: this.startHour, - startMin: this.startMin, - endHour: this.endHour, - endMin: this.endMin, - role: this.role, - password: this.password, - token: token, - // 使用从响应中获取的有效token - publicKey: this.publicKey, - privateKey: this.privateKey, - ); - } - - // 如果无法创建重发命令,抛出异常 - throw Exception('无法为响应创建重发命令: $response'); - } -} diff --git a/lib/ble/command/request/ble_cmd_get_private_key.dart b/lib/ble/command/request/ble_cmd_get_private_key.dart deleted file mode 100644 index 8c72a0b..0000000 --- a/lib/ble/command/request/ble_cmd_get_private_key.dart +++ /dev/null @@ -1,113 +0,0 @@ -import 'dart:convert'; - -import 'package:crypto/crypto.dart'; -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/response/ble_cmd_get_private_key_parser.dart'; -import 'package:starwork_flutter/common/sm4_encipher/sm4.dart'; - -/// ✨✨✨ 获取私钥命令类 - 继承BaseBleCommand ✨✨✨ -class BleCmdGetPrivateKey extends BaseBleCommand { - final String lockId; - final String keyId; - final String authUserID; - final List publicKey; - final int nowTime; - final int _encryptType; // 私有字段存储加密类型 - - /// 指令 ID: 0x3091 - static const int cmdId = 0x3091; - - /// 构造函数 - BleCmdGetPrivateKey({ - int encryptType = BaseBleCommand.ENCRYPT_TYPE_SM4_PRESET, - required this.lockId, - required this.keyId, - required this.authUserID, - required this.nowTime, - required this.publicKey, - }) : _encryptType = encryptType { - if (lockId.isEmpty) { - throw ArgumentError('LockID cannot be empty'); - } - if (lockId.length > 40) { - throw ArgumentError('LockID must be at most 40 characters long, got ${lockId.length}'); - } - AppLogger.debug('🔑 BleCmdGetPrivateKey 创建: LockID="$lockId", 加密类型=$_encryptType'); - } - - /// ✨✨✨ 重写基类方法 - 返回当前命令的加密类型 ✨✨✨ - @override - int getEncryptType() { - return _encryptType; - } - - /// ✨✨✨ 实现BaseBleCommand的抽象方法 - 构建命令数据 ✨✨✨ - @override - List buildData() { - List data = []; - List ebcData = []; - - // 指令类型 - final int type = cmdId; - final double typeDouble = type / 256; - final int type1 = typeDouble.toInt(); - final int type2 = type % 256; - data.add(type1); - data.add(type2); - - // 锁id - final int lockIDLength = utf8.encode(lockId!).length; - data.addAll(utf8.encode(lockId)); - data = getFixedLengthList(data, 40 - lockIDLength); - - //KeyID 40 - final int keyIDLength = utf8.encode(keyId!).length; - data.addAll(utf8.encode(keyId)); - data = getFixedLengthList(data, 40 - keyIDLength); - - //authUserID 20 - final int authUserIDLength = utf8.encode(authUserID!).length; - data.addAll(utf8.encode(authUserID)); - data = getFixedLengthList(data, 20 - authUserIDLength); - - //NowTime 4 - data.add((nowTime & 0xff000000) >> 24); - data.add((nowTime & 0xff0000) >> 16); - data.add((nowTime & 0xff00) >> 8); - data.add(nowTime & 0xff); - - final List authCodeData = []; - - //authUserID - authCodeData.addAll(utf8.encode(authUserID!)); - - //KeyID - authCodeData.addAll(utf8.encode(keyId!)); - - //NowTime 4 - authCodeData.add((nowTime & 0xff000000) >> 24); - authCodeData.add((nowTime & 0xff0000) >> 16); - authCodeData.add((nowTime & 0xff00) >> 8); - authCodeData.add(nowTime & 0xff); - - authCodeData.addAll(publicKey); - - // 把authUserID、KeyID、时间戳、公钥通过md5加密之后就是authCode - var authCode = md5.convert(authCodeData); - - data.add(authCode.bytes.length); - data.addAll(authCode.bytes); - - if ((data.length % 16) != 0) { - final int add = 16 - data.length % 16; - for (int i = 0; i < add; i++) { - data.add(0); - } - } - - // 拿到数据之后通过LockId进行SM4 ECB加密 key:544d485f633335373034383064613864 - ebcData = SM4.encrypt(data, key: utf8.encode(lockId), mode: SM4CryptoMode.ECB); - return ebcData; - } -} diff --git a/lib/ble/command/request/ble_cmd_get_public_key.dart b/lib/ble/command/request/ble_cmd_get_public_key.dart deleted file mode 100644 index 89dd34f..0000000 --- a/lib/ble/command/request/ble_cmd_get_public_key.dart +++ /dev/null @@ -1,64 +0,0 @@ -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/response/ble_cmd_get_public_key_parser.dart'; - -/// ✨✨✨ 获取公钥命令类 - 继承BaseBleCommand ✨✨✨ -class BleCmdGetPublicKey extends BaseBleCommand { - final String lockId; - final int _encryptType; // 私有字段存储加密类型 - - /// 指令 ID: 0x3090 - static const int cmdId = 0x3090; - - /// 构造函数 - /// [lockId] 锁设备ID - /// [encryptType] 加密类型,默认为明文 - BleCmdGetPublicKey({ - int encryptType = BaseBleCommand.ENCRYPT_TYPE_PLAIN, - required this.lockId, - }) : _encryptType = encryptType { - if (lockId.isEmpty) { - throw ArgumentError('LockID cannot be empty'); - } - if (lockId.length > 40) { - throw ArgumentError('LockID must be at most 40 characters long, got ${lockId.length}'); - } - AppLogger.debug('🔑 BleCmdGetPublicKey 创建: LockID="$lockId", 加密类型=$_encryptType'); - } - - /// ✨✨✨ 重写基类方法 - 返回当前命令的加密类型 ✨✨✨ - @override - int getEncryptType() { - return _encryptType; - } - - /// ✨✨✨ 实现BaseBleCommand的抽象方法 - 构建命令数据 ✨✨✨ - @override - List buildData() { - final List buffer = []; - - // 1. 写入 CmdID (2 字节,大端序) - buffer.add((cmdId >> 8) & 0xFF); // 高字节 - buffer.add(cmdId & 0xFF); // 低字节 - - // 2. 处理 LockID:先转换实际字符,再检查是否需要填充到40字节 - List lockIdBytes = List.from(lockId.codeUnits); // 创建可修改的副本 - - // 检查是否需要填充到40字节 - if (lockIdBytes.length < 40) { - // 用0填充到40字节 - int paddingNeeded = 40 - lockIdBytes.length; - lockIdBytes.addAll(List.filled(paddingNeeded, 0)); - } - - buffer.addAll(lockIdBytes); - - if ((buffer.length % 16) != 0) { - final int add = 16 - buffer.length % 16; - for (int i = 0; i < add; i++) { - buffer.add(0); - } - } - return buffer; - } -} diff --git a/lib/ble/command/request/ble_cmd_read_lock_status.dart b/lib/ble/command/request/ble_cmd_read_lock_status.dart deleted file mode 100644 index 75d87f7..0000000 --- a/lib/ble/command/request/ble_cmd_read_lock_status.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'dart:convert'; - -import 'package:crypto/crypto.dart'; -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/response/ble_cmd_get_private_key_parser.dart'; -import 'package:starwork_flutter/ble/command/response/ble_cmd_read_lock_status_parser.dart'; -import 'package:starwork_flutter/common/sm4_encipher/sm4.dart'; - -/// ✨✨✨ 读取锁状态命令类 - 继承BaseBleCommand ✨✨✨ -class BleCmdReadLockStatus extends BaseBleCommand { - final String lockId; - final String userId; - final int timeStamp; - final int localUnix; - final List privateKey; - final int _encryptType; // 私有字段存储加密类型 - - /// 指令 ID: 0x3040 - static const int cmdId = 0x3040; - - /// 构造函数 - BleCmdReadLockStatus({ - int encryptType = BaseBleCommand.ENCRYPT_TYPE_SM4_DEVICE, - required this.lockId, - required this.userId, - required this.timeStamp, - required this.localUnix, - required this.privateKey, - }) : _encryptType = encryptType { - AppLogger.debug('🔑 BleCmdReadLockStatus 创建: LockID="$lockId", 加密类型=$_encryptType'); - } - - /// ✨✨✨ 重写基类方法 - 返回当前命令的加密类型 ✨✨✨ - @override - int getEncryptType() { - return _encryptType; - } - - /// ✨✨✨ 实现BaseBleCommand的抽象方法 - 构建命令数据 ✨✨✨ - @override - List buildData() { - List data = []; - List ebcData = []; - - // 指令类型 - final int type = cmdId; - final double typeDouble = type / 256; - final int type1 = typeDouble.toInt(); - final int type2 = type % 256; - data.add(type1); - data.add(type2); - - // 锁id - final int lockIDLength = utf8.encode(lockId).length; - data.addAll(utf8.encode(lockId)); - data = getFixedLengthList(data, 40 - lockIDLength); - - //userId 40 - final int userIdLength = utf8.encode(userId).length; - data.addAll(utf8.encode(userId)); - data = getFixedLengthList(data, 40 - userIdLength); - - //timeStamp 4 - data.add((timeStamp & 0xff000000) >> 24); - data.add((timeStamp & 0xff0000) >> 16); - data.add((timeStamp & 0xff00) >> 8); - data.add(timeStamp & 0xff); - - //localUnix 4 - data.add((localUnix & 0xff000000) >> 24); - data.add((localUnix & 0xff0000) >> 16); - data.add((localUnix & 0xff00) >> 8); - data.add(localUnix & 0xff); - - if ((data.length % 16) != 0) { - final int add = 16 - data.length % 16; - for (int i = 0; i < add; i++) { - data.add(0); - } - } - - // 拿到数据之后通过私钥进行SM4 ECB加密 - ebcData = SM4.encrypt(data, key:privateKey, mode: SM4CryptoMode.ECB); - return ebcData; - } -} diff --git a/lib/ble/command/response/ble_cmd_add_admin_parser.dart b/lib/ble/command/response/ble_cmd_add_admin_parser.dart deleted file mode 100644 index ef4b21d..0000000 --- a/lib/ble/command/response/ble_cmd_add_admin_parser.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'dart:convert'; - -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_add_admin.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/model/ble_add_admin_response.dart'; - -class BleCmdAddAdminParser extends BaseBleResponseParser { - @override - int get commandId => BleCmdAddAdmin.cmdId; - - @override - String get commandName => '增加超级管理员命令'; - - /// ✨✨✨ 解析获取增加超级管理员的应答数据 ✨✨✨ - @override - BleAddAdminResponse parseResponse(ParsedPacket parsedPacket, List rawResponseData) { - try { - if (rawResponseData.length < 3) { - throw ArgumentError('应答数据长度不足: ${rawResponseData.length}字节 < 3字节'); - } - - int offset = 0; - - // 1. 提取命令ID (已在isMatchingResponse中验证过,这里记录日志) - int responseCommandId = BaseBleResponseParser.extractInt(rawResponseData, offset, 2); - offset += 2; - - // 2. lockId - List lockId = BaseBleResponseParser.extractBytes(rawResponseData, offset, 40); - String lockIdStr = _safeStringDecode(lockId).trim(); - offset += 40; - - // 3. token - List token = BaseBleResponseParser.extractBytes(rawResponseData, offset, 4); - offset += 4; - - // 4. 提取状态码 - int statusCode = rawResponseData[offset++]; - - // 当状态码为0x06(需要token)时,token字段是有效的,应该返回这个token - if (statusCode == 0x06) { - return BleAddAdminResponse( - commandId: commandId, - statusCode: statusCode, - token: token, // 状态码为0x06时返回有效的token - lockId: lockIdStr, - serialNo: '', - no: -1, - ); - } - - // 当状态码为0时,表示成功,此时可能也有token(根据协议而定) - if (statusCode == 0) { - return BleAddAdminResponse( - commandId: commandId, - statusCode: statusCode, - token: token, // 状态码为0时也返回token - lockId: lockIdStr, - serialNo: '', - no: -1, - ); - } - - // 其他状态码情况 - return BleAddAdminResponse( - commandId: commandId, - statusCode: statusCode, - token: [], // 其他状态码时返回空token - lockId: lockIdStr, - serialNo: '', - no: -1, - ); - } catch (e) { - AppLogger.error('❌ 解析添加管理员应答数据异常', error: e); - rethrow; - } - } - - /// ✨✨✨ 安全的字符串解码方法 ✨✨✨ - /// 处理可能包含非UTF-8字节的数据 - String _safeStringDecode(List data) { - // 1. 找到第一个 0x00(空字节)的位置,只取前面的有效部分 - int endIndex = 0; - for (int i = 0; i < data.length; i++) { - if (data[i] == 0) { - break; - } - endIndex = i + 1; - } - - // 2. 截取有效字节 - List validBytes = data.sublist(0, endIndex); - - try { - // 3. 使用 UTF-8 解码(允许畸形,但会替换为 ) - return utf8.decode(validBytes, allowMalformed: true).trim(); - } catch (e) { - // 4. 极端情况:用字符码直接构造(基本不会走到这里) - return String.fromCharCodes(validBytes).trim(); - } - } - - /// ✨✨✨ 静态便捷方法 - 直接解析原始数据包 ✨✨✨ - static BleAddAdminResponse? parseRawPacket(List rawPacketData) { - BleCmdAddAdminParser parser = BleCmdAddAdminParser(); - return parser.handleResponse(rawPacketData) as BleAddAdminResponse?; - } -} diff --git a/lib/ble/command/response/ble_cmd_get_private_key_parser.dart b/lib/ble/command/response/ble_cmd_get_private_key_parser.dart deleted file mode 100644 index 271f921..0000000 --- a/lib/ble/command/response/ble_cmd_get_private_key_parser.dart +++ /dev/null @@ -1,145 +0,0 @@ -import 'dart:typed_data'; - -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_private_key.dart'; -import 'package:starwork_flutter/ble/command/request/ble_cmd_get_public_key.dart'; - -/// ✨✨✨ 获取私钥命令应答数据结构 ✨✨✨ -class GetPrivateKeyResponse { - final int commandId; - final int statusCode; - final List commKey; - final String commKeyHex; - final List signKey; - final String signKeyHex; - final int randPassTick; - - GetPrivateKeyResponse({ - required this.commandId, - required this.statusCode, - required this.commKey, - required this.commKeyHex, - required this.signKey, - required this.signKeyHex, - required this.randPassTick, - }); - - /// 是否成功 - bool get isSuccess => statusCode == 0x00; - - /// 状态描述 - String get statusDescription { - switch (statusCode) { - case 0x00: - return '成功'; - case 0x01: - return '失败'; - case 0x02: - return '参数错误'; - case 0x03: - return '设备忙'; - case 0x06: - return '需要token'; - default: - return '未知状态(0x${statusCode.toRadixString(16).padLeft(2, '0')})'; - } - } - - @override - String toString() { - return 'GetPrivateKeyResponse{commandId: $commandId, statusCode: $statusCode, commKey: $commKey, commKeyHex: $commKeyHex, signKey: $signKey, signKeyHex: $signKeyHex, randPassTick: $randPassTick}'; - } -} - -/// ✨✨✨ 获取私钥命令应答解析器 ✨✨✨ -class BleCmdGetPrivateKeyParser extends BaseBleResponseParser { - @override - int get commandId => BleCmdGetPrivateKey.cmdId; - - @override - String get commandName => '获取私钥命令'; - - /// ✨✨✨ 解析获取私钥命令的应答数据 ✨✨✨ - @override - GetPrivateKeyResponse parseResponse(ParsedPacket parsedPacket, List rawResponseData) { - try { - if (rawResponseData.length < 3) { - throw ArgumentError('应答数据长度不足: ${rawResponseData.length}字节 < 3字节'); - } - - int offset = 0; - - // 1. 提取命令ID (已在isMatchingResponse中验证过,这里记录日志) - int responseCommandId = BaseBleResponseParser.extractInt(rawResponseData, offset, 2); - offset += 2; - - // 2. 提取状态码 - int statusCode = rawResponseData[offset++]; - - if (statusCode == 0) { - List commKey = []; - if (offset < rawResponseData.length) { - commKey = BaseBleResponseParser.extractBytes(rawResponseData, offset, 16); - } - offset += 16; - String commKeyHex = BaseBleResponseParser.bytesToHex(commKey, separator: ''); - - List signKey = []; - if (offset < rawResponseData.length) { - signKey = BaseBleResponseParser.extractBytes(rawResponseData, offset, 16); - } - offset += 16; - String signKeyHex = BaseBleResponseParser.bytesToHex(signKey, separator: ''); - - List randPassTick = []; - if (offset < rawResponseData.length) { - randPassTick = BaseBleResponseParser.extractBytes(rawResponseData, offset, rawResponseData.length - offset); - } - - final buffer = Uint8List.fromList(randPassTick).buffer; - final data = ByteData.view(buffer); - - // 4. 创建应答对象 - GetPrivateKeyResponse response = GetPrivateKeyResponse( - commandId: responseCommandId, - statusCode: statusCode, - commKey: commKey, - commKeyHex: commKeyHex, - signKey: signKey, - signKeyHex: signKeyHex, - randPassTick: data.getInt64(0, Endian.little), - ); - - if (response.isSuccess) { - AppLogger.highlight('✨✨✨ ✅ 获取私钥成功,私钥长度: ${commKey.length}字节 ✨✨✨'); - } else { - AppLogger.warn('⚠️ 获取私钥失败: ${response.statusDescription}'); - } - - return response; - } else { - AppLogger.warn('⚠️ 获取私钥失败: ${statusCode}'); - return GetPrivateKeyResponse( - commandId: responseCommandId, - statusCode: statusCode, - commKey: [], - commKeyHex: '', - signKey: [], - signKeyHex: '', - randPassTick: -1, - ); - } - } catch (e) { - AppLogger.error('❌ 解析获取私钥应答数据异常', error: e); - rethrow; - } - } - - /// ✨✨✨ 静态便捷方法 - 直接解析原始数据包 ✨✨✨ - static GetPrivateKeyResponse? parseRawPacket(List rawPacketData) { - BleCmdGetPrivateKeyParser parser = BleCmdGetPrivateKeyParser(); - return parser.handleResponse(rawPacketData) as GetPrivateKeyResponse?; - } -} diff --git a/lib/ble/command/response/ble_cmd_get_public_key_parser.dart b/lib/ble/command/response/ble_cmd_get_public_key_parser.dart deleted file mode 100644 index 3aadb10..0000000 --- a/lib/ble/command/response/ble_cmd_get_public_key_parser.dart +++ /dev/null @@ -1,131 +0,0 @@ -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'; - -/// ✨✨✨ 获取公钥命令应答数据结构 ✨✨✨ -class GetPublicKeyResponse { - final int commandId; - final int statusCode; - final List publicKey; - final String publicKeyHex; - - GetPublicKeyResponse({ - required this.commandId, - required this.statusCode, - required this.publicKey, - required this.publicKeyHex, - }); - - /// 是否成功 - bool get isSuccess => statusCode == 0x00; - - /// 状态描述 - String get statusDescription { - switch (statusCode) { - case 0x00: - return '成功'; - case 0x01: - return '失败'; - case 0x02: - return '参数错误'; - case 0x03: - return '设备忙'; - case 0x06: - return '需要token'; - default: - return '未知状态(0x${statusCode.toRadixString(16).padLeft(2, '0')})'; - } - } - - @override - String toString() { - return 'GetPublicKeyResponse(commandId: 0x${commandId.toRadixString(16).padLeft(4, '0')}, ' + - 'status: $statusDescription, publicKeyLength: ${publicKey.length}, ' + - 'publicKey: ${publicKeyHex.substring(0, publicKeyHex.length > 32 ? 32 : publicKeyHex.length)}...)'; - } -} - -/// ✨✨✨ 获取公钥命令应答解析器 ✨✨✨ -class BleCmdGetPublicKeyParser extends BaseBleResponseParser { - @override - int get commandId => BleCmdGetPublicKey.cmdId; // 0x3090 - - @override - String get commandName => '获取公钥命令'; - - /// ✨✨✨ 解析获取公钥命令的应答数据 ✨✨✨ - @override - GetPublicKeyResponse parseResponse(ParsedPacket parsedPacket, List rawResponseData) { - try { - // 数据包格式分析: - // 字节 0-1: 命令ID (0x3090, 大端序) - // 字节 2: 状态码 (0x00=成功, 其他=失败) - // 字节 3-N: 公钥数据 (长度可变) - - if (rawResponseData.length < 3) { - throw ArgumentError('应答数据长度不足: ${rawResponseData.length}字节 < 3字节'); - } - - int offset = 0; - - // 1. 提取命令ID (已在isMatchingResponse中验证过,这里记录日志) - int responseCommandId = BaseBleResponseParser.extractInt(rawResponseData, offset, 2); - offset += 2; - - // 2. 提取状态码 - int statusCode = rawResponseData[offset++]; - - if (statusCode == 0) { - List publicKey = []; - if (offset < rawResponseData.length) { - publicKey = BaseBleResponseParser.extractBytes(rawResponseData, offset, rawResponseData.length - offset); - } - - String publicKeyHex = BaseBleResponseParser.bytesToHex(publicKey, separator: ''); - - // 4. 创建应答对象 - GetPublicKeyResponse response = GetPublicKeyResponse( - commandId: responseCommandId, - statusCode: statusCode, - publicKey: publicKey, - publicKeyHex: publicKeyHex, - ); - - return response; - } else { - return GetPublicKeyResponse( - commandId: responseCommandId, - statusCode: statusCode, - publicKey: [], - publicKeyHex: '', - ); - } - } catch (e) { - AppLogger.error('❌ 解析获取公钥应答数据异常', error: e); - rethrow; - } - } - - /// 获取状态码描述 - String _getStatusDescription(int statusCode) { - switch (statusCode) { - case 0x00: - return '成功'; - case 0x01: - return '失败'; - case 0x02: - return '参数错误'; - case 0x03: - return '设备忙'; - default: - return '未知状态'; - } - } - - /// ✨✨✨ 静态便捷方法 - 直接解析原始数据包 ✨✨✨ - static GetPublicKeyResponse? parseRawPacket(List rawPacketData) { - BleCmdGetPublicKeyParser parser = BleCmdGetPublicKeyParser(); - return parser.handleResponse(rawPacketData) as GetPublicKeyResponse?; - } -} 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 deleted file mode 100644 index a607e06..0000000 --- a/lib/ble/command/response/ble_cmd_read_lock_status_parser.dart +++ /dev/null @@ -1,299 +0,0 @@ -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 { - final int commandId; - final int statusCode; - - final String vendor; // 厂商名称,例如 WDJT - final int product; // 锁设备类型:1=普通门锁,2=视频门锁,3=人脸识别门锁,4=挂锁 - final String model; // 产品型号,如 singlegrip2 - final String fwVersion; // 软件版本,如 1.0.0.230828 - final String hwVersion; // 硬件版本,如 1.0.0.230828 - final String serialNum0; // 厂商序列号(唯一,重置不变) - final String? serialNum1; // 成品商序列号(可选) - final String btDeviceName; // 蓝牙名称,如 TMH_c3570480da8d - final int electricQuantity; // 电池剩余电量 (%) - final int electricQuantityStandby; // 备用电池电量 - final int restoreCounter; // 重置次数 - final int restoreDate; // 重置时间(UNIX 时间戳,单位:秒) - final String icPartNo; // 主控芯片型号 - final int indate; // 有效时间(UNIX 时间戳) - final String mac; // 蓝牙 MAC 地址 - final int featureValueLength; // 特征值字符串长度 - final List featureValue; // 特征值 - final String featureValueHexStr; // 特征值16进制字符串 - final int featureEnValLength; // 启用功能长度 - final List featureEnVal; // 启用功能 - final String featureEnValHexStr; // 启用功能16进制字符串 - final int featureParaTotal; // 支持的带参数特征值的总条目数 - final List featureParaData; // 对应特征值的参数,从FeatureParaTotal开始,按照协议文档循环 - - ReadLockStatusResponse({ - required this.commandId, - required this.statusCode, - required this.vendor, - required this.product, - required this.model, - required this.fwVersion, - required this.hwVersion, - required this.serialNum0, - required this.serialNum1, - required this.btDeviceName, - required this.electricQuantity, - required this.electricQuantityStandby, - required this.restoreCounter, - required this.restoreDate, - required this.icPartNo, - required this.indate, - required this.mac, - required this.featureValueLength, - required this.featureValue, - required this.featureValueHexStr, - required this.featureEnValLength, - required this.featureEnVal, - required this.featureEnValHexStr, - required this.featureParaTotal, - required this.featureParaData, - }); - - /// 是否成功 - bool get isSuccess => statusCode == 0x00; - - /// 状态描述 - String get statusDescription { - switch (statusCode) { - case 0x00: - return '成功'; - case 0x01: - return '失败'; - case 0x02: - return '参数错误'; - case 0x03: - return '设备忙'; - case 0x06: - return '需要token'; - default: - return '未知状态(0x${statusCode.toRadixString(16).padLeft(2, '0')})'; - } - } - - @override - String toString() { - return 'ReadLockStatusResponse(' - 'commandId: 0x${commandId.toRadixString(16).padLeft(4, '0')}, ' - 'status: $statusDescription, ' - 'vendor: $vendor, ' - 'product: $product, ' - 'model: $model, ' - 'fwVersion: $fwVersion, ' - 'hwVersion: $hwVersion, ' - 'serialNum0: $serialNum0, ' - 'serialNum1: $serialNum1, ' - 'btDeviceName: $btDeviceName, ' - 'electricQuantity: $electricQuantity%, ' - 'electricQuantityStandby: $electricQuantityStandby%, ' - 'restoreCounter: $restoreCounter, ' - 'restoreDate: $restoreDate (${DateTime.fromMillisecondsSinceEpoch(restoreDate * 1000, isUtc: true)}), ' - 'icPartNo: $icPartNo, ' - 'indate: $indate (${DateTime.fromMillisecondsSinceEpoch(indate * 1000, isUtc: true)}), ' - 'mac: $mac, ' - 'featureValueLength: $featureValueLength,' - 'featureValueHexStr: $featureValueHexStr,' - 'featureEnValLength: $featureEnValLength,' - 'featureEnValHexStr: $featureEnValHexStr,' - 'featureParaTotal: $featureParaTotal,' - 'featureParaData: $featureParaData' - ')'; - } -} - -/// ✨✨✨ 读取锁状态命令应答解析器 ✨✨✨ -class BleCmdReadLockStatusParser extends BaseBleResponseParser { - @override - int get commandId => BleCmdReadLockStatus.cmdId; - - @override - String get commandName => '读取锁状态命令'; - - /// ✨✨✨ 解析获取读取锁状态的应答数据 ✨✨✨ - @override - ReadLockStatusResponse parseResponse(ParsedPacket parsedPacket, List rawResponseData) { - try { - if (rawResponseData.length < 3) { - throw ArgumentError('应答数据长度不足: ${rawResponseData.length}字节 < 3字节'); - } - - int offset = 0; - - // 1. 提取命令ID (已在isMatchingResponse中验证过,这里记录日志) - int responseCommandId = BaseBleResponseParser.extractInt(rawResponseData, offset, 2); - offset += 2; - - // 2. 提取状态码 - int statusCode = rawResponseData[offset++]; - - if (statusCode != 0) { - return ReadLockStatusResponse( - commandId: commandId, - statusCode: statusCode, - vendor: '', - product: 0, - model: '', - fwVersion: '', - hwVersion: '', - serialNum0: '', - serialNum1: '', - btDeviceName: '', - electricQuantity: 0, - electricQuantityStandby: 0, - restoreCounter: 0, - restoreDate: 0, - icPartNo: '', - indate: 0, - mac: '', - featureValueLength: 0, - featureValue: [], - featureEnValLength: 0, - featureValueHexStr: '', - featureEnVal: [], - featureEnValHexStr: '', - featureParaTotal: -1, - featureParaData: [], - ); - } - - // 3. 厂商名称 (20 bytes, UTF-8) - List vendorBytes = BaseBleResponseParser.extractBytes(rawResponseData, offset, 20); - String vendor = utf8String(vendorBytes).trim(); - offset += 20; - - // 4. 锁设备类型 (1 byte) - int product = rawResponseData[offset++]; - - // 5. 产品型号 (20 bytes, UTF-8) - List modelBytes = BaseBleResponseParser.extractBytes(rawResponseData, offset, 20); - String model = utf8String(modelBytes).trim(); - offset += 20; - - // 6. 软件版本 (20 bytes, UTF-8) - List fwVersionBytes = BaseBleResponseParser.extractBytes(rawResponseData, offset, 20); - String fwVersion = utf8String(fwVersionBytes).trim(); - offset += 20; - - // 7. 硬件版本 (20 bytes, UTF-8) - List hwVersionBytes = BaseBleResponseParser.extractBytes(rawResponseData, offset, 20); - String hwVersion = utf8String(hwVersionBytes).trim(); - offset += 20; - - // 8. 厂商序列号 (16 bytes, UTF-8) - List serialNum0Bytes = BaseBleResponseParser.extractBytes(rawResponseData, offset, 16); - String serialNum0 = utf8String(serialNum0Bytes).trim(); - offset += 16; - - // 9. 成品商序列号 (16 bytes, UTF-8, 可为空) - List serialNum1Bytes = BaseBleResponseParser.extractBytes(rawResponseData, offset, 16); - String serialNum1 = utf8String(serialNum1Bytes).trim(); - offset += 16; - - // 10. 蓝牙名称 (16 bytes, UTF-8) - List btDeviceNameBytes = BaseBleResponseParser.extractBytes(rawResponseData, offset, 16); - String btDeviceName = utf8String(btDeviceNameBytes).trim(); - offset += 16; - - // 11. 电池剩余电量 (1 byte) - int electricQuantity = rawResponseData[offset++]; - - // 12. 备用电池剩余电量 (1 byte) - int electricQuantityStandby = rawResponseData[offset++]; - - // 13. 重置次数 (2 bytes, big-endian) - int restoreCounter = BaseBleResponseParser.extractInt(rawResponseData, offset, 2); - offset += 2; - - // 14. 重置时间 (4 bytes, big-endian, 单位:秒) - int restoreDateSeconds = BaseBleResponseParser.extractInt(rawResponseData, offset, 4); - offset += 4; - - // 15. 主控芯片型号 (10 bytes, UTF-8) - List icPartNoBytes = BaseBleResponseParser.extractBytes(rawResponseData, offset, 10); - String icPartNo = utf8String(icPartNoBytes).trim(); - offset += 10; - - // 16. 有效时间 (4 bytes, big-endian, 单位:秒) - int indateSeconds = BaseBleResponseParser.extractInt(rawResponseData, offset, 4); - offset += 4; - - // 17. MAC 地址 (20 bytes, UTF-8) - List macBytes = BaseBleResponseParser.extractBytes(rawResponseData, offset, 20); - String mac = utf8String(macBytes).trim(); - offset += 20; - - // 18. 特征值长度 (1 byte) - int featureValueLength = rawResponseData[offset++]; - - // 19. 锁特征值说明(本机能支持的功能) - List featureValue = BaseBleResponseParser.extractBytes(rawResponseData, offset, featureValueLength); - String featureValueStr = asciiString(featureValue); - offset += featureValueLength; - - // 20. 启用功能长度 - int featureEnValLength = rawResponseData[offset++]; - - // 21. 启用功能说明(本机启用的功能) - List featureEnVal = BaseBleResponseParser.extractBytes(rawResponseData, offset, featureEnValLength); - String featureEnValStr = asciiString(featureValue); - offset += featureEnValLength; - - // 22. 支持的带参数特征值的总条目数 - int featureParaTotal = rawResponseData[offset++]; - - // 23. 支持特征值的参数设置 - List featureParaData = rawResponseData.sublist(offset); - - // ✅ 构造完整对象 - return ReadLockStatusResponse( - commandId: commandId, - statusCode: statusCode, - vendor: vendor, - product: product, - model: model, - fwVersion: fwVersion, - hwVersion: hwVersion, - serialNum0: serialNum0, - serialNum1: serialNum1, - btDeviceName: btDeviceName, - electricQuantity: electricQuantity, - electricQuantityStandby: electricQuantityStandby, - restoreCounter: restoreCounter, - restoreDate: restoreDateSeconds, - // 注意:UNIX 秒时间戳 - icPartNo: icPartNo, - indate: indateSeconds, - // 注意:UNIX 秒时间戳 - mac: mac, - featureValueLength: featureValueLength, - featureValue: featureValue, - featureValueHexStr: featureValueStr, - featureEnValLength: featureEnValLength, - featureEnVal: featureEnVal, - featureEnValHexStr: featureEnValStr, - featureParaTotal: featureParaTotal, - featureParaData: featureParaData, - ); - } catch (e) { - AppLogger.error('❌ 解析读取锁状态答数据异常', error: e); - rethrow; - } - } - - /// ✨✨✨ 静态便捷方法 - 直接解析原始数据包 ✨✨✨ - static ReadLockStatusResponse? parseRawPacket(List rawPacketData) { - BleCmdReadLockStatusParser parser = BleCmdReadLockStatusParser(); - return parser.handleResponse(rawPacketData) as ReadLockStatusResponse?; - } -} diff --git a/lib/ble/command/response/model/ble_add_admin_response.dart b/lib/ble/command/response/model/ble_add_admin_response.dart deleted file mode 100644 index d44142c..0000000 --- a/lib/ble/command/response/model/ble_add_admin_response.dart +++ /dev/null @@ -1,43 +0,0 @@ -class BleAddAdminResponse { - final int commandId; - final int statusCode; - final int no; - final String serialNo; - final String lockId; - final List token; - - BleAddAdminResponse({ - required this.commandId, - required this.statusCode, - required this.token, - required this.lockId, - required this.serialNo, - required this.no, - }); - - /// 是否成功 - bool get isSuccess => statusCode == 0x00; - - /// 状态描述 - String get statusDescription { - switch (statusCode) { - case 0x00: - return '成功'; - case 0x01: - return '失败'; - case 0x02: - return '参数错误'; - case 0x03: - return '设备忙'; - case 0x06: - return '需要token'; - default: - return '未知状态(0x${statusCode.toRadixString(16).padLeft(2, '0')})'; - } - } - - @override - String toString() { - return 'BleAddAdminResponse{commandId: $commandId, statusCode: $statusCode, no: $no, serialNo: $serialNo, lockId: $lockId, token: $token}'; - } -} diff --git a/lib/ble/constant/lock_ble_constant.dart b/lib/ble/constant/lock_ble_constant.dart deleted file mode 100644 index 1d198bf..0000000 --- a/lib/ble/constant/lock_ble_constant.dart +++ /dev/null @@ -1,19 +0,0 @@ -class LockBleConstant { - /// KeyType: 钥匙类型 - /// 0: 普通钥匙 - /// 1: 临时钥匙(到期自动删除) - /// 2: 蓝牙遥控器 - /// 255: 测试钥匙(添加正式钥匙后自动失效) - static const int keyTypeNormal = 0; - static const int keyTypeTemporary = 1; - static const int keyTypeBluetoothRemote = 2; - static const int keyTypeTest = 255; - - /// Role: 用户角色 - /// 0: 普通用户 - /// 1: 管理员 - /// 255 (0xFF): 超级管理员 - static const int roleNormalUser = 0; - static const int roleAdmin = 1; - static const int roleSuperAdmin = 255; // 0xFF -} diff --git a/lib/ble/exceptional/ble_connected_exception.dart b/lib/ble/exceptional/ble_connected_exception.dart deleted file mode 100644 index 9030b99..0000000 --- a/lib/ble/exceptional/ble_connected_exception.dart +++ /dev/null @@ -1,3 +0,0 @@ -class BleConnectedException implements Exception{ - -} \ No newline at end of file diff --git a/lib/ble/model/scan_device_info.dart b/lib/ble/model/scan_device_info.dart deleted file mode 100644 index 0bfa1cb..0000000 --- a/lib/ble/model/scan_device_info.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter_blue_plus/flutter_blue_plus.dart'; - -class ScanDeviceInfo { - //设备名字 - final String advName; - - // 是否绑定 - final bool isBinding; - // 是否有新事件 - final bool hasNewEvent; - - // 原始设备信息 - final ScanResult rawDeviceInfo; - - ScanDeviceInfo({ - required this.advName, - this.isBinding = false, // 默认值为 false - this.hasNewEvent = false, // 默认值为 false - required this.rawDeviceInfo, - }); - - @override - String toString() { - return 'ScanDeviceInfo{advName: $advName, isBinding: $isBinding, hasNewEvent: $hasNewEvent,rawDeviceInfo:$rawDeviceInfo}'; - } -} diff --git a/lib/views/device/searchDevice/search_device_controller.dart b/lib/views/device/searchDevice/search_device_controller.dart index 1695ca4..a520aa7 100644 --- a/lib/views/device/searchDevice/search_device_controller.dart +++ b/lib/views/device/searchDevice/search_device_controller.dart @@ -9,17 +9,6 @@ import 'package:starcloud/sdk/starcloud.dart'; import 'package:starwork_flutter/base/app_logger.dart'; import 'package:starwork_flutter/base/app_permission.dart'; import 'package:starwork_flutter/base/base_controller.dart'; -import 'package:starwork_flutter/ble/ble_service.dart'; -import 'package:starwork_flutter/ble/command/base/base_ble_response_parser.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/command/response/ble_cmd_read_lock_status_parser.dart'; -import 'package:starwork_flutter/ble/command/response/model/ble_add_admin_response.dart'; -import 'package:starwork_flutter/ble/model/scan_device_info.dart'; import 'package:starwork_flutter/common/constant/app_toast_messages.dart'; import 'package:starwork_flutter/common/constant/cache_keys.dart'; import 'package:starwork_flutter/common/utils/shared_preferences_utils.dart'; diff --git a/lib/views/device/searchDevice/search_device_view.dart b/lib/views/device/searchDevice/search_device_view.dart index cef8039..9536c67 100644 --- a/lib/views/device/searchDevice/search_device_view.dart +++ b/lib/views/device/searchDevice/search_device_view.dart @@ -4,7 +4,6 @@ import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:starcloud/entity/star_cloud_scan_result.dart'; -import 'package:starwork_flutter/ble/model/scan_device_info.dart'; import 'package:starwork_flutter/views/device/searchDevice/search_device_controller.dart'; class SearchDeviceView extends GetView { @@ -173,7 +172,6 @@ class SearchDeviceView extends GetView { } _buildItem({required StarCloudScanResult device, required int index}) { - return GestureDetector( onTap: () async { controller.connectingDevices(device); diff --git a/lib/views/main/main_controller.dart b/lib/views/main/main_controller.dart index c82aa16..30cb897 100644 --- a/lib/views/main/main_controller.dart +++ b/lib/views/main/main_controller.dart @@ -4,8 +4,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/request/change_current_team_request.dart'; import 'package:starwork_flutter/api/model/team/response/team_info_response.dart'; import 'package:starwork_flutter/api/service/team_api_service.dart'; +import 'package:starwork_flutter/base/app_logger.dart'; import 'package:starwork_flutter/base/base_controller.dart'; import 'package:starwork_flutter/common/constant/app_images.dart'; import 'package:starwork_flutter/routes/app_routes.dart'; @@ -115,4 +117,21 @@ class MainController extends BaseController { selectedTeam.value = myTeamList.first; } } + + /// 切换当前团队 + void requestChangeCurrentTeam(TeamInfoResponse teamInfo) async { + var teamNo = teamInfo.teamNo; + if (teamNo == null) { + return; + } + var changeCurrentTeamResponse = await teamApi.requestChangeCurrentTeam( + request: ChangeCurrentTeamRequest( + teamNo: teamNo, + ), + ); + if (changeCurrentTeamResponse.isSuccess) { + AppLogger.highlight('切换当前团队成功'); + selectedTeam.value = teamInfo; + } + } } diff --git a/lib/views/main/main_view.dart b/lib/views/main/main_view.dart index 338f395..bf633ef 100644 --- a/lib/views/main/main_view.dart +++ b/lib/views/main/main_view.dart @@ -38,8 +38,8 @@ class MainView extends GetView { teamList: controller.myTeamList, selectedTeam: controller.selectedTeam.value, onTeamSelected: (team) { - controller.selectedTeam.value = team; - AppLogger.highlight('message:${team}'); + controller.requestChangeCurrentTeam(team); + }, onRefreshList: () { controller.requestAllTeamInfoList(); diff --git a/lib/views/team/useCaseSetting/use_case_setting_controller.dart b/lib/views/team/useCaseSetting/use_case_setting_controller.dart index fc68ebc..4908798 100644 --- a/lib/views/team/useCaseSetting/use_case_setting_controller.dart +++ b/lib/views/team/useCaseSetting/use_case_setting_controller.dart @@ -1,5 +1,7 @@ import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; +import 'package:starcloud/sdk/entity/cloud_user_info.dart'; +import 'package:starcloud/sdk/starcloud.dart'; import 'package:starwork_flutter/api/model/team/request/create_team_request.dart'; import 'package:starwork_flutter/api/model/team/response/scene_info_response.dart'; import 'package:starwork_flutter/api/service/team_api_service.dart'; @@ -28,19 +30,41 @@ class UseCaseSettingController extends BaseController { /// 请求团队使用场景信息 void requestAllSceneInfoList() async { - showLoading(); - var sceneList = await teamApi.requestAllSceneInfoList(); if (sceneList.isSuccess) { // 使用场景列表 useCases.value = sceneList.data?.list ?? []; useCases.refresh(); } - hideLoading(); } void requestCreateTeam() async { - showLoading(); + var cacheStarCloudUserName = await SharedPreferencesUtils.getString(CacheKeys.starCloudUserName); + var cacheStarCloudPassword = await SharedPreferencesUtils.getString(CacheKeys.starCloudPassword); + var cacheStarCloudUid = await SharedPreferencesUtils.getString(CacheKeys.starCloudUid); + if (cacheStarCloudUserName == null || cacheStarCloudPassword == null || cacheStarCloudUid == null) { + await StarCloudSDK.instance.createCloudUser( + onError: (err) { + AppLogger.error('err:${err}'); + hideLoading(); + }, + onSuccess: (userInfo) { + SharedPreferencesUtils.setString(CacheKeys.starCloudUserName, userInfo.username); + SharedPreferencesUtils.setString(CacheKeys.starCloudPassword, userInfo.password); + SharedPreferencesUtils.setString(CacheKeys.starCloudUid, userInfo.uid.toString()); + }, + ); + } + StarCloudSDK.instance.setCloudAccounts( + [ + CloudUserInfo( + username: cacheStarCloudUserName!, + password: cacheStarCloudPassword!, + uid: int.parse(cacheStarCloudUid!), + ) + ], + ); + var createTeamResponse = await teamApi.requestCreateTeam( request: CreateTeamRequest( teamName: teamNameInputController.text,