From 1710194d2f44c48c6e6c9b27943ffa40f2e86d5c 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, 11 Sep 2026 13:42:23 +0800 Subject: [PATCH] =?UTF-8?q?fix(ble&homepage):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E8=93=9D=E7=89=99=E5=8F=82=E6=95=B0=E5=8F=98=E5=8C=96=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E4=B8=8E=E4=B8=BB=E9=A1=B5=E7=8A=B6=E6=80=81=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 调整BLE参数变化通知的数据长度校验,支持无载荷的通知场景 2. 当参数变化通知无载荷时,主动触发对应配置项的查询 3. 新增蓝牙状态补拉排队机制,解决串行查询期间请求被吞的问题 4. 优化主页配置轮询逻辑,使用独立快照避免BLE回写污染导致的循环查询 5. 新增定时任务变化检测与精准拉取逻辑 --- .../Bluetooth/BLECommunicationManager.cs | 45 ++++- .../Scripts/UI/Pages/HomePage/HomePageCtrl.cs | 189 +++++++++++++++++- 2 files changed, 227 insertions(+), 7 deletions(-) diff --git a/Assets/Scripts/Bluetooth/BLECommunicationManager.cs b/Assets/Scripts/Bluetooth/BLECommunicationManager.cs index c28844d..a222cbf 100644 --- a/Assets/Scripts/Bluetooth/BLECommunicationManager.cs +++ b/Assets/Scripts/Bluetooth/BLECommunicationManager.cs @@ -2803,7 +2803,8 @@ namespace Kill.Bluetooth private void HandleParameterChangeNotification(byte[] data) { - if (data == null || data.Length < 3) + // 最少2字节:通知类型 + 命令码(载荷允许为空,如固件上报 06-A0 不带新值) + if (data == null || data.Length < 2) { LogWarning("参数变化通知数据长度不足"); return; @@ -2812,6 +2813,48 @@ namespace Kill.Bluetooth var notify = ParameterChangeNotification.FromBytes(data); Log($"参数变化通知: 命令=0x{notify.CommandCode:X2}, 数据长度={(notify.Payload?.Length ?? 0)}"); + if (notify.Payload == null || notify.Payload.Length == 0) + { + // 硬件约定:参数变化通知不携带新值,收到后按命令码主动查询一次真实值 + switch (notify.CommandCode) + { + case BLEConstants.CMD_WORK_MODE_SETTING: + ReadWorkMode(); + break; + case BLEConstants.CMD_LANGUAGE_SETTING: + ReadLanguageSetting(); + break; + case BLEConstants.CMD_SCHEDULE_TASK: + ReadScheduleTasks(); + break; + case BLEConstants.CMD_DEVICE_LOCK: + ReadDeviceLockControl(); + break; + case BLEConstants.CMD_FILL_LIGHT_CONTROL: + ReadFillLightControl(); + break; + case BLEConstants.CMD_FILL_LIGHT_CONNECTION_STATUS: + ReadFillLightConnectionStatus(); + break; + case BLEConstants.CMD_ANGLE_CONTROL: + ReadAngleControl(); + break; + case BLEConstants.CMD_DISTANCE_CONTROL: + ReadDistanceControl(); + break; + case BLEConstants.CMD_VISUAL_DETECTION_SETTING: + ReadVisualDetectionSetting(); + break; + case BLEConstants.CMD_MILLIMETER_WAVE_SETTING: + ReadMillimeterWaveSetting(); + break; + default: + LogWarning($"参数变化通知无对应读接口: 命令=0x{notify.CommandCode:X2}"); + break; + } + return; + } + // 优先回写本地状态仓库并按字段广播 OnDeviceStateChanged,便于业务侧按字段做局部刷新 ApplyParameterChangeToDeviceState(notify); diff --git a/Assets/Scripts/UI/Pages/HomePage/HomePageCtrl.cs b/Assets/Scripts/UI/Pages/HomePage/HomePageCtrl.cs index 2837542..f7cf5aa 100644 --- a/Assets/Scripts/UI/Pages/HomePage/HomePageCtrl.cs +++ b/Assets/Scripts/UI/Pages/HomePage/HomePageCtrl.cs @@ -12,7 +12,7 @@ using Kill.Bluetooth; using Kill.Bluetooth.Protocol; using Kill.Core; using Unity.VisualScripting; - +using Kill.Managers.ResponseModels; namespace Kill.UI.Pages { public class HomePageCtrl : MonoBehaviour @@ -66,6 +66,9 @@ namespace Kill.UI.Pages // 串行查询相关 private Queue statusQueryQueue = new Queue(); // 状态查询队列 private bool isQueryingStatus = false; // 是否正在查询状态 + private HashSet _pendingBluetoothPullFields; // 串行查询期间排队的蓝牙补拉:变化的配置字段 + private bool _pendingBluetoothPullTasks; // 串行查询期间排队的蓝牙补拉:定时任务是否变化 + private DeviceConfig _lastCloudConfigSnapshot; // 轮询专用云端配置快照(与 DataManager.deviceConfig 解耦,避免BLE回写污染导致变化误判) // 工作模式设置后查询相关 private bool isWaitingForWorkModeQuery = false; // 是否正在等待工作模式查询响应 @@ -352,6 +355,15 @@ namespace Kill.UI.Pages StopCoroutine(deviceStatusQueryCoroutine); deviceStatusQueryCoroutine = null; } + + // 串行查询协程在此被终止(正常完成/超时),必须复位查询标志并处理排队中的蓝牙状态补拉, + // 否则 isQueryingStatus 会一直卡在 true,后续蓝牙状态拉取请求全部被吞 + if (isQueryingStatus) + { + isQueryingStatus = false; + TryFlushPendingBluetoothPull(); + } + bluetoothDeviceInfoCoroutine=StartCoroutine(CheckDeviceError()); Debug.Log("[HomePageCtrl] 设备状态初始化完成"); } @@ -380,6 +392,8 @@ namespace Kill.UI.Pages DataManager.Instance.hasBluetooth = false; isBluetoothUserLoggedIn = false; isQueryingStatus = false; + _pendingBluetoothPullFields = null; + _pendingBluetoothPullTasks = false; statusQueryQueue?.Clear(); if (deviceStatusQueryCoroutine != null) { @@ -474,10 +488,103 @@ namespace Kill.UI.Pages isQueryingStatus = false; Debug.Log("[HomePageCtrl] 串行查询设备状态完成"); + + // 串行查询期间触发的蓝牙状态拉取请求,结束后按变化字段补拉 + TryFlushPendingBluetoothPull(); + if(!hasWifi) OnGetAllMosquitoDataClick(); } + /// + /// 通过蓝牙按变化字段精准拉取最新设备状态(命令队列串行发送,结果经 OnDeviceStateChanged 按字段刷新UI) + /// + /// 变化的 DeviceConfig 字段名集合;为空表示无配置字段变化 + /// 定时任务是否变化 + private void PullLatestStatusViaBluetooth(HashSet changedFields = null, bool scheduleTasksChanged = false) + { + var ble = BLECommunicationManager.Instance; + if (ble == null) return; + + // 解析变化字段对应的蓝牙查询命令(去重),其余字段(wifi/rgb/lcd/video/sound等)主页不展示且无蓝牙对应查询,跳过 + var commands = new HashSet(); + if (changedFields != null) + { + foreach (var field in changedFields) + { + switch (field) + { + case "work_mode": commands.Add("work_mode"); break; + case "language": commands.Add("language"); break; + case "is_locked": commands.Add("lock"); break; + case "fov_angle": commands.Add("angle"); break; + case "detection_distance": + case "aim_distance": commands.Add("distance"); break; + case "fill_light_enable": + case "fill_light_type": + case "fill_light_intensity": commands.Add("fill_light"); break; + case "visual_detect_enable": + case "visual_sensitivity": commands.Add("visual"); break; + case "radar_enable": + case "radar_sensitivity": + case "radar_safe_distance": commands.Add("radar"); break; + } + } + } + if (scheduleTasksChanged) commands.Add("schedule"); + + if (commands.Count == 0) + { + Debug.LogWarning("[HomePageCtrl] 蓝牙状态拉取无需要查询的变化字段,跳过"); + return; + } + + Debug.Log($"[HomePageCtrl] 蓝牙已连接,按变化字段拉取设备状态: {string.Join(",", commands)}"); + + if (commands.Contains("work_mode")) ble.ReadWorkMode(); + if (commands.Contains("schedule")) ble.ReadScheduleTasks(); + if (commands.Contains("language")) ble.ReadLanguageSetting(); + if (commands.Contains("lock")) ble.ReadDeviceLockControl(); + if (commands.Contains("angle")) ble.ReadAngleControl(); + if (commands.Contains("distance")) ble.ReadDistanceControl(); + if (commands.Contains("fill_light")) ble.ReadFillLightControl(); + if (commands.Contains("visual")) ble.ReadVisualDetectionSetting(); + if (commands.Contains("radar")) ble.ReadMillimeterWaveSetting(); + } + + /// + /// 串行查询进行中,把本次蓝牙拉取请求排队(合并变化字段),查询结束后补拉 + /// + private void QueueBluetoothPull(HashSet changedFields, bool scheduleTasksChanged) + { + if (changedFields != null && changedFields.Count > 0) + { + if (_pendingBluetoothPullFields == null) + _pendingBluetoothPullFields = new HashSet(); + foreach (var f in changedFields) + _pendingBluetoothPullFields.Add(f); + } + if (scheduleTasksChanged) _pendingBluetoothPullTasks = true; + Debug.Log("[HomePageCtrl] 串行查询进行中,蓝牙状态拉取排队待补拉"); + } + + /// + /// 处理排队的蓝牙状态补拉(按变化字段精准查询),并清空排队记录 + /// + private void TryFlushPendingBluetoothPull() + { + bool hasPending = (_pendingBluetoothPullFields != null && _pendingBluetoothPullFields.Count > 0) || _pendingBluetoothPullTasks; + if (!hasPending) return; + + var fields = _pendingBluetoothPullFields; + bool tasksChanged = _pendingBluetoothPullTasks; + _pendingBluetoothPullFields = null; + _pendingBluetoothPullTasks = false; + + if (hasBluetooth) + PullLatestStatusViaBluetooth(fields, tasksChanged); + } + /// /// 查询所有设备状态(旧方法,已废弃) /// @@ -2098,20 +2205,59 @@ namespace Kill.UI.Pages }); } - // 在线状态处理后拉取一次设备配置,与本地快照比对,有变化则刷新主页UI - DeviceConfig configBefore = DataManager.Instance.deviceConfig?.Clone(); + // 在线状态处理后拉取一次设备配置,与云端快照比对,有变化则刷新主页UI + // 快照用独立的 _lastCloudConfigSnapshot,不能用 DataManager.deviceConfig: + // BLE 拉取结果会回写 deviceConfig(如工作模式/锁定状态),若快照被污染, + // 会造成"云端值 vs BLE回写值"每轮都判为变化 → 无限循环触发蓝牙拉取 + DeviceConfig configBefore = _lastCloudConfigSnapshot; + bool configChanged = false; await DataManager.Instance.GetDeviceConfig(selectedDevice.ble_mac); if (stopPolling) break; - var changedFields = DataManager.Instance.deviceConfig?.GetChangedFields(configBefore); + // 首轮(无快照)只建立基线不触发,避免初始化时全字段误判变化 + var changedFields = configBefore != null + ? DataManager.Instance.deviceConfig?.GetChangedFields(configBefore) + : null; if (changedFields != null && changedFields.Count > 0) { + configChanged = true; Debug.Log($"[HomePageCtrl] 检测到设备配置变化: {string.Join(",", changedFields.Keys)},刷新主页UI"); + } + // 无论是否变化都更新云端快照(Clone 隔离,防止后续 BLE 回写污染) + _lastCloudConfigSnapshot = DataManager.Instance.deviceConfig?.Clone(); + + // 拉取定时任务与本地快照比对 + var tasksBefore = DataManager.Instance.scheduleTaskDatas != null + ? new List(DataManager.Instance.scheduleTaskDatas) + : new List(); + await DataManager.Instance.GetScheduleTasks(selectedDevice.ble_mac); + if (stopPolling) break; + bool tasksChanged = !IsSameScheduleTasks(tasksBefore, DataManager.Instance.scheduleTaskDatas); + if (tasksChanged) + { + Debug.Log("[HomePageCtrl] 检测到定时任务变化,刷新主页定时任务UI"); + } + + if (configChanged || tasksChanged) + { + var changedKeys = configChanged ? new HashSet(changedFields.Keys) : null; pendingUIUpdates.Add(() => { // await 期间蓝牙可能已连接,避免用 WiFi 数据覆盖 BLE 数据 - if (hasBluetooth) return; - InitDeviceUIFromConfig(); + if (hasBluetooth) + { + // 蓝牙已连接:按变化字段通过蓝牙精准拉取(结果经 OnDeviceStateChanged 按字段刷新UI) + if (isQueryingStatus) + { + // 串行全量查询进行中,结束后按变化字段补拉,避免本次触发被吞 + QueueBluetoothPull(changedKeys, tasksChanged); + return; + } + PullLatestStatusViaBluetooth(changedKeys, tasksChanged); + return; + } + if (configChanged) InitDeviceUIFromConfig(); + if (tasksChanged) ApplyScheduleTasksToHomeUI(); }); } } @@ -2195,6 +2341,14 @@ namespace Kill.UI.Pages private async Task RefreshHomeScheduleFromWifi() { await DataManager.Instance.GetScheduleTasks(selectedDevice.ble_mac); + ApplyScheduleTasksToHomeUI(); + } + + /// + /// 把 DataManager.scheduleTaskDatas 转换并刷新主页定时任务UI + /// + private void ApplyScheduleTasksToHomeUI() + { var tasks = new List(); foreach (var d in DataManager.Instance.scheduleTaskDatas) { @@ -2223,6 +2377,29 @@ namespace Kill.UI.Pages }); } + /// + /// 比较两次定时任务列表是否一致(逐项比较关键字段) + /// + private static bool IsSameScheduleTasks(List before, List after) + { + int beforeCount = before?.Count ?? 0; + int afterCount = after?.Count ?? 0; + if (beforeCount != afterCount) return false; + for (int i = 0; i < beforeCount; i++) + { + var a = before[i]; + var b = after[i]; + if (a.task_id != b.task_id || a.enabled != b.enabled || + a.start_hour != b.start_hour || a.start_minute != b.start_minute || + a.end_hour != b.end_hour || a.end_minute != b.end_minute || + a.mode != b.mode || a.repeat_mask != b.repeat_mask) + { + return false; + } + } + return true; + } + /// /// 通过WiFi发送BLE指令到设备(使用共通错误处理) ///