feat(bluetooth,ui): 添加毫米波雷达状态支持并优化设备状态处理
1. 新增MillimeterWaveSetting设备状态字段与更新方法 2. 修复HomePageCtrl中设备配置空引用风险 3. 重构BLE通讯状态更新逻辑,统一MAC地址获取 4. 补充各类BLE通知的数据长度校验 5. 新增设备锁状态参数变更通知处理 6. 优化日志描述与协程管理逻辑
This commit is contained in:
parent
8e879b009a
commit
320ece473f
@ -128,7 +128,7 @@ namespace Kill.Bluetooth
|
||||
/// <summary>
|
||||
/// 更新当前设备状态,并仅在值发生变化时广播状态事件。
|
||||
/// </summary>
|
||||
private bool UpdateDeviceState(string deviceMac, BLEDeviceStateField field, Action<BLEDeviceState> update)
|
||||
private bool UpdateDeviceState(string deviceMac, BLEDeviceStateField field, Func<BLEDeviceState, bool> update)
|
||||
{
|
||||
if (update == null)
|
||||
{
|
||||
@ -162,10 +162,9 @@ namespace Kill.Bluetooth
|
||||
return changed;
|
||||
}
|
||||
|
||||
private bool UpdateStateFromResponse(BLEResponse response, BLEDeviceStateField field, Action<BLEDeviceState> update)
|
||||
private bool UpdateStateFromResponse(BLEResponse response, BLEDeviceStateField field, Func<BLEDeviceState, bool> 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
|
||||
/// </summary>
|
||||
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));
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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<Action> statusQueryQueue = new Queue<Action>(); // 状态查询队列
|
||||
@ -286,6 +287,12 @@ namespace Kill.UI.Pages
|
||||
/// </summary>
|
||||
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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -493,9 +510,13 @@ namespace Kill.UI.Pages
|
||||
/// <summary>
|
||||
/// 按 MAC 隔离:当前设备不匹配(已切换/未连接/选中设备不同)则丢弃通知
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user