sort方法原理、深浅拷贝、async,await异常捕获

本文介绍了一种基于JavaScript的自定义数组排序方法,并通过示例展示了升序和降序的实现过程。此外,还探讨了浅拷贝与深拷贝的区别及实现方式,包括JSON字符串化与解析的方法及一种更全面的深拷贝方案。

sort方法原理

Array.prototype.sortTest = function (arguments) {
            let fn = arguments ? typeof arguments === "function" && arguments : (x, y) => x - y;
            for (var i = 0; i < this.length; i++) {
                for (var j = i + 1; j < this.length; j++) {
                    if (fn(this[i], this[j]) > 0) {
                        p = this[i];
                        this[i] = this[j];
                        this[j] = p;
                    };
                };
            };
            return this;
        }

        var add = (x, y) => x - y;
        var des = (x, y) => y - x;
        var arr = [10, 44, 5, 78, 1, 99];
        var arr1 = [10, 44, 5, 78, 1, 99];
        var arr2 = [10, 44, 5, 78, 1, 99];
        var arr3 = [10, 44, 5, 78, 1, 99];
        // console.log(arr.sortTest(add));//[1, 5, 10, 44, 78, 99]
        // console.log(arr1.sort(add));//[1, 5, 10, 44, 78, 99]
        // console.log(arr2.sortTest(des));//[99, 78, 44, 10, 5, 1]
        // console.log(arr3.sort(des));//[99, 78, 44, 10, 5, 1]

深浅拷贝

// 浅拷贝一
        function shallowCloneCopy(target) {
            if (typeof target === 'object' && target !== null) {
                const cloneTarget = Array.isArray(target) ? [] : {}
                for (let prop in target) {
                    if (target.hasOwnProperty(prop)) {
                        // cloneTarget[prop] = shallowClone(target[prop]) 深拷贝
                        cloneTarget[prop] = target[prop]

                    }
                }
                return cloneTarget
            } else {
                return target
            }
        }

        // 深拷贝二
        function shallowClone(target) {
            if (typeof target === 'object' && target !== null) {

                return JSON.parse(JSON.stringify(target))
            } else {
                return target
            }
        }

        const shallowCloneObj = shallowClone(obj1)

        console.log(shallowCloneObj === obj1)  // false,返回的是一个新对象
        console.log(shallowCloneObj.arry === obj1.arry)

        // 深拷贝第三种
        function clone(obj, hash = new WeakMap()) {
            // 判断是否为 null 或者 typeof 返回类型是否为 object
            if (obj == null || typeof obj !== "object") return obj;
            else if (obj instanceof Date) return new Date(obj);
            else if (obj instanceof RegExp) return new RegExp(obj);

            // 判断集合是否有这个属性,有则直接 return obj
            if (hash.has(obj)) return hash.get(obj)
            const newObj = new obj.constructor();

            // 将属性和拷贝后的值作为一个map
            hash.set(obj, newObj);

            // 遍历 obj
            for (let key in Object.getOwnPropertyDescriptors(obj)) {
                // 过滤掉原型身上的属性
                if (obj.hasOwnProperty(key)) {
                    // 递归拷贝
                    newObj[key] = clone(obj[key], hash);
                }

            }
            return newObj;
        }

async,await异常捕获

// 捕获async await异常
async function asyncAwaitError(fn) {
  try {
    let resp = await fn;
    return [null, resp];
  } catch (err) {
    return [err, null];
  }
}


//使用
async getList5() {
			let [err, res] = await this.asyncAwaitError(this.httpTest());
			console.log('res===>', res);
            console.log('err===>', err);
}

import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:bluetooth_low_energy/bluetooth_low_energy.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:permission_handler/permission_handler.dart'; import 'comm_protocol.dart'; import 'package:path_provider/path_provider.dart'; import 'dart:async'; import 'package:flutter/services.dart'; class BleAdvertiseManager { // 单例实例 static final BleAdvertiseManager _instance = BleAdvertiseManager._internal(); static const MethodChannel _methodChannel = MethodChannel('com.gdmsdz.light/ble_power'); // 获取当前发射功率级别 // 工厂构造函数 factory BleAdvertiseManager() { return _instance; } // 私有构造函数 BleAdvertiseManager._internal() { // 初始化代码 } // 蓝牙低功耗核心管理器(仅插件实例) final PeripheralManager _peripheralManager = PeripheralManager(); Timer? _stopTimer; // 状态管理变量 bool _isAdvertising = false; bool _isSendingCommand = false; String? _currentCommand; bool _shouldCancelCurrentCommand = false; final LightProtocol _protocol = LightProtocol.withSequentialProductId(); int _sequenceNumber = -1; // 防快速点击:记录最后一次命令发送时间 DateTime? _lastCommandSentTime; // 当前操作码,用于动态生成数据 int? _currentOpCode; // 当前设备识别码,用于动态生成数据 int? _currentDeviceId; // 添加一个标志,跟踪设备ID是否已手动设置 bool _isDeviceIdManuallySet = false; // 使用ValueNotifier来管理广播状态 final ValueNotifier<bool> isAdvertisingNotifier = ValueNotifier(false); // 全局快速广播开关与默认参数 bool fastMode = true; bool muteAdvertising = false; // 静音广播开关 // 快速广播⬇️ static const int _fastRepeat = 10; // 连发次数 static const int _fastIntervalMs = 100; // 包间隔(ms) static const double _fastBroadcastDurationMs = 200;// 单次广播持续(ms, 可为小数) static const int _packetsPerBroadcast = 40; int txPowerLevel = 4; // 普通广播⬇️ static const int _normalRepeat = 1; static const int _normalIntervalMs = 200; static const double _normalBroadcastDurationMs = 150.0; int get _repeatDefault => fastMode ? _fastRepeat : _normalRepeat; int get _intervalDefaultMs => fastMode ? _fastIntervalMs : _normalIntervalMs; double get _broadcastDefaultMs => fastMode ? _fastBroadcastDurationMs : _normalBroadcastDurationMs; bool _shouldGenerateNewDeviceId = false; Future<List<int>> getAllDeviceIds() async { return await DeviceIdManager().loadAllDeviceIds(); } // 设置发射功率(仅安卓有效) Future<bool> setTxPowerLevel(int powerLevel) async { if (!Platform.isAndroid) { debugPrint('发射功率设置仅支持安卓平台'); return false; } try { // 调用原生方法,传入功率级别(如-21、-15、-7、0、4) final bool success = await _methodChannel.invokeMethod( 'setTxPowerLevel', {'powerLevel': powerLevel}, ); debugPrint('发射功率设置${success ? '成功' : '失败'},级别: $powerLevel dBm'); return success; } on PlatformException catch (e) { debugPrint('设置发射功率出错: ${e.message}'); return false; } } // 创建一个新设备。 Future<int> createNewDevice() async { final newDeviceId = await DeviceIdManager().getNextAvailableDeviceId(); _currentDeviceId = newDeviceId; await DeviceIdManager().saveDeviceId(newDeviceId); // 不在此处设置上次使用的设备ID,以避免冲突。 debugPrint('创建新设备,分配了新的设备ID: $newDeviceId'); await _initProductId(newDeviceId); // 为新设备初始化产品ID return newDeviceId; } /// 将管理器的上下文切换到现有设备。 /// 如果成功则返回true,如果设备ID不存在则返回false。 Future<bool> switchToDeviceId(int deviceId) async { final deviceIds = await DeviceIdManager().loadAllDeviceIds(); if (!deviceIds.contains(deviceId)) { debugPrint('切换失败:设备ID $deviceId 不存在'); return false; } _currentDeviceId = deviceId; await _initProductId(deviceId); // 加载现有设备的产品ID debugPrint('已切换到设备ID: $deviceId'); return true; } Future<void> _saveDeviceId(int deviceId) async { try { final directory = await getApplicationDocumentsDirectory(); final deviceIdPath = '${directory.path}/device_id.dat'; await File(deviceIdPath).writeAsString(deviceId.toString()); debugPrint('保存设备ID: $deviceId'); } catch (e) { debugPrint('保存设备ID时出错: $e'); } } void checkDeviceIdStatus() { debugPrint( '当前设备ID状态 - ID: $_currentDeviceId, 手动设置: $_isDeviceIdManuallySet'); } // 将2字节值转换为完整的128位蓝牙UUID UUID _convertToBluetoothUUID(int twoByteValue) { // 交换高低位 int swappedValue = ((twoByteValue & 0xFF) << 8) | ((twoByteValue >> 8) & 0xFF); String uuidStr = '0000${swappedValue.toRadixString(16).padLeft(4, '0')}-0000-1000-8000-00805F9B34FB'; return UUID.fromString(uuidStr); } Future<List<int>> generateNewProductId() async { final newId = await _generateSequentialProductId(); _protocol.setProductId(Uint8List.fromList(newId)); final counterPath = await _counterFilePath; final counterFile = File(counterPath); int counter = 1; if (await counterFile.exists()) { final content = await counterFile.readAsString(); counter = int.tryParse(content) ?? 1; } // 修复:使用当前设备ID保存新识别码 if (_currentDeviceId != null) { await _saveProductId(_currentDeviceId!, newId); } else { await _saveProductId(0, newId); } return newId; } Future<void> _initProductId(int deviceId) async { try { final path = await _getProductIdFilePath(deviceId); final file = File(path); if (await file.exists()) { final bytes = await file.readAsBytes(); if (bytes.length == 6) { _protocol.setProductId(Uint8List.fromList(bytes)); debugPrint('加载设备ID $deviceId 的识别码: 0x${_bytesToHex(bytes)}'); return; } } } catch (e) { debugPrint('加载产品识别码时出错: $e'); } List<int> newId = [deviceId, 0x00, 0x00, 0x00, 0x00, 0x00]; _protocol.setProductId(Uint8List.fromList(newId)); await _saveProductId(deviceId, newId); debugPrint('为设备ID $deviceId 生成新识别码: 0x${_bytesToHex(newId)}'); } void setProductId(int deviceId, List<int> productId) { _currentDeviceId = deviceId; // 保存设备ID用于动态生成 if (productId.length != 6) { throw ArgumentError("产品识别码必须是6字节"); } _protocol.setProductId(Uint8List.fromList(productId)); _saveProductId(deviceId, productId); } List<int> get currentProductId => _protocol.productId.toList(); Future<void> _saveProductId(int deviceId, List<int> productId) async { try { final directory = await getApplicationDocumentsDirectory(); final path = '${directory.path}/product_id_$deviceId.dat'; final file = File(path); await file.writeAsBytes(productId); await _saveProductIdHistory(productId); } catch (e) { debugPrint('保存产品识别码时出错: $e'); } } Future<String> get _counterFilePath async { final directory = await getApplicationDocumentsDirectory(); return '${directory.path}/product_counter.txt'; } static String _bytesToHex(List<int> bytes) { return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(''); } Future<List<int>> _generateSequentialProductId() async { try { final counterPath = await _counterFilePath; final counterFile = File(counterPath); int counter = 1; if (await counterFile.exists()) { final content = await counterFile.readAsString(); counter = int.tryParse(content) ?? 1; } Uint8List id = _protocol.generateSequentialProductId(); await counterFile.writeAsString((counter + 1).toString()); debugPrint('生成顺序产品识别码: 0x${_bytesToHex(id)}'); return id.toList(); } catch (e) { debugPrint('生成顺序产品识别码时出错: $e'); return _generateFallbackProductId(); } } List<int> _generateFallbackProductId() { final random = Random(); int firstByte = random.nextInt(256); List<int> id = [firstByte, 0x00, 0x00, 0x00, 0x00, 0x00]; debugPrint( '新产品识别码: 0x${id[0].toRadixString(16).padLeft(2, '0').toUpperCase()}'); return id; } Future<void> _saveProductIdHistory(List<int> productId) async { try { final historyPath = await _productIdHistoryPath; final historyFile = File(historyPath); String history = await historyFile.exists() ? await historyFile.readAsString() : ''; history += '${DateTime.now().toIso8601String()}: 0x${_bytesToHex(productId)}\n'; await historyFile.writeAsString(history); } catch (e) { debugPrint('保存产品识别码历史记录时出错: $e'); } } Future<String> get _productIdHistoryPath async { final directory = await getApplicationDocumentsDirectory(); return '${directory.path}/product_id_history.txt'; } Future<String> _getProductIdFilePath(int deviceId) async { final directory = await getApplicationDocumentsDirectory(); return '${directory.path}/product_id_$deviceId.dat'; } int _getNextSequenceNumber() { _sequenceNumber = (_sequenceNumber + 1); if (_sequenceNumber > 256) { _sequenceNumber = 1; } return _sequenceNumber; } // 通用方法:根据设备ID和操作码动态填充数据体 // 仅填充00 01,剩余用00补充(示例) void _fillDynamicDataToBody( Uint8List dataBody, int startIndex, int length, int opCode, ) { if (_currentDeviceId == null) { debugPrint('设备ID未初始化,使用硬编码00 01填充数据体'); for (int i = 0; i < length; i++) { int position = startIndex + i; if (position >= dataBody.length) break; // 前两个字节为00、01,后续都为00 dataBody[position] = i == 0 ? 0x00 : (i == 1 ? 0x01 : 0x00); } return; } } // 修改广播发送逻辑,优化广播数据结构(可配置持续时间与更短启停间隔) Future<void> _sendAdvNonConnIndBroadcast( int opCode, Uint8List dataBody, {double broadcastDurationMs = 100.0, required int txPowerLevel, }) async { try { // 权限检查 if (!await _checkBluetoothPermissions()) { debugPrint('蓝牙权限不足,无法发送广播'); EasyLoading.showError('蓝牙权限不足,请授予必要权限'); // 抛出特定异常,让上层调用者知道是权限问题 throw Exception('蓝牙权限不足'); } if (Platform.isAndroid) { await setTxPowerLevel(txPowerLevel); } // 确保设备ID已初始化 if (_currentDeviceId == null) { debugPrint('设备ID未初始化,将创建一个新设备'); await createNewDevice(); } Uint8List broadcastPacket = dataBody; debugPrint('收到的广播包长度: ${broadcastPacket.length}字节'); // 将广播包转换为十六进制字符串 String broadcastPacketHex = broadcastPacket .map((byte) => byte.toRadixString(16).padLeft(2, '0')) .join('') .toUpperCase(); // 添加调试日志:打印广播包十六进制字符串和设备ID debugPrint( '生成的广播包十六进制字符串 (长度: ${broadcastPacketHex.length}): $broadcastPacketHex'); debugPrint('当前使用的设备ID: $_currentDeviceId'); // 使用当前设备ID、操作码和广播包十六进制字符串生成动态UUID列表 List<UUID> uuidList = UuidManager.getDynamicServiceUuids( deviceId: _currentDeviceId!, opCode: opCode, broadcastPacketHex: broadcastPacketHex) .map((uuid16) { int twoByteValue = int.parse(uuid16, radix: 16); return _convertToBluetoothUUID(twoByteValue); }).toList(); // 添加调试日志:打印UUID列表 debugPrint( '生成的UUID列表 (数量: ${uuidList.length}): ${uuidList.map((uuid) => uuid.toString()).join(', ')}'); // 广播数据结构 Advertisement advertisement = Advertisement( name: 'MS', serviceUUIDs: uuidList, ); if (_isAdvertising) { debugPrint('停止当前广播...'); await stopAdvertising(); // 缩短停播到重启之间的等待,依据本次广播持续时间动态调整,最小5ms,最大50ms final int coolDownMs = max(5, min(50, (broadcastDurationMs / 3).round())); await Future.delayed(Duration(milliseconds: coolDownMs)); } if (_shouldCancelCurrentCommand) { debugPrint("当前命令 ($_currentCommand) 在广播会话间隔期间被取消。"); } // 启动广播 await _peripheralManager.startAdvertising(advertisement); _isAdvertising = true; isAdvertisingNotifier.value = true; debugPrint('广播已成功启动,操作码: 0x${opCode.toRadixString(16).padLeft(2, '0')}'); debugPrint('当前广播状态: _isAdvertising=$_isAdvertising, _shouldCancelCurrentCommand=$_shouldCancelCurrentCommand'); // 按可配置时长自动停止广播 _stopTimer?.cancel(); _stopTimer = Timer(Duration(milliseconds: broadcastDurationMs.round()), () { if (_isAdvertising) { stopAdvertising(); } }); debugPrint('广播已启动,将在 ${broadcastDurationMs}ms 后自动停止'); } catch (e) { debugPrint('发送广播时捕获到未处理的异常: $e'); // 专门处理蓝牙错误代码3(权限或蓝牙未开启) if (e.toString().contains('error code: 3')) { // debugPrint('蓝牙广播失败:错误代码3 - 请检查蓝牙权限和蓝牙开关状态'); // EasyLoading.showError('蓝牙广播失败,请检查蓝牙权限和开关状态'); // 抛出特定异常,让外层循环能够正确处理,避免无限广播 throw Exception('蓝牙广播失败:错误代码3 - 权限或蓝牙未开启'); } // 确保即使发生错误也重置状态 _isAdvertising = false; isAdvertisingNotifier.value = false; // 对于其他异常也重新抛出,让外层处理 rethrow; } } // 权限检查函数 Future<bool> _checkBluetoothPermissions() async { // iOS平台需要特殊处理蓝牙权限 if (Platform.isIOS) { // iOS上使用蓝牙权限检查 PermissionStatus bluetoothStatus = await Permission.bluetooth.status; if (bluetoothStatus != PermissionStatus.granted) { Map<Permission, PermissionStatus> statuses = await [ Permission.bluetooth, ].request(); return statuses[Permission.bluetooth] == PermissionStatus.granted; } return true; } else { // Android平台使用标准蓝牙权限检查 PermissionStatus advertiseStatus = await Permission.bluetoothAdvertise.status; PermissionStatus connectStatus = await Permission.bluetoothConnect.status; PermissionStatus scanStatus = await Permission.bluetoothScan.status; if (advertiseStatus != PermissionStatus.granted || connectStatus != PermissionStatus.granted || scanStatus != PermissionStatus.granted) { Map<Permission, PermissionStatus> statuses = await [ Permission.bluetoothAdvertise, Permission.bluetoothConnect, Permission.bluetoothScan, ].request(); return statuses[Permission.bluetoothAdvertise] == PermissionStatus.granted && statuses[Permission.bluetoothConnect] == PermissionStatus.granted && statuses[Permission.bluetoothScan] == PermissionStatus.granted; } return true; } } void _stopBroadcast() async { try { await _peripheralManager.stopAdvertising(); _isAdvertising = false; isAdvertisingNotifier.value = false; } catch (e) { debugPrint('停止广播失败: $e'); } } Future<void> stopAdvertising() async { if (_isAdvertising) { _stopTimer?.cancel(); await _peripheralManager.stopAdvertising(); _isAdvertising = false; isAdvertisingNotifier.value = false; debugPrint('广播已停止'); } } Future<void> cancelCurrentCommand() async { if (_isSendingCommand) { _shouldCancelCurrentCommand = true; _stopTimer?.cancel(); debugPrint('取消当前命令: $_currentCommand'); if (_isAdvertising) { await _peripheralManager.stopAdvertising().catchError((e) { debugPrint('强制停止广播错误: $e'); }); _isAdvertising = false; isAdvertisingNotifier.value = false; } // 移除立即重置状态的代码,统一由_sendCommand的finally块处理 // 保留取消标志,确保循环能够检测到取消 } } Future<void> startAdvertising(String serviceUuid, String command) async { await _sendCommand( commandName: 'startAdvertising', opCode: 0x0C, buildDataBody: (sequence) { return _protocol.buildPairingPacket(sequence, true); }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } Future<void> sendUnbindCommand() async { await _sendCommand( commandName: 'unbindDevice', opCode: 0x0C, buildDataBody: (sequence) { return _protocol.buildPairingPacket(sequence, false); // isPair: false for unbinding }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } // {int count = 10} Future<void> advertiseButtonCommand(String command,) async { await _sendCommand( commandName: 'buttonCommandAdvNonConn', opCode: 0x15, buildDataBody: (sequence) { Uint8List dataBody = Uint8List(13); List<int> commandBytes = ('TC$command').codeUnits; int copyLength = commandBytes.length < 13 ? commandBytes.length : 13; dataBody.setRange(0, copyLength, commandBytes); // 动态填充剩余部分 int remainingLength = 13 - copyLength; if (remainingLength > 0) { _fillDynamicDataToBody(dataBody, copyLength, remainingLength, 0x15); } return dataBody; }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } Future<void> sendFanModeCommand(int mode) async { await _sendCommand( commandName: 'fanModeControlAdvNonConn', opCode: 0x16, buildDataBody: (sequence) { return _protocol.buildFanModePacket(sequence, mode); }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } Future<void> sendMotorDirectionCommand(bool isReverse) async { await _sendCommand( commandName: 'motorDirectionCommandAdvNonConn', opCode: 0x15, buildDataBody: (sequence) { return _protocol.buildMotorDirectionPacket(sequence, !isReverse); }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } Future<void> sendOscillatingCommand(bool isEnabled) async { await _sendCommand( commandName: 'oscillatingControlAdvNonConn', opCode: 0x17, buildDataBody: (sequence) { return _protocol.buildSwingPacket(sequence, isEnabled); }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } Future<void> sendSwitchCommand(bool isOn) async { await _sendCommand( commandName: 'switchControlAdvNonConn', opCode: 0x00, buildDataBody: (sequence) { return _protocol.buildSwitchControlPacket(sequence, isOn); }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } Future<void> sendMotorSpeedCommand(int speed) async { if (speed < 1 || speed > 100) { throw ArgumentError('风速值必须在1~100范围内,当前值: $speed'); } await _sendCommand( commandName: 'motorSpeedControlAdvNonConn', opCode: 0x14, buildDataBody: (sequence) { return _protocol.buildMotorSpeedPacket(sequence, speed); }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } Future<void> advertisePairCommand(bool isPair) async { await _sendCommand( commandName: 'pairingCommandAdvNonConn', opCode: 0x0C, buildDataBody: (sequence) { Uint8List dataBody = _protocol.buildPairingPacket(sequence, isPair); // 确保数据体长度并动态填充剩余部分 if (dataBody.length < 14) { Uint8List extendedBody = Uint8List(14); extendedBody.setRange(0, dataBody.length, dataBody); _fillDynamicDataToBody( extendedBody, dataBody.length, 14 - dataBody.length, 0x0C); return extendedBody; } return dataBody; }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } Future<void> sendSetTimerCommandAdvNonConn( int hour, int minute, int second, int weekByte) async { await _sendCommand( commandName: 'setTimerCommandAdvNonConn', opCode: 0x0D, buildDataBody: (sequence) { return _protocol.buildSetTimerPacket( sequence, hour, minute, second, weekByte); }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } Future<void> sendReadTimerCommand() async { await _sendCommand( commandName: 'readTimerCommand', opCode: 0x0E, buildDataBody: (sequence) { Uint8List dataBody = _protocol.buildReadTimerPacket(sequence); // 确保数据体长度并动态填充剩余部分 if (dataBody.length < 14) { Uint8List extendedBody = Uint8List(14); extendedBody.setRange(0, dataBody.length, dataBody); _fillDynamicDataToBody( extendedBody, dataBody.length, 14 - dataBody.length, 0x0E); return extendedBody; } return dataBody; }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } Future<void> sendUnbindDeviceCommand() async { await advertiseButtonCommand('UNPAIR_DEVICE'); } Future<void> sendSetBrightnessCommand(int brightness) async { if (brightness < 0 || brightness > 100) { throw ArgumentError('亮度值必须在0~100范围内'); } await _sendCommand( commandName: 'setBrightnessCommandAdvNonConn', opCode: 0x01, buildDataBody: (sequence) { return _protocol.buildSetBrightnessPacket(sequence, brightness); }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } Future<void> sendSetColorTemperatureCommand(int temperature) async { await _sendCommand( commandName: 'setColorTemperatureCommandAdvNonConn', opCode: 0x02, buildDataBody: (sequence) { Uint8List dataBody = _protocol.buildSetColorTemperaturePacket(sequence, temperature); // 确保数据体长度并动态填充剩余部分 if (dataBody.length < 14) { Uint8List extendedBody = Uint8List(14); extendedBody.setRange(0, dataBody.length, dataBody); _fillDynamicDataToBody( extendedBody, dataBody.length, 14 - dataBody.length, 0x02); return extendedBody; } return dataBody; }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } Future<void> sendSceneModeCommand(int mode) async { await _sendCommand( commandName: 'sceneModeCommandAdvNonConn', opCode: 0x04, buildDataBody: (sequence) { return _protocol.buildSceneModePacket(sequence, mode); }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } Future<void> sendNightLightCommand() async { await _sendCommand( commandName: 'nightLightCommandAdvNonConn', opCode: 0x19, buildDataBody: (sequence) { return _protocol.buildNightLightPacket(sequence); }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } Future<void> sendAuxLightCommand(bool isEnabled) async { await _sendCommand( commandName: 'auxLightCommandAdvNonConn', opCode: 0x1A, buildDataBody: (sequence) { return _protocol.buildAuxLightPacket(sequence, isEnabled); }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } // 发送设置灯珠数量命令 Future<void> sendSetLedCountCommand(int count) async { await _sendCommand( commandName: 'setLedCountCommandAdvNonConn', opCode: 0x1B, buildDataBody: (sequence) { return _protocol.buildLedCountPacket(sequence, count); }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } // 发送0x18操作码的广播命令(风扇开关) Future<void> sendFanSwitchCommand(bool enable) async { await _sendCommand( commandName: 'FanSwitch', opCode: 0x18, buildDataBody: (sequence) { return _protocol.buildFanSwitchPacket(sequence, enable); }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } Future<void> sendSetColorCommand(int r, int g, int b, int w, bool setBrightness, int brightness) async { await _sendCommand( commandName: 'setColorCommandAdvNonConn', opCode: 0x03, buildDataBody: (sequence) { Uint8List dataBody = _protocol.buildSetColorPacket( sequence, r, g, b, w, setBrightness, brightness); // 确保数据体长度并动态填充剩余部分 if (dataBody.length < 14) { Uint8List extendedBody = Uint8List(14); extendedBody.setRange(0, dataBody.length, dataBody); _fillDynamicDataToBody( extendedBody, dataBody.length, 14 - dataBody.length, 0x03); return extendedBody; } return dataBody; }, repeat: _repeatDefault, intervalMs: _intervalDefaultMs, broadcastDurationMs: _broadcastDefaultMs, txPowerLevel: txPowerLevel, ); } // 支持在单次广播中发送多个数据包 Future<void> _sendCommand({ required String commandName, required int opCode, required Uint8List Function(int sequence) buildDataBody, int? repeat, int? intervalMs, double? broadcastDurationMs, int? packetsPerBroadcast, // 新增参数 int txPowerLevel = 4, }) async { // 防快速点击:检查是否在短时间内重复发送相同命令 final now = DateTime.now(); if (_lastCommandSentTime != null && now.difference(_lastCommandSentTime!) < const Duration(milliseconds: 50) && _currentCommand == commandName) { debugPrint('防快速点击:跳过重复命令: $commandName'); return; } // 更新最后发送时间 _lastCommandSentTime = now; // 在开始新命令前取消之前的命令 // await cancelCurrentCommand(); // await Future.delayed(const Duration(milliseconds: 50)); // 给足够时间让系统停止广播 final int finalRepeat = repeat ?? _repeatDefault; final int finalIntervalMs = intervalMs ?? _intervalDefaultMs; final double finalBroadcastDurationMs = broadcastDurationMs ?? _broadcastDefaultMs; final int finalPacketsPerBroadcast = packetsPerBroadcast ?? _packetsPerBroadcast; // 默认使用10个数据包 debugPrint('当前设置: 重复次数=$finalRepeat, 数据包数量=$finalPacketsPerBroadcast, 间隔=${finalIntervalMs}ms, 广播持续时间=${finalBroadcastDurationMs}ms'); // 调试静音:直接跳过所有广播发送 if (muteAdvertising) { debugPrint('广播已静音,跳过发送: ' + commandName); return; } // 确保设备ID已初始化 if (_currentDeviceId == null) { debugPrint('设备ID为null,开始初始化...'); await createNewDevice(); } else { debugPrint('设备ID已初始化: $_currentDeviceId'); } // 增强取消逻辑:确保在发送新命令前完全停止之前的所有广播 // 此逻辑已被开头的cancelCurrentCommand()调用替代 _isSendingCommand = true; _currentCommand = commandName; _shouldCancelCurrentCommand = false; _currentOpCode = opCode; try { // 在命令开始时获取序列号,确保整个命令使用相同的序列号 int sequence = _getNextSequenceNumber(); for (int i = 0; i < finalRepeat; i++) { if (_shouldCancelCurrentCommand) { debugPrint("当前命令 ($commandName) 在重复期间被取消。"); break; } // 单次广播会话中发送多个数据包 for (int j = 0; j < finalPacketsPerBroadcast; j++) { debugPrint("开始处理数据包 ${j+1}/$finalPacketsPerBroadcast"); // 使用相同的序列号,不再为每个数据包获取新序列号 if (_shouldCancelCurrentCommand) { debugPrint("当前命令 ($commandName) 在发送数据包期间被取消。"); break; // 直接返回而不是break,确保完全停止 } Uint8List dataBody = buildDataBody(sequence); debugPrint('发送数据包(${j + 1}/$finalPacketsPerBroadcast) - 操作码: 0x${opCode.toRadixString(16).padLeft(2, '0')}'); await _sendAdvNonConnIndBroadcast(opCode, dataBody, broadcastDurationMs: finalBroadcastDurationMs,txPowerLevel: txPowerLevel,); debugPrint("数据包 ${j+1}/$finalPacketsPerBroadcast 发送完成"); if (j < finalPacketsPerBroadcast - 1) { if (_shouldCancelCurrentCommand) { debugPrint("当前命令 ($commandName) 在数据包间隔期间被取消。"); break; // 直接返回而不是break,确保完全停止 } // 数据包之间的间隔可以比广播会话之间的间隔更小 debugPrint('数据包间隔前状态: _shouldCancelCurrentCommand=$_shouldCancelCurrentCommand'); await Future.delayed(Duration(milliseconds: finalIntervalMs ~/ 2)); debugPrint('数据包间隔后状态: _shouldCancelCurrentCommand=$_shouldCancelCurrentCommand'); } } if (i < finalRepeat - 1) { if (_shouldCancelCurrentCommand) { debugPrint("当前命令 ($commandName) 在广播会话间隔期间被取消。"); break; // 直接返回而不是break,确保完全停止 } await Future.delayed(Duration(milliseconds: finalIntervalMs)); } } } catch (e) { debugPrint('发送命令出错: $e'); // 如果是权限不足的异常,不显示错误提示,因为已经在权限检查时显示过了 if (!e.toString().contains('蓝牙权限不足')) { // EasyLoading.showError('发送命令失败: $e'); } // 强制停止所有广播,防止异常后继续发送 try { await forceStopAllAdvertising(); } catch (stopError) { debugPrint('异常后强制停止广播失败: $stopError'); } // 重新抛出异常,确保状态正确重置 rethrow; } finally { // 兜底:确保广播在命令完成后被停止,防止极端情况下仍在播 try { await stopAdvertising(); debugPrint('命令完成,已确保停止广播'); } catch (e) { debugPrint('命令完成后停止广播异常: $e'); } // 无论命令是否被取消,都重置状态(移除条件判断) _isSendingCommand = false; _currentCommand = null; _currentOpCode = null; _shouldCancelCurrentCommand = false; _lastCommandSentTime = null; } } // 添加一个强制停止所有广播的方法 Future<void> forceStopAllAdvertising() async { debugPrint('强制停止所有广播...'); _stopTimer?.cancel(); try { await _peripheralManager.stopAdvertising(); } catch (e) { debugPrint('强制停止广播异常: $e'); } _isAdvertising = false; isAdvertisingNotifier.value = false; debugPrint('强制停止广播完成'); } } class UuidManager { // 动态生成 UUID,从广播包的十六进制字符串中截取 static List<String> getDynamicServiceUuids({ required int deviceId, required int opCode, required String broadcastPacketHex, }) { List<String> dynamicUuids = []; String protocolString = broadcastPacketHex.padLeft(52, '0').substring(0, 52); // 从协议字符串中每 2 字节截取生成动态 UUID for (int i = 0; i < protocolString.length; i += 4) { int end = i + 4; if (end > protocolString.length) { end = protocolString.length; } // 2 字节作为一个 UUID 部分 String uuidPart = protocolString.substring(i, end); // 如果不足2 字节,用 0 填充 if (uuidPart.length < 4) { uuidPart = uuidPart.padRight(4, '0'); } dynamicUuids.add(uuidPart.toUpperCase()); } while (dynamicUuids.length < 13) { // 当数据不足时,用0000填充剩余UUID dynamicUuids.add('0000'); } int seed = (deviceId ^ opCode ^ dynamicUuids.length) & 0xFFFF; // 交换高低位 int swappedSeed = ((seed & 0xFF) << 8) | ((seed >> 8) & 0xFF); String uuid = swappedSeed.toRadixString(16).padLeft(4, '0').toUpperCase(); if (dynamicUuids.length > 13) { dynamicUuids = dynamicUuids.sublist(0, 13); } // 调试日志 debugPrint( 'UuidManager生成的动态UUID列表 (数量: ${dynamicUuids.length}): $dynamicUuids'); return dynamicUuids; } } // 添加到文件末尾,在UuidManager类之后 class DeviceIdManager { static final DeviceIdManager _instance = DeviceIdManager._internal(); factory DeviceIdManager() => _instance; DeviceIdManager._internal(); // 加载所有保存的设备ID Future<List<int>> loadAllDeviceIds() async { try { final directory = await getApplicationDocumentsDirectory(); final deviceIdsPath = '${directory.path}/device_ids.dat'; final deviceIdsFile = File(deviceIdsPath); if (await deviceIdsFile.exists()) { final content = await deviceIdsFile.readAsString(); return content .split(',') .map((id) => int.tryParse(id) ?? 0) .where((id) => id > 0) .toList(); } } catch (e) { debugPrint('加载设备ID列表出错: $e'); } return []; } // 保存新的设备ID Future<void> saveDeviceId(int deviceId) async { try { final directory = await getApplicationDocumentsDirectory(); final deviceIdsPath = '${directory.path}/device_ids.dat'; final deviceIdsFile = File(deviceIdsPath); List<int> deviceIds = []; if (await deviceIdsFile.exists()) { final content = await deviceIdsFile.readAsString(); deviceIds = content .split(',') .map((id) => int.tryParse(id) ?? 0) .where((id) => id > 0) .toList(); } // 确保设备ID唯一 if (!deviceIds.contains(deviceId)) { deviceIds.add(deviceId); deviceIds.sort(); // 保持ID有序 await deviceIdsFile.writeAsString(deviceIds.join(',')); debugPrint('保存新设备ID: $deviceId, 当前设备ID列表: $deviceIds'); } } catch (e) { debugPrint('保存设备ID出错: $e'); } } // 获取下一个可用的设备ID Future<int> getNextAvailableDeviceId() async { List<int> deviceIds = await loadAllDeviceIds(); if (deviceIds.isEmpty) { return 1; // 如果没有设备ID,从1开始 } // 找到第一个缺失的ID int nextId = 1; for (int id in deviceIds) { if (id > nextId) { break; } nextId = id + 1; } return nextId; } // 获取上次使用的设备ID Future<int?> getLastUsedDeviceId() async { try { final directory = await getApplicationDocumentsDirectory(); final lastUsedPath = '${directory.path}/last_used_device_id.dat'; final lastUsedFile = File(lastUsedPath); if (await lastUsedFile.exists()) { final content = await lastUsedFile.readAsString(); return int.tryParse(content) ?? null; } } catch (e) { debugPrint('加载上次使用的设备ID出错: $e'); } return null; } // 设置上次使用的设备ID Future<void> setLastUsedDeviceId(int deviceId) async { try { final directory = await getApplicationDocumentsDirectory(); final lastUsedPath = '${directory.path}/last_used_device_id.dat'; await File(lastUsedPath).writeAsString(deviceId.toString()); debugPrint('设置上次使用的设备ID: $deviceId'); } catch (e) { debugPrint('保存上次使用的设备ID出错: $e'); } } // 删除上次使用的设备ID Future<void> deleteLastUsedDeviceId() async { try { final directory = await getApplicationDocumentsDirectory(); final lastUsedPath = '${directory.path}/last_used_device_id.dat'; final file = File(lastUsedPath); if (await file.exists()) { await file.delete(); debugPrint('上次使用的设备ID已删除'); } } catch (e) { debugPrint('删除上次使用的设备ID时出错: $e'); } } // 删除指定的设备ID Future<void> deleteDeviceId(int deviceId) async { try { final directory = await getApplicationDocumentsDirectory(); final deviceIdsPath = '${directory.path}/device_ids.dat'; final deviceIdsFile = File(deviceIdsPath); if (await deviceIdsFile.exists()) { List<int> deviceIds = await loadAllDeviceIds(); if (deviceIds.remove(deviceId)) { await deviceIdsFile.writeAsString(deviceIds.join(',')); debugPrint('删除设备ID: $deviceId, 当前设备ID列表: $deviceIds'); } } } catch (e) { debugPrint('删除设备ID出错: $e'); } } }
09-13
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值