From 320ece473f3708f1409ecf4ad13e6b5636cdaa61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9C=E8=99=9E=E6=B8=A0=E6=88=90=E2=80=9D?= <“yuqucheng2006@qq.com”> Date: Fri, 4 Sep 2026 10:59:43 +0800 Subject: [PATCH] =?UTF-8?q?feat(bluetooth,ui):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=AF=AB=E7=B1=B3=E6=B3=A2=E9=9B=B7=E8=BE=BE=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=B9=B6=E4=BC=98=E5=8C=96=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增MillimeterWaveSetting设备状态字段与更新方法 2. 修复HomePageCtrl中设备配置空引用风险 3. 重构BLE通讯状态更新逻辑,统一MAC地址获取 4. 补充各类BLE通知的数据长度校验 5. 新增设备锁状态参数变更通知处理 6. 优化日志描述与协程管理逻辑 --- .../Bluetooth/BLECommunicationManager.cs | 97 ++++++++++++++++--- Assets/Scripts/Bluetooth/BLEDeviceState.cs | 17 ++++ .../Scripts/UI/Pages/HomePage/HomePageCtrl.cs | 43 ++++++-- 3 files changed, 133 insertions(+), 24 deletions(-) diff --git a/Assets/Scripts/Bluetooth/BLECommunicationManager.cs b/Assets/Scripts/Bluetooth/BLECommunicationManager.cs index 2d9d4a7..216b7d1 100644 --- a/Assets/Scripts/Bluetooth/BLECommunicationManager.cs +++ b/Assets/Scripts/Bluetooth/BLECommunicationManager.cs @@ -128,7 +128,7 @@ namespace Kill.Bluetooth /// /// 更新当前设备状态,并仅在值发生变化时广播状态事件。 /// - private bool UpdateDeviceState(string deviceMac, BLEDeviceStateField field, Action update) + private bool UpdateDeviceState(string deviceMac, BLEDeviceStateField field, Func update) { if (update == null) { @@ -162,10 +162,9 @@ namespace Kill.Bluetooth return changed; } - private bool UpdateStateFromResponse(BLEResponse response, BLEDeviceStateField field, Action update) + private bool UpdateStateFromResponse(BLEResponse response, BLEDeviceStateField field, Func update) { - return response != null && response.IsSuccess && - UpdateDeviceState(GetCurrentDeviceMac(), field, update); + return response.IsSuccess && UpdateDeviceState(GetStateMac(), field, update); } private static string NormalizeDeviceMac(string deviceMac) @@ -295,11 +294,13 @@ namespace Kill.Bluetooth { Log($"蓝牙已断开: {address}"); - // 使用断开参数作为设备状态键;部分平台会在清空地址前触发事件。 - string stateMac = NormalizeDeviceMac(address); + // 断开参数通常是 BLE 地址,优先使用已记录的真实 MAC 作为状态键。 + string stateMac = string.IsNullOrEmpty(_lastStateMac) + ? NormalizeDeviceMac(address) + : _lastStateMac; if (string.IsNullOrEmpty(stateMac)) { - stateMac = string.IsNullOrEmpty(_lastStateMac) ? GetCurrentDeviceMac() : _lastStateMac; + stateMac = GetCurrentDeviceMac(); } _lastStateMac = string.IsNullOrEmpty(stateMac) ? _lastStateMac : stateMac; UpdateDeviceState( @@ -881,6 +882,15 @@ namespace Kill.Bluetooth Log($"定时任务设置成功: {tasks[0]}"); else Log($"定时任务批量设置成功,共 {tasks.Length} 条任务"); + + ScheduleTask[] normalizedTasks = new ScheduleTask[tasks.Length]; + for (int i = 0; i < tasks.Length; i++) + { + normalizedTasks[i] = tasks[i]; + normalizedTasks[i].TaskId = (byte)i; + } + UpdateStateFromResponse(response, BLEDeviceStateField.ScheduleTasks, + state => state.SetScheduleTasks(normalizedTasks)); } else { @@ -1601,6 +1611,8 @@ namespace Kill.Bluetooth SendFrame(frame, (response) => { var setting = MillimeterWaveSetting.FromBytes(response.Data); + UpdateStateFromResponse(response, BLEDeviceStateField.MillimeterWave, + state => state.SetMillimeterWave(setting)); callback?.Invoke(setting); OnMillimeterWaveSettingReceived?.Invoke(setting); Log($"读取毫米波雷达设置: {setting}"); @@ -1630,9 +1642,15 @@ namespace Kill.Bluetooth { bool success = response.IsSuccess; if (success) - Log($"毫米波雷达设置成功: {setting}"); + { + Log($"毫米波设置成功: {setting}"); + UpdateStateFromResponse(response, BLEDeviceStateField.MillimeterWave, + state => state.SetMillimeterWave(setting)); + } else - LogError($"毫米波雷达设置失败, 状态码={response.Status:X2}"); + { + LogError($"毫米波设置失败, 状态码={response.Status:X2}"); + } callback?.Invoke(success); }); } @@ -2612,7 +2630,7 @@ namespace Kill.Bluetooth // 通知帧优先处理,并且绝对不影响命令队列(IsWaitingResponse / _pendingCallback / _responseTimer) // 设备主动通知是异步旁路通道,与当前等待响应的命令是两条独立链路 - if (frame.ReadWrite == BLEConstants.RW_NOTIFY || frame.Command == BLEConstants.NOTIFY_COMMAND_CODE) + if (frame.ReadWrite == BLEConstants.RW_NOTIFY && frame.Command == BLEConstants.NOTIFY_COMMAND_CODE) { HandleNotification(frame.Data); return; @@ -2728,6 +2746,12 @@ namespace Kill.Bluetooth private void HandleDeviceStateChangeNotification(byte[] data) { + if (data == null || data.Length < 2) + { + LogWarning("设备状态变化通知数据长度不足"); + return; + } + var notify = DeviceStateChangeNotification.FromBytes(data); if (notify.WorkMode > (byte)WorkMode.Eliminate) { @@ -2737,7 +2761,7 @@ namespace Kill.Bluetooth Log($"设备状态变化通知: 工作模式=0x{notify.WorkMode:X2}"); UpdateDeviceState( - GetCurrentDeviceMac(), + GetStateMac(), BLEDeviceStateField.WorkMode, state => state.SetWorkMode((WorkMode)notify.WorkMode)); try @@ -2752,11 +2776,17 @@ namespace Kill.Bluetooth private void HandleErrorStatusNotification(byte[] data) { + if (data == null || data.Length < 8) + { + LogWarning("错误状态通知数据长度不足"); + return; + } + var notify = ErrorStatusNotification.FromBytes(data); if (data != null && data.Length >= 8) { UpdateDeviceState( - GetCurrentDeviceMac(), + GetStateMac(), BLEDeviceStateField.HardwareStatus, state => state.SetHardwareStatus(notify.Status)); } @@ -2773,6 +2803,12 @@ namespace Kill.Bluetooth private void HandleParameterChangeNotification(byte[] data) { + if (data == null || data.Length < 3) + { + LogWarning("参数变化通知数据长度不足"); + return; + } + var notify = ParameterChangeNotification.FromBytes(data); Log($"参数变化通知: 命令=0x{notify.CommandCode:X2}, 数据长度={(notify.Payload?.Length ?? 0)}"); @@ -2794,7 +2830,7 @@ namespace Kill.Bluetooth /// private void ApplyParameterChangeToDeviceState(ParameterChangeNotification notify) { - string deviceMac = GetCurrentDeviceMac(); + string deviceMac = GetStateMac(); if (string.IsNullOrEmpty(deviceMac) || notify.Payload == null) { return; @@ -2805,35 +2841,52 @@ namespace Kill.Bluetooth switch (notify.CommandCode) { case BLEConstants.CMD_LANGUAGE_SETTING: + if (notify.Payload.Length < 1) return; UpdateDeviceState(deviceMac, BLEDeviceStateField.Language, state => state.SetLanguage(LanguageSetting.FromBytes(notify.Payload))); break; case BLEConstants.CMD_SCHEDULE_TASK: + if (notify.Payload.Length < 7) return; UpdateDeviceState(deviceMac, BLEDeviceStateField.ScheduleTasks, state => state.SetScheduleTasks(ScheduleTaskListResponse.FromBytes(0x00, notify.Payload).Tasks)); break; + case BLEConstants.CMD_DEVICE_LOCK: + if (notify.Payload.Length < 1) return; + UpdateDeviceState(deviceMac, BLEDeviceStateField.LockState, + state => state.SetLockState(DeviceLockControl.FromBytes(notify.Payload))); + break; case BLEConstants.CMD_FILL_LIGHT_CONTROL: + if (notify.Payload.Length < 3) return; UpdateDeviceState(deviceMac, BLEDeviceStateField.FillLight, state => state.SetFillLight(FillLightControl.FromBytes(notify.Payload))); break; case BLEConstants.CMD_ANGLE_CONTROL: + if (notify.Payload.Length < 2) return; UpdateDeviceState(deviceMac, BLEDeviceStateField.Angle, state => state.SetAngle(AngleControl.FromBytes(notify.Payload))); break; case BLEConstants.CMD_DISTANCE_CONTROL: + if (notify.Payload.Length < 4) return; UpdateDeviceState(deviceMac, BLEDeviceStateField.Distance, state => state.SetDistance(DistanceControl.FromBytes(notify.Payload))); break; case BLEConstants.CMD_VISUAL_DETECTION_SETTING: + if (notify.Payload.Length < 2) return; UpdateDeviceState(deviceMac, BLEDeviceStateField.VisualDetection, state => state.SetVisualDetection(VisualDetectionSetting.FromBytes(notify.Payload))); break; + case BLEConstants.CMD_MILLIMETER_WAVE_SETTING: + if (notify.Payload.Length < 4) return; + UpdateDeviceState(deviceMac, BLEDeviceStateField.MillimeterWave, + state => state.SetMillimeterWave(MillimeterWaveSetting.FromBytes(notify.Payload))); + break; case BLEConstants.CMD_FILL_LIGHT_CONNECTION_STATUS: + if (notify.Payload.Length < 1) return; UpdateDeviceState(deviceMac, BLEDeviceStateField.FillLightConnection, state => state.SetFillLightConnection(FillLightConnectionStatus.FromBytes(notify.Payload))); break; default: - // 未映射的字段:不写仓库,避免误覆盖 + // 暂不写入本地状态,避免异常参数覆盖正确值 break; } } @@ -2845,11 +2898,17 @@ namespace Kill.Bluetooth private void HandleCapacitorStateNotification(byte[] data) { + if (data == null || data.Length < 2) + { + LogWarning("蓄能状态通知数据长度不足"); + return; + } + var notify = CapacitorStateNotification.FromBytes(data); if (data != null && data.Length >= 2) { UpdateDeviceState( - GetCurrentDeviceMac(), + GetStateMac(), BLEDeviceStateField.CapacitorState, state => state.SetCapacitorState(notify)); } @@ -2866,12 +2925,18 @@ namespace Kill.Bluetooth private void HandleFillLightConnectionNotification(byte[] data) { + if (data == null || data.Length < 2) + { + LogWarning("补光灯连接状态通知数据长度不足"); + return; + } + var notify = FillLightConnectionNotification.FromBytes(data); if (data != null && data.Length >= 2) { var status = new FillLightConnectionStatus { IsConnected = notify.IsConnected }; UpdateDeviceState( - GetCurrentDeviceMac(), + GetStateMac(), BLEDeviceStateField.FillLightConnection, state => state.SetFillLightConnection(status)); } diff --git a/Assets/Scripts/Bluetooth/BLEDeviceState.cs b/Assets/Scripts/Bluetooth/BLEDeviceState.cs index 8a0212e..2bf209d 100644 --- a/Assets/Scripts/Bluetooth/BLEDeviceState.cs +++ b/Assets/Scripts/Bluetooth/BLEDeviceState.cs @@ -52,6 +52,8 @@ namespace Kill.Bluetooth public VisualDetectionSetting? VisualDetection { get; private set; } + public MillimeterWaveSetting? MillimeterWave { get; private set; } + public LanguageSetting? Language { get; private set; } public HardwareStatus? HardwareStatus { get; private set; } @@ -183,6 +185,21 @@ namespace Kill.Bluetooth return true; } + internal bool SetMillimeterWave(MillimeterWaveSetting setting) + { + if (MillimeterWave.HasValue && + MillimeterWave.Value.Enable == setting.Enable && + MillimeterWave.Value.Sensitivity == setting.Sensitivity && + MillimeterWave.Value.ActualSafeDistance == setting.ActualSafeDistance) + { + return false; + } + + MillimeterWave = setting; + MarkUpdated(); + return true; + } + internal bool SetLanguage(LanguageSetting language) { if (Language.HasValue && Language.Value.Language == language.Language) diff --git a/Assets/Scripts/UI/Pages/HomePage/HomePageCtrl.cs b/Assets/Scripts/UI/Pages/HomePage/HomePageCtrl.cs index 998b267..5fb591a 100644 --- a/Assets/Scripts/UI/Pages/HomePage/HomePageCtrl.cs +++ b/Assets/Scripts/UI/Pages/HomePage/HomePageCtrl.cs @@ -60,6 +60,7 @@ namespace Kill.UI.Pages private int TOTAL_STATUS_COUNT = 8; // 总共需要查询的状态数量 private float statusQueryTimeout = 15f; // 状态查询超时时间(秒) private Coroutine statusQueryTimeoutCoroutine; // 超时协程 + private Coroutine deviceStatusQueryCoroutine; // 首次状态全量查询协程 // 串行查询相关 private Queue statusQueryQueue = new Queue(); // 状态查询队列 @@ -286,6 +287,12 @@ namespace Kill.UI.Pages /// private void StartDeviceStateInitialization() { + if (deviceStatusQueryCoroutine != null) + { + StopCoroutine(deviceStatusQueryCoroutine); + deviceStatusQueryCoroutine = null; + } + isDeviceStateInitializing = true; pendingStatusCount = TOTAL_STATUS_COUNT; @@ -334,6 +341,11 @@ namespace Kill.UI.Pages StopCoroutine(statusQueryTimeoutCoroutine); statusQueryTimeoutCoroutine = null; } + if (deviceStatusQueryCoroutine != null) + { + StopCoroutine(deviceStatusQueryCoroutine); + deviceStatusQueryCoroutine = null; + } bluetoothDeviceInfoCoroutine=StartCoroutine(CheckDeviceError()); Debug.Log("[HomePageCtrl] 设备状态初始化完成"); } @@ -363,6 +375,11 @@ namespace Kill.UI.Pages isBluetoothUserLoggedIn = false; isQueryingStatus = false; statusQueryQueue?.Clear(); + if (deviceStatusQueryCoroutine != null) + { + StopCoroutine(deviceStatusQueryCoroutine); + deviceStatusQueryCoroutine = null; + } UIManager.Instance?.ClearBackAction(); // 刷新首页设备连接状态UI(延迟到主循环执行,与连接成功时保持一致) pendingUIUpdates.Add(() => @@ -398,7 +415,7 @@ namespace Kill.UI.Pages StartDeviceStateInitialization(); // 串行查询设备状态 - StartCoroutine(QueryAllDeviceStatusSequential()); + deviceStatusQueryCoroutine = StartCoroutine(QueryAllDeviceStatusSequential()); } /// @@ -493,9 +510,13 @@ namespace Kill.UI.Pages /// /// 按 MAC 隔离:当前设备不匹配(已切换/未连接/选中设备不同)则丢弃通知 /// - private bool IsNotificationForCurrentDevice() + private bool IsNotificationForCurrentDevice(BLEDeviceState state = null) { - string mac = BLECommunicationManager.Instance?.GetCurrentDeviceMac(); + string mac = state?.DeviceMac; + if (string.IsNullOrEmpty(mac)) + { + mac = BLECommunicationManager.Instance?.GetCurrentDeviceMac(); + } string selectedMac = selectedDevice?.ble_mac; if (string.IsNullOrEmpty(mac) || string.IsNullOrEmpty(selectedMac)) return false; return string.Equals(mac, selectedMac, StringComparison.OrdinalIgnoreCase); @@ -509,7 +530,7 @@ namespace Kill.UI.Pages private void OnDeviceStateChanged(BLEDeviceState state, BLEDeviceStateField field) { if (state == null) return; - if (!IsNotificationForCurrentDevice()) return; + if (!IsNotificationForCurrentDevice(state)) return; if (isDeviceStateInitializing) return; switch (field) @@ -519,7 +540,10 @@ namespace Kill.UI.Pages { int mode = (int)state.WorkMode.Value; currentWorkMode = state.WorkMode.Value; - DataManager.Instance.deviceConfig.work_mode = DeviceConfig.ToServerString(state.WorkMode.Value); + if (DataManager.Instance.deviceConfig != null) + { + DataManager.Instance.deviceConfig.work_mode = DeviceConfig.ToServerString(state.WorkMode.Value); + } pendingUIUpdates.Add(() => deviceCtrl?.InitWrokMode(mode)); } break; @@ -576,12 +600,15 @@ namespace Kill.UI.Pages case BLEDeviceStateField.Language: if (state.Language.HasValue) { - DataManager.Instance.deviceConfig.language = state.Language.Value.IsChinese ? 1 : 0; - DataManager.Instance.SyncDeviceConfigToServer(); + if (DataManager.Instance.deviceConfig != null) + { + DataManager.Instance.deviceConfig.language = state.Language.Value.IsChinese ? 1 : 0; + DataManager.Instance.SyncDeviceConfigToServer(); + } } break; default: - // 其他字段(Connection / LockState / HardwareStatus / CapacitorState / FillLightConnection)暂不在主页直接刷新 + // 其他字段(Connection / HardwareStatus / CapacitorState / FillLightConnection)暂不在主页直接刷新 break; } }