fix(ble&homepage): 修复蓝牙参数变化通知与主页状态同步问题
1. 调整BLE参数变化通知的数据长度校验,支持无载荷的通知场景 2. 当参数变化通知无载荷时,主动触发对应配置项的查询 3. 新增蓝牙状态补拉排队机制,解决串行查询期间请求被吞的问题 4. 优化主页配置轮询逻辑,使用独立快照避免BLE回写污染导致的循环查询 5. 新增定时任务变化检测与精准拉取逻辑
This commit is contained in:
parent
5478355435
commit
1710194d2f
@ -2803,7 +2803,8 @@ namespace Kill.Bluetooth
|
|||||||
|
|
||||||
private void HandleParameterChangeNotification(byte[] data)
|
private void HandleParameterChangeNotification(byte[] data)
|
||||||
{
|
{
|
||||||
if (data == null || data.Length < 3)
|
// 最少2字节:通知类型 + 命令码(载荷允许为空,如固件上报 06-A0 不带新值)
|
||||||
|
if (data == null || data.Length < 2)
|
||||||
{
|
{
|
||||||
LogWarning("参数变化通知数据长度不足");
|
LogWarning("参数变化通知数据长度不足");
|
||||||
return;
|
return;
|
||||||
@ -2812,6 +2813,48 @@ namespace Kill.Bluetooth
|
|||||||
var notify = ParameterChangeNotification.FromBytes(data);
|
var notify = ParameterChangeNotification.FromBytes(data);
|
||||||
Log($"参数变化通知: 命令=0x{notify.CommandCode:X2}, 数据长度={(notify.Payload?.Length ?? 0)}");
|
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,便于业务侧按字段做局部刷新
|
// 优先回写本地状态仓库并按字段广播 OnDeviceStateChanged,便于业务侧按字段做局部刷新
|
||||||
ApplyParameterChangeToDeviceState(notify);
|
ApplyParameterChangeToDeviceState(notify);
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ using Kill.Bluetooth;
|
|||||||
using Kill.Bluetooth.Protocol;
|
using Kill.Bluetooth.Protocol;
|
||||||
using Kill.Core;
|
using Kill.Core;
|
||||||
using Unity.VisualScripting;
|
using Unity.VisualScripting;
|
||||||
|
using Kill.Managers.ResponseModels;
|
||||||
namespace Kill.UI.Pages
|
namespace Kill.UI.Pages
|
||||||
{
|
{
|
||||||
public class HomePageCtrl : MonoBehaviour
|
public class HomePageCtrl : MonoBehaviour
|
||||||
@ -66,6 +66,9 @@ namespace Kill.UI.Pages
|
|||||||
// 串行查询相关
|
// 串行查询相关
|
||||||
private Queue<Action> statusQueryQueue = new Queue<Action>(); // 状态查询队列
|
private Queue<Action> statusQueryQueue = new Queue<Action>(); // 状态查询队列
|
||||||
private bool isQueryingStatus = false; // 是否正在查询状态
|
private bool isQueryingStatus = false; // 是否正在查询状态
|
||||||
|
private HashSet<string> _pendingBluetoothPullFields; // 串行查询期间排队的蓝牙补拉:变化的配置字段
|
||||||
|
private bool _pendingBluetoothPullTasks; // 串行查询期间排队的蓝牙补拉:定时任务是否变化
|
||||||
|
private DeviceConfig _lastCloudConfigSnapshot; // 轮询专用云端配置快照(与 DataManager.deviceConfig 解耦,避免BLE回写污染导致变化误判)
|
||||||
|
|
||||||
// 工作模式设置后查询相关
|
// 工作模式设置后查询相关
|
||||||
private bool isWaitingForWorkModeQuery = false; // 是否正在等待工作模式查询响应
|
private bool isWaitingForWorkModeQuery = false; // 是否正在等待工作模式查询响应
|
||||||
@ -352,6 +355,15 @@ namespace Kill.UI.Pages
|
|||||||
StopCoroutine(deviceStatusQueryCoroutine);
|
StopCoroutine(deviceStatusQueryCoroutine);
|
||||||
deviceStatusQueryCoroutine = null;
|
deviceStatusQueryCoroutine = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 串行查询协程在此被终止(正常完成/超时),必须复位查询标志并处理排队中的蓝牙状态补拉,
|
||||||
|
// 否则 isQueryingStatus 会一直卡在 true,后续蓝牙状态拉取请求全部被吞
|
||||||
|
if (isQueryingStatus)
|
||||||
|
{
|
||||||
|
isQueryingStatus = false;
|
||||||
|
TryFlushPendingBluetoothPull();
|
||||||
|
}
|
||||||
|
|
||||||
bluetoothDeviceInfoCoroutine=StartCoroutine(CheckDeviceError());
|
bluetoothDeviceInfoCoroutine=StartCoroutine(CheckDeviceError());
|
||||||
Debug.Log("[HomePageCtrl] 设备状态初始化完成");
|
Debug.Log("[HomePageCtrl] 设备状态初始化完成");
|
||||||
}
|
}
|
||||||
@ -380,6 +392,8 @@ namespace Kill.UI.Pages
|
|||||||
DataManager.Instance.hasBluetooth = false;
|
DataManager.Instance.hasBluetooth = false;
|
||||||
isBluetoothUserLoggedIn = false;
|
isBluetoothUserLoggedIn = false;
|
||||||
isQueryingStatus = false;
|
isQueryingStatus = false;
|
||||||
|
_pendingBluetoothPullFields = null;
|
||||||
|
_pendingBluetoothPullTasks = false;
|
||||||
statusQueryQueue?.Clear();
|
statusQueryQueue?.Clear();
|
||||||
if (deviceStatusQueryCoroutine != null)
|
if (deviceStatusQueryCoroutine != null)
|
||||||
{
|
{
|
||||||
@ -474,10 +488,103 @@ namespace Kill.UI.Pages
|
|||||||
|
|
||||||
isQueryingStatus = false;
|
isQueryingStatus = false;
|
||||||
Debug.Log("[HomePageCtrl] 串行查询设备状态完成");
|
Debug.Log("[HomePageCtrl] 串行查询设备状态完成");
|
||||||
|
|
||||||
|
// 串行查询期间触发的蓝牙状态拉取请求,结束后按变化字段补拉
|
||||||
|
TryFlushPendingBluetoothPull();
|
||||||
|
|
||||||
if(!hasWifi)
|
if(!hasWifi)
|
||||||
OnGetAllMosquitoDataClick();
|
OnGetAllMosquitoDataClick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通过蓝牙按变化字段精准拉取最新设备状态(命令队列串行发送,结果经 OnDeviceStateChanged 按字段刷新UI)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="changedFields">变化的 DeviceConfig 字段名集合;为空表示无配置字段变化</param>
|
||||||
|
/// <param name="scheduleTasksChanged">定时任务是否变化</param>
|
||||||
|
private void PullLatestStatusViaBluetooth(HashSet<string> changedFields = null, bool scheduleTasksChanged = false)
|
||||||
|
{
|
||||||
|
var ble = BLECommunicationManager.Instance;
|
||||||
|
if (ble == null) return;
|
||||||
|
|
||||||
|
// 解析变化字段对应的蓝牙查询命令(去重),其余字段(wifi/rgb/lcd/video/sound等)主页不展示且无蓝牙对应查询,跳过
|
||||||
|
var commands = new HashSet<string>();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 串行查询进行中,把本次蓝牙拉取请求排队(合并变化字段),查询结束后补拉
|
||||||
|
/// </summary>
|
||||||
|
private void QueueBluetoothPull(HashSet<string> changedFields, bool scheduleTasksChanged)
|
||||||
|
{
|
||||||
|
if (changedFields != null && changedFields.Count > 0)
|
||||||
|
{
|
||||||
|
if (_pendingBluetoothPullFields == null)
|
||||||
|
_pendingBluetoothPullFields = new HashSet<string>();
|
||||||
|
foreach (var f in changedFields)
|
||||||
|
_pendingBluetoothPullFields.Add(f);
|
||||||
|
}
|
||||||
|
if (scheduleTasksChanged) _pendingBluetoothPullTasks = true;
|
||||||
|
Debug.Log("[HomePageCtrl] 串行查询进行中,蓝牙状态拉取排队待补拉");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理排队的蓝牙状态补拉(按变化字段精准查询),并清空排队记录
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查询所有设备状态(旧方法,已废弃)
|
/// 查询所有设备状态(旧方法,已废弃)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -2098,20 +2205,59 @@ namespace Kill.UI.Pages
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在线状态处理后拉取一次设备配置,与本地快照比对,有变化则刷新主页UI
|
// 在线状态处理后拉取一次设备配置,与云端快照比对,有变化则刷新主页UI
|
||||||
DeviceConfig configBefore = DataManager.Instance.deviceConfig?.Clone();
|
// 快照用独立的 _lastCloudConfigSnapshot,不能用 DataManager.deviceConfig:
|
||||||
|
// BLE 拉取结果会回写 deviceConfig(如工作模式/锁定状态),若快照被污染,
|
||||||
|
// 会造成"云端值 vs BLE回写值"每轮都判为变化 → 无限循环触发蓝牙拉取
|
||||||
|
DeviceConfig configBefore = _lastCloudConfigSnapshot;
|
||||||
|
bool configChanged = false;
|
||||||
await DataManager.Instance.GetDeviceConfig(selectedDevice.ble_mac);
|
await DataManager.Instance.GetDeviceConfig(selectedDevice.ble_mac);
|
||||||
if (stopPolling) break;
|
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)
|
if (changedFields != null && changedFields.Count > 0)
|
||||||
{
|
{
|
||||||
|
configChanged = true;
|
||||||
Debug.Log($"[HomePageCtrl] 检测到设备配置变化: {string.Join(",", changedFields.Keys)},刷新主页UI");
|
Debug.Log($"[HomePageCtrl] 检测到设备配置变化: {string.Join(",", changedFields.Keys)},刷新主页UI");
|
||||||
|
}
|
||||||
|
// 无论是否变化都更新云端快照(Clone 隔离,防止后续 BLE 回写污染)
|
||||||
|
_lastCloudConfigSnapshot = DataManager.Instance.deviceConfig?.Clone();
|
||||||
|
|
||||||
|
// 拉取定时任务与本地快照比对
|
||||||
|
var tasksBefore = DataManager.Instance.scheduleTaskDatas != null
|
||||||
|
? new List<ScheduleTaskData>(DataManager.Instance.scheduleTaskDatas)
|
||||||
|
: new List<ScheduleTaskData>();
|
||||||
|
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<string>(changedFields.Keys) : null;
|
||||||
pendingUIUpdates.Add(() =>
|
pendingUIUpdates.Add(() =>
|
||||||
{
|
{
|
||||||
// await 期间蓝牙可能已连接,避免用 WiFi 数据覆盖 BLE 数据
|
// await 期间蓝牙可能已连接,避免用 WiFi 数据覆盖 BLE 数据
|
||||||
if (hasBluetooth) return;
|
if (hasBluetooth)
|
||||||
InitDeviceUIFromConfig();
|
{
|
||||||
|
// 蓝牙已连接:按变化字段通过蓝牙精准拉取(结果经 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()
|
private async Task RefreshHomeScheduleFromWifi()
|
||||||
{
|
{
|
||||||
await DataManager.Instance.GetScheduleTasks(selectedDevice.ble_mac);
|
await DataManager.Instance.GetScheduleTasks(selectedDevice.ble_mac);
|
||||||
|
ApplyScheduleTasksToHomeUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 把 DataManager.scheduleTaskDatas 转换并刷新主页定时任务UI
|
||||||
|
/// </summary>
|
||||||
|
private void ApplyScheduleTasksToHomeUI()
|
||||||
|
{
|
||||||
var tasks = new List<ScheduleTask>();
|
var tasks = new List<ScheduleTask>();
|
||||||
foreach (var d in DataManager.Instance.scheduleTaskDatas)
|
foreach (var d in DataManager.Instance.scheduleTaskDatas)
|
||||||
{
|
{
|
||||||
@ -2223,6 +2377,29 @@ namespace Kill.UI.Pages
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 比较两次定时任务列表是否一致(逐项比较关键字段)
|
||||||
|
/// </summary>
|
||||||
|
private static bool IsSameScheduleTasks(List<ScheduleTaskData> before, List<ScheduleTaskData> 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;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 通过WiFi发送BLE指令到设备(使用共通错误处理)
|
/// 通过WiFi发送BLE指令到设备(使用共通错误处理)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user