暂时提交
This commit is contained in:
parent
2dd4013f6f
commit
8e879b009a
@ -50,7 +50,14 @@ namespace Kill.Bluetooth
|
||||
public event Action<StatisticsData> OnStatisticsDataReceived; // 收到统计数据
|
||||
public event Action<SensorData> OnSensorDataReceived; // 收到传感器数据
|
||||
public event Action<MosquitoData> OnMosquitoDataReceived; // 收到蚊虫数据通知
|
||||
public event Action<DeviceStateChangeNotification> OnDeviceStateChangeNotification; // 设备状态变化通知(0x01)
|
||||
public event Action<ErrorStatusNotification> OnErrorStatusNotification; // 错误状态上报通知(0x02)
|
||||
public event Action<ParameterChangeNotification> OnParameterChangeNotification; // 参数变化通知(0x06)
|
||||
public event Action<CapacitorStateNotification> OnCapacitorStateNotification; // 蓄能状态通知(0x08)
|
||||
public event Action<FillLightConnectionNotification> OnFillLightConnectionNotification; // 补光灯连接状态通知(0x09)
|
||||
public event Action<byte, string> OnDeviceNotificationReceived; // 通用通知(命令码 + 设备MAC)用于按 MAC 隔离广播
|
||||
public event Action<string> OnRawDataReceived; // 收到原始数据(十六进制字符串)
|
||||
public event Action<BLEDeviceState, BLEDeviceStateField> OnDeviceStateChanged; // 本地设备状态变化(设备状态、字段)
|
||||
public event Action<string> OnRawDataSent; // 发送原始数据(十六进制字符串)
|
||||
public event Action<string> OnCommunicationError; // 通信错误
|
||||
|
||||
@ -89,6 +96,83 @@ namespace Kill.Bluetooth
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<string, BLEDeviceState> _deviceStates =
|
||||
new Dictionary<string, BLEDeviceState>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly object _deviceStateLock = new object();
|
||||
private string _lastStateMac;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前连接设备对应的本地状态对象。
|
||||
/// 状态仓库按设备 MAC 隔离,旧设备通知不会覆盖当前设备。
|
||||
/// </summary>
|
||||
public BLEDeviceState GetDeviceState(string deviceMac)
|
||||
{
|
||||
string normalizedMac = NormalizeDeviceMac(deviceMac);
|
||||
if (string.IsNullOrEmpty(normalizedMac))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
lock (_deviceStateLock)
|
||||
{
|
||||
if (!_deviceStates.TryGetValue(normalizedMac, out BLEDeviceState state))
|
||||
{
|
||||
state = new BLEDeviceState(normalizedMac);
|
||||
_deviceStates[normalizedMac] = state;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新当前设备状态,并仅在值发生变化时广播状态事件。
|
||||
/// </summary>
|
||||
private bool UpdateDeviceState(string deviceMac, BLEDeviceStateField field, Action<BLEDeviceState> update)
|
||||
{
|
||||
if (update == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BLEDeviceState state = GetDeviceState(deviceMac);
|
||||
if (state == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool changed;
|
||||
lock (_deviceStateLock)
|
||||
{
|
||||
changed = update(state);
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
try
|
||||
{
|
||||
OnDeviceStateChanged?.Invoke(state, field);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
LogError($"OnDeviceStateChanged 处理异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private bool UpdateStateFromResponse(BLEResponse response, BLEDeviceStateField field, Action<BLEDeviceState> update)
|
||||
{
|
||||
return response != null && response.IsSuccess &&
|
||||
UpdateDeviceState(GetCurrentDeviceMac(), field, update);
|
||||
}
|
||||
|
||||
private static string NormalizeDeviceMac(string deviceMac)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(deviceMac) ? string.Empty : deviceMac.Trim().ToUpperInvariant();
|
||||
}
|
||||
|
||||
// 服务和特征UUID (根据实际设备配置)
|
||||
public string ServiceUUID = "0000ffe0-0000-1000-8000-00805f9b34fb";
|
||||
public string WriteCharacteristicUUID = "0000ffe1-0000-1000-8000-00805f9b34fb";
|
||||
@ -190,6 +274,16 @@ namespace Kill.Bluetooth
|
||||
/// </summary>
|
||||
private void OnBluetoothConnected()
|
||||
{
|
||||
string stateMac = GetCurrentDeviceMac();
|
||||
if (!string.IsNullOrEmpty(stateMac))
|
||||
{
|
||||
_lastStateMac = stateMac;
|
||||
UpdateDeviceState(
|
||||
stateMac,
|
||||
BLEDeviceStateField.Connection,
|
||||
state => state.SetConnection(true));
|
||||
}
|
||||
|
||||
if (AutoInitialize)
|
||||
InitializeCommunication();
|
||||
}
|
||||
@ -200,6 +294,19 @@ namespace Kill.Bluetooth
|
||||
private void OnBluetoothDisconnected(string address)
|
||||
{
|
||||
Log($"蓝牙已断开: {address}");
|
||||
|
||||
// 使用断开参数作为设备状态键;部分平台会在清空地址前触发事件。
|
||||
string stateMac = NormalizeDeviceMac(address);
|
||||
if (string.IsNullOrEmpty(stateMac))
|
||||
{
|
||||
stateMac = string.IsNullOrEmpty(_lastStateMac) ? GetCurrentDeviceMac() : _lastStateMac;
|
||||
}
|
||||
_lastStateMac = string.IsNullOrEmpty(stateMac) ? _lastStateMac : stateMac;
|
||||
UpdateDeviceState(
|
||||
stateMac,
|
||||
BLEDeviceStateField.Connection,
|
||||
state => state.SetConnection(false));
|
||||
|
||||
// 清理状态
|
||||
IsWaitingResponse = false;
|
||||
_responseTimer = 0;
|
||||
@ -562,6 +669,15 @@ namespace Kill.Bluetooth
|
||||
SendFrame(frame, (response) =>
|
||||
{
|
||||
var setting = LanguageSetting.FromBytes(response.Data);
|
||||
if (!response.IsSuccess)
|
||||
{
|
||||
LogError($"读取语言设置失败: 状态码={response.Status:X2}");
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.Language,
|
||||
state => state.SetLanguage(setting));
|
||||
}
|
||||
callback?.Invoke(setting);
|
||||
OnLanguageSettingReceived?.Invoke(setting);
|
||||
});
|
||||
@ -592,9 +708,15 @@ namespace Kill.Bluetooth
|
||||
{
|
||||
bool success = response.IsSuccess;
|
||||
if (success)
|
||||
{
|
||||
Log($"语言设置成功: {(isChinese ? "中文" : "英文")}");
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.Language,
|
||||
state => state.SetLanguage(setting));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError($"语言设置失败, 状态码={response.Status:X2}");
|
||||
}
|
||||
callback?.Invoke(success);
|
||||
});
|
||||
}
|
||||
@ -674,6 +796,15 @@ namespace Kill.Bluetooth
|
||||
SendFrame(frame, (response) =>
|
||||
{
|
||||
var taskList = ScheduleTaskListResponse.FromBytes(response.Status, response.Data);
|
||||
if (!response.IsSuccess)
|
||||
{
|
||||
LogError($"读取定时任务失败: 状态码={response.Status:X2}");
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.ScheduleTasks,
|
||||
state => state.SetScheduleTasks(taskList.Tasks));
|
||||
}
|
||||
callback?.Invoke(taskList);
|
||||
OnScheduleTaskListReceived?.Invoke(taskList);
|
||||
});
|
||||
@ -1116,6 +1247,8 @@ namespace Kill.Bluetooth
|
||||
SendFrame(frame, (response) =>
|
||||
{
|
||||
var control = FillLightControl.FromBytes(response.Data);
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.FillLight,
|
||||
state => state.SetFillLight(control));
|
||||
callback?.Invoke(control);
|
||||
OnFillLightControlReceived?.Invoke(control);
|
||||
});
|
||||
@ -1145,9 +1278,15 @@ namespace Kill.Bluetooth
|
||||
{
|
||||
bool success = response.IsSuccess;
|
||||
if (success)
|
||||
{
|
||||
Log($"补光灯控制设置成功: 开关={(control.Enable ? "开启" : "关闭")}, 类型={control.GetLightTypeString()}, 强度={control.GetIntensityString()}");
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.FillLight,
|
||||
state => state.SetFillLight(control));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError($"补光灯控制设置失败, 状态码={response.Status:X2}");
|
||||
}
|
||||
callback?.Invoke(success);
|
||||
});
|
||||
}
|
||||
@ -1172,6 +1311,8 @@ namespace Kill.Bluetooth
|
||||
SendFrame(frame, (response) =>
|
||||
{
|
||||
var status = FillLightConnectionStatus.FromBytes(response.Data);
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.FillLightConnection,
|
||||
state => state.SetFillLightConnection(status));
|
||||
callback?.Invoke(status);
|
||||
OnFillLightConnectionStatusReceived?.Invoke(status);
|
||||
});
|
||||
@ -1254,6 +1395,8 @@ namespace Kill.Bluetooth
|
||||
Debug.Log($"[ReadDeviceLockControl] 收到响应: Status={response.Status:X2}, Data={BitConverter.ToString(response.Data)}");
|
||||
var control = DeviceLockControl.FromBytes(response.Data);
|
||||
Debug.Log($"[ReadDeviceLockControl] 解析结果: IsLocked={control.IsLocked}");
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.LockState,
|
||||
state => state.SetLockState(control));
|
||||
callback?.Invoke(control);
|
||||
OnDeviceLockControlReceived?.Invoke(control);
|
||||
});
|
||||
@ -1287,9 +1430,15 @@ namespace Kill.Bluetooth
|
||||
Debug.Log($"[WriteDeviceLockControl] 收到响应: Status={response.Status:X2}");
|
||||
bool success = response.IsSuccess;
|
||||
if (success)
|
||||
{
|
||||
Log($"设备{(isLocked ? "锁定" : "解锁")}成功");
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.LockState,
|
||||
state => state.SetLockState(control));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError($"设备{(isLocked ? "锁定" : "解锁")}失败, 状态码={response.Status:X2}");
|
||||
}
|
||||
callback?.Invoke(success);
|
||||
});
|
||||
}
|
||||
@ -1318,6 +1467,8 @@ namespace Kill.Bluetooth
|
||||
SendFrame(frame, (response) =>
|
||||
{
|
||||
var control = AngleControl.FromBytes(response.Data);
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.Angle,
|
||||
state => state.SetAngle(control));
|
||||
callback?.Invoke(control);
|
||||
OnAngleControlReceived?.Invoke(control);
|
||||
Log($"读取角度控制: {control}");
|
||||
@ -1348,9 +1499,15 @@ namespace Kill.Bluetooth
|
||||
{
|
||||
bool success = response.IsSuccess;
|
||||
if (success)
|
||||
{
|
||||
Log($"角度控制设置成功: {control}");
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.Angle,
|
||||
state => state.SetAngle(control));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError($"角度控制设置失败, 状态码={response.Status:X2}");
|
||||
}
|
||||
callback?.Invoke(success);
|
||||
});
|
||||
}
|
||||
@ -1375,6 +1532,8 @@ namespace Kill.Bluetooth
|
||||
SendFrame(frame, (response) =>
|
||||
{
|
||||
var control = DistanceControl.FromBytes(response.Data);
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.Distance,
|
||||
state => state.SetDistance(control));
|
||||
callback?.Invoke(control);
|
||||
OnDistanceControlReceived?.Invoke(control);
|
||||
Log($"读取距离控制: {control}");
|
||||
@ -1405,9 +1564,15 @@ namespace Kill.Bluetooth
|
||||
{
|
||||
bool success = response.IsSuccess;
|
||||
if (success)
|
||||
{
|
||||
Log($"距离控制设置成功: {control}");
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.Distance,
|
||||
state => state.SetDistance(control));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError($"距离控制设置失败, 状态码={response.Status:X2}");
|
||||
}
|
||||
callback?.Invoke(success);
|
||||
});
|
||||
}
|
||||
@ -1492,6 +1657,8 @@ namespace Kill.Bluetooth
|
||||
SendFrame(frame, (response) =>
|
||||
{
|
||||
var setting = VisualDetectionSetting.FromBytes(response.Data);
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.VisualDetection,
|
||||
state => state.SetVisualDetection(setting));
|
||||
callback?.Invoke(setting);
|
||||
OnVisualDetectionSettingReceived?.Invoke(setting);
|
||||
Log($"读取视觉检测设置: {setting}");
|
||||
@ -1521,9 +1688,15 @@ namespace Kill.Bluetooth
|
||||
{
|
||||
bool success = response.IsSuccess;
|
||||
if (success)
|
||||
{
|
||||
Log($"视觉检测设置成功: {setting}");
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.VisualDetection,
|
||||
state => state.SetVisualDetection(setting));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError($"视觉检测设置失败, 状态码={response.Status:X2}");
|
||||
}
|
||||
callback?.Invoke(success);
|
||||
});
|
||||
}
|
||||
@ -1690,6 +1863,15 @@ namespace Kill.Bluetooth
|
||||
SendFrame(frame, (response) =>
|
||||
{
|
||||
var setting = WorkModeSetting.FromBytes(response.Data);
|
||||
if (!response.IsSuccess)
|
||||
{
|
||||
LogError($"读取工作模式失败: 状态码={response.Status:X2}");
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.WorkMode,
|
||||
state => state.SetWorkMode(setting.Mode));
|
||||
}
|
||||
callback?.Invoke(setting);
|
||||
OnWorkModeSettingReceived?.Invoke(setting);
|
||||
Log($"读取工作模式: {setting}");
|
||||
@ -1719,9 +1901,15 @@ namespace Kill.Bluetooth
|
||||
{
|
||||
bool success = response.IsSuccess;
|
||||
if (success)
|
||||
{
|
||||
Log($"工作模式设置成功: {setting}");
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.WorkMode,
|
||||
state => state.SetWorkMode(setting.Mode));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError($"工作模式设置失败, 状态码={response.Status:X2}");
|
||||
}
|
||||
callback?.Invoke(success);
|
||||
});
|
||||
}
|
||||
@ -1746,6 +1934,8 @@ namespace Kill.Bluetooth
|
||||
SendFrame(frame, (response) =>
|
||||
{
|
||||
var status = HardwareStatus.FromBytes(response.Data);
|
||||
UpdateStateFromResponse(response, BLEDeviceStateField.HardwareStatus,
|
||||
state => state.SetHardwareStatus(status));
|
||||
callback?.Invoke(status);
|
||||
OnHardwareStatusReceived?.Invoke(status);
|
||||
Log($"查询硬件状态: {status}");
|
||||
@ -2420,14 +2610,17 @@ namespace Kill.Bluetooth
|
||||
{
|
||||
bool wasWaitingResponse = IsWaitingResponse;
|
||||
|
||||
// 优先处理设备主动通知,避免被等待响应的流程拦截
|
||||
if (frame.ReadWrite == BLEConstants.RW_NOTIFY)
|
||||
// 通知帧优先处理,并且绝对不影响命令队列(IsWaitingResponse / _pendingCallback / _responseTimer)
|
||||
// 设备主动通知是异步旁路通道,与当前等待响应的命令是两条独立链路
|
||||
if (frame.ReadWrite == BLEConstants.RW_NOTIFY || frame.Command == BLEConstants.NOTIFY_COMMAND_CODE)
|
||||
{
|
||||
HandleNotification(frame.Data);
|
||||
return;
|
||||
}
|
||||
else if (IsWaitingResponse && frame.Command == LastCommand)
|
||||
|
||||
// 响应帧:优先匹配当前等待的命令。
|
||||
if (IsWaitingResponse && frame.Command == LastCommand)
|
||||
{
|
||||
// 匹配到等待的响应
|
||||
IsWaitingResponse = false;
|
||||
_responseTimer = 0;
|
||||
|
||||
@ -2437,7 +2630,7 @@ namespace Kill.Bluetooth
|
||||
}
|
||||
else if (IsWaitingResponse)
|
||||
{
|
||||
// 尝试处理非匹配的响应,可能是硬件响应延迟
|
||||
// 保留原兼容行为:设备响应延迟时仍允许结束当前等待任务。
|
||||
IsWaitingResponse = false;
|
||||
_responseTimer = 0;
|
||||
|
||||
@ -2446,7 +2639,6 @@ namespace Kill.Bluetooth
|
||||
_pendingCallback = null;
|
||||
}
|
||||
|
||||
// 如果之前有等待响应,现在响应已处理,继续处理队列
|
||||
if (wasWaitingResponse)
|
||||
{
|
||||
ProcessCommandQueue();
|
||||
@ -2454,19 +2646,243 @@ namespace Kill.Bluetooth
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通用的设备通知分发器,按命令类型路由到对应处理逻辑
|
||||
/// 通用的设备通知分发器,按通知类型路由到对应处理逻辑
|
||||
/// 通知帧约定:data[0]=通知类型,data[1..]=通知数据
|
||||
/// </summary>
|
||||
private void HandleNotification(byte[] data)
|
||||
{
|
||||
byte command=data[0];
|
||||
switch (command)
|
||||
if (data == null || data.Length == 0)
|
||||
{
|
||||
LogWarning("收到空通知数据");
|
||||
return;
|
||||
}
|
||||
|
||||
byte notifyType = data[0];
|
||||
|
||||
// 按 MAC 隔离:仅当通知来自当前已连接设备时才向下分发
|
||||
string currentMac = GetCurrentDeviceMac();
|
||||
if (!string.IsNullOrEmpty(currentMac))
|
||||
{
|
||||
// 触发通用通知事件(携带通知类型 + 当前设备 MAC),便于业务侧按 MAC 二次过滤
|
||||
try
|
||||
{
|
||||
OnDeviceNotificationReceived?.Invoke(notifyType, currentMac);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
LogError($"OnDeviceNotificationReceived 处理异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
switch (notifyType)
|
||||
{
|
||||
case BLEConstants.NOTIFY_MOSQUITO_DATA:
|
||||
HandleMosquitoDataNotification(data);
|
||||
break;
|
||||
default:
|
||||
Log($"未处理的设备通知 0x{command:X2}");
|
||||
case BLEConstants.NOTIFY_DEVICE_STATE_CHANGE:
|
||||
HandleDeviceStateChangeNotification(data);
|
||||
break;
|
||||
case BLEConstants.NOTIFY_ERROR_STATUS:
|
||||
HandleErrorStatusNotification(data);
|
||||
break;
|
||||
case BLEConstants.NOTIFY_PARAMETER_CHANGE:
|
||||
HandleParameterChangeNotification(data);
|
||||
break;
|
||||
case BLEConstants.NOTIFY_CAPACITOR_STATE:
|
||||
HandleCapacitorStateNotification(data);
|
||||
break;
|
||||
case BLEConstants.NOTIFY_FILL_LIGHT_CONNECTION:
|
||||
HandleFillLightConnectionNotification(data);
|
||||
break;
|
||||
case BLEConstants.NOTIFY_STATISTICS_UPDATE:
|
||||
case BLEConstants.NOTIFY_SECURITY_ALERT:
|
||||
Log($"收到通知类型 0x{notifyType:X2}(暂未实现具体业务解析)");
|
||||
break;
|
||||
default:
|
||||
Log($"未处理的设备通知类型 0x{notifyType:X2}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前已连接设备的 MAC(用于按 MAC 隔离通知事件)
|
||||
/// 优先使用 iOS 端从广播中解析出的真实 MAC,否则回退到 BLE 地址
|
||||
/// </summary>
|
||||
public string GetCurrentDeviceMac()
|
||||
{
|
||||
if (BluetoothManager.Instance == null || !BluetoothManager.Instance.IsConnected)
|
||||
return null;
|
||||
string mac = BluetoothManager.Instance.ConnectedDeviceMacAddress;
|
||||
if (string.IsNullOrEmpty(mac))
|
||||
mac = BluetoothManager.Instance.ConnectedDeviceAddress;
|
||||
return string.IsNullOrEmpty(mac) ? null : mac.ToUpperInvariant();
|
||||
}
|
||||
|
||||
private string GetStateMac()
|
||||
{
|
||||
string currentMac = GetCurrentDeviceMac();
|
||||
return string.IsNullOrEmpty(currentMac) && !string.IsNullOrEmpty(_lastStateMac)
|
||||
? _lastStateMac
|
||||
: currentMac;
|
||||
}
|
||||
|
||||
private void HandleDeviceStateChangeNotification(byte[] data)
|
||||
{
|
||||
var notify = DeviceStateChangeNotification.FromBytes(data);
|
||||
if (notify.WorkMode > (byte)WorkMode.Eliminate)
|
||||
{
|
||||
LogWarning($"设备状态变化通知包含无效工作模式: 0x{notify.WorkMode:X2}");
|
||||
return;
|
||||
}
|
||||
|
||||
Log($"设备状态变化通知: 工作模式=0x{notify.WorkMode:X2}");
|
||||
UpdateDeviceState(
|
||||
GetCurrentDeviceMac(),
|
||||
BLEDeviceStateField.WorkMode,
|
||||
state => state.SetWorkMode((WorkMode)notify.WorkMode));
|
||||
try
|
||||
{
|
||||
OnDeviceStateChangeNotification?.Invoke(notify);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
LogError($"OnDeviceStateChangeNotification 处理异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleErrorStatusNotification(byte[] data)
|
||||
{
|
||||
var notify = ErrorStatusNotification.FromBytes(data);
|
||||
if (data != null && data.Length >= 8)
|
||||
{
|
||||
UpdateDeviceState(
|
||||
GetCurrentDeviceMac(),
|
||||
BLEDeviceStateField.HardwareStatus,
|
||||
state => state.SetHardwareStatus(notify.Status));
|
||||
}
|
||||
Log($"错误状态上报通知: {notify.Status}");
|
||||
try
|
||||
{
|
||||
OnErrorStatusNotification?.Invoke(notify);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
LogError($"OnErrorStatusNotification 处理异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleParameterChangeNotification(byte[] data)
|
||||
{
|
||||
var notify = ParameterChangeNotification.FromBytes(data);
|
||||
Log($"参数变化通知: 命令=0x{notify.CommandCode:X2}, 数据长度={(notify.Payload?.Length ?? 0)}");
|
||||
|
||||
// 优先回写本地状态仓库并按字段广播 OnDeviceStateChanged,便于业务侧按字段做局部刷新
|
||||
ApplyParameterChangeToDeviceState(notify);
|
||||
|
||||
try
|
||||
{
|
||||
OnParameterChangeNotification?.Invoke(notify);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
LogError($"OnParameterChangeNotification 处理异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 把 0x06 参数变化通知解析后回写到 BLEDeviceState 仓库,仅在值变化时通过 OnDeviceStateChanged 按字段广播
|
||||
/// </summary>
|
||||
private void ApplyParameterChangeToDeviceState(ParameterChangeNotification notify)
|
||||
{
|
||||
string deviceMac = GetCurrentDeviceMac();
|
||||
if (string.IsNullOrEmpty(deviceMac) || notify.Payload == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
switch (notify.CommandCode)
|
||||
{
|
||||
case BLEConstants.CMD_LANGUAGE_SETTING:
|
||||
UpdateDeviceState(deviceMac, BLEDeviceStateField.Language,
|
||||
state => state.SetLanguage(LanguageSetting.FromBytes(notify.Payload)));
|
||||
break;
|
||||
case BLEConstants.CMD_SCHEDULE_TASK:
|
||||
UpdateDeviceState(deviceMac, BLEDeviceStateField.ScheduleTasks,
|
||||
state => state.SetScheduleTasks(ScheduleTaskListResponse.FromBytes(0x00, notify.Payload).Tasks));
|
||||
break;
|
||||
case BLEConstants.CMD_FILL_LIGHT_CONTROL:
|
||||
UpdateDeviceState(deviceMac, BLEDeviceStateField.FillLight,
|
||||
state => state.SetFillLight(FillLightControl.FromBytes(notify.Payload)));
|
||||
break;
|
||||
case BLEConstants.CMD_ANGLE_CONTROL:
|
||||
UpdateDeviceState(deviceMac, BLEDeviceStateField.Angle,
|
||||
state => state.SetAngle(AngleControl.FromBytes(notify.Payload)));
|
||||
break;
|
||||
case BLEConstants.CMD_DISTANCE_CONTROL:
|
||||
UpdateDeviceState(deviceMac, BLEDeviceStateField.Distance,
|
||||
state => state.SetDistance(DistanceControl.FromBytes(notify.Payload)));
|
||||
break;
|
||||
case BLEConstants.CMD_VISUAL_DETECTION_SETTING:
|
||||
UpdateDeviceState(deviceMac, BLEDeviceStateField.VisualDetection,
|
||||
state => state.SetVisualDetection(VisualDetectionSetting.FromBytes(notify.Payload)));
|
||||
break;
|
||||
case BLEConstants.CMD_FILL_LIGHT_CONNECTION_STATUS:
|
||||
UpdateDeviceState(deviceMac, BLEDeviceStateField.FillLightConnection,
|
||||
state => state.SetFillLightConnection(FillLightConnectionStatus.FromBytes(notify.Payload)));
|
||||
break;
|
||||
default:
|
||||
// 未映射的字段:不写仓库,避免误覆盖
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
LogError($"参数变化通知写仓库异常(命令=0x{notify.CommandCode:X2}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCapacitorStateNotification(byte[] data)
|
||||
{
|
||||
var notify = CapacitorStateNotification.FromBytes(data);
|
||||
if (data != null && data.Length >= 2)
|
||||
{
|
||||
UpdateDeviceState(
|
||||
GetCurrentDeviceMac(),
|
||||
BLEDeviceStateField.CapacitorState,
|
||||
state => state.SetCapacitorState(notify));
|
||||
}
|
||||
Log($"蓄能状态通知: state=0x{notify.State:X2}");
|
||||
try
|
||||
{
|
||||
OnCapacitorStateNotification?.Invoke(notify);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
LogError($"OnCapacitorStateNotification 处理异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleFillLightConnectionNotification(byte[] data)
|
||||
{
|
||||
var notify = FillLightConnectionNotification.FromBytes(data);
|
||||
if (data != null && data.Length >= 2)
|
||||
{
|
||||
var status = new FillLightConnectionStatus { IsConnected = notify.IsConnected };
|
||||
UpdateDeviceState(
|
||||
GetCurrentDeviceMac(),
|
||||
BLEDeviceStateField.FillLightConnection,
|
||||
state => state.SetFillLightConnection(status));
|
||||
}
|
||||
Log($"补光灯连接状态通知: IsConnected={notify.IsConnected}");
|
||||
try
|
||||
{
|
||||
OnFillLightConnectionNotification?.Invoke(notify);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
LogError($"OnFillLightConnectionNotification 处理异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
245
Assets/Scripts/Bluetooth/BLEDeviceState.cs
Normal file
245
Assets/Scripts/Bluetooth/BLEDeviceState.cs
Normal file
@ -0,0 +1,245 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Kill.Bluetooth.Protocol;
|
||||
|
||||
namespace Kill.Bluetooth
|
||||
{
|
||||
/// <summary>
|
||||
/// 设备配置变化字段。
|
||||
/// </summary>
|
||||
public enum BLEDeviceStateField : byte
|
||||
{
|
||||
None = 0,
|
||||
Connection,
|
||||
WorkMode,
|
||||
LockState,
|
||||
ScheduleTasks,
|
||||
Angle,
|
||||
Distance,
|
||||
FillLight,
|
||||
FillLightConnection,
|
||||
VisualDetection,
|
||||
MillimeterWave,
|
||||
Language,
|
||||
HardwareStatus,
|
||||
CapacitorState
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 蓝牙设备运行状态。
|
||||
/// 以设备 MAC 为边界保存状态,避免切换设备时旧设备数据覆盖当前设备。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class BLEDeviceState
|
||||
{
|
||||
public string DeviceMac { get; private set; }
|
||||
|
||||
public bool IsConnected { get; private set; }
|
||||
|
||||
public WorkMode? WorkMode { get; private set; }
|
||||
|
||||
public DeviceLockControl? LockState { get; private set; }
|
||||
|
||||
public ScheduleTask[] ScheduleTasks { get; private set; } = new ScheduleTask[0];
|
||||
|
||||
public AngleControl? Angle { get; private set; }
|
||||
|
||||
public DistanceControl? Distance { get; private set; }
|
||||
|
||||
public FillLightControl? FillLight { get; private set; }
|
||||
|
||||
public FillLightConnectionStatus? FillLightConnection { get; private set; }
|
||||
|
||||
public VisualDetectionSetting? VisualDetection { get; private set; }
|
||||
|
||||
public LanguageSetting? Language { get; private set; }
|
||||
|
||||
public HardwareStatus? HardwareStatus { get; private set; }
|
||||
|
||||
public CapacitorStateNotification? CapacitorState { get; private set; }
|
||||
|
||||
public DateTime LastUpdatedAt { get; private set; }
|
||||
|
||||
public BLEDeviceState(string deviceMac)
|
||||
{
|
||||
DeviceMac = deviceMac ?? string.Empty;
|
||||
LastUpdatedAt = DateTime.Now;
|
||||
}
|
||||
|
||||
internal bool SetConnection(bool connected)
|
||||
{
|
||||
if (IsConnected == connected)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IsConnected = connected;
|
||||
MarkUpdated();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetWorkMode(WorkMode mode)
|
||||
{
|
||||
if (WorkMode.HasValue && WorkMode.Value == mode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
WorkMode = mode;
|
||||
MarkUpdated();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetLockState(DeviceLockControl lockState)
|
||||
{
|
||||
if (LockState.HasValue && LockState.Value.IsLocked == lockState.IsLocked)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LockState = lockState;
|
||||
MarkUpdated();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetScheduleTasks(ScheduleTask[] tasks)
|
||||
{
|
||||
ScheduleTask[] newTasks = tasks == null ? new ScheduleTask[0] : (ScheduleTask[])tasks.Clone();
|
||||
if (ScheduleTasksEqual(ScheduleTasks, newTasks))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ScheduleTasks = newTasks;
|
||||
MarkUpdated();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetAngle(AngleControl angle)
|
||||
{
|
||||
if (Angle.HasValue && Angle.Value.AngleRange == angle.AngleRange)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Angle = angle;
|
||||
MarkUpdated();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetDistance(DistanceControl distance)
|
||||
{
|
||||
if (Distance.HasValue &&
|
||||
Distance.Value.DetectionDistance == distance.DetectionDistance &&
|
||||
Distance.Value.AimDistance == distance.AimDistance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Distance = distance;
|
||||
MarkUpdated();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetFillLight(FillLightControl fillLight)
|
||||
{
|
||||
if (FillLight.HasValue &&
|
||||
FillLight.Value.Enable == fillLight.Enable &&
|
||||
FillLight.Value.LightType == fillLight.LightType &&
|
||||
FillLight.Value.Intensity == fillLight.Intensity)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FillLight = fillLight;
|
||||
MarkUpdated();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetFillLightConnection(FillLightConnectionStatus status)
|
||||
{
|
||||
if (FillLightConnection.HasValue &&
|
||||
FillLightConnection.Value.IsConnected == status.IsConnected)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FillLightConnection = status;
|
||||
MarkUpdated();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetVisualDetection(VisualDetectionSetting setting)
|
||||
{
|
||||
if (VisualDetection.HasValue &&
|
||||
VisualDetection.Value.Enable == setting.Enable &&
|
||||
VisualDetection.Value.Sensitivity == setting.Sensitivity)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
VisualDetection = setting;
|
||||
MarkUpdated();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetLanguage(LanguageSetting language)
|
||||
{
|
||||
if (Language.HasValue && Language.Value.Language == language.Language)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Language = language;
|
||||
MarkUpdated();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetHardwareStatus(HardwareStatus hardwareStatus)
|
||||
{
|
||||
if (HardwareStatus.HasValue && HardwareStatus.Value.Equals(hardwareStatus))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HardwareStatus = hardwareStatus;
|
||||
MarkUpdated();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetCapacitorState(CapacitorStateNotification capacitorState)
|
||||
{
|
||||
if (CapacitorState.HasValue && CapacitorState.Value.State == capacitorState.State)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CapacitorState = capacitorState;
|
||||
MarkUpdated();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void MarkUpdated()
|
||||
{
|
||||
LastUpdatedAt = DateTime.Now;
|
||||
}
|
||||
|
||||
private static bool ScheduleTasksEqual(ScheduleTask[] left, ScheduleTask[] right)
|
||||
{
|
||||
if (left == null || right == null || left.Length != right.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < left.Length; i++)
|
||||
{
|
||||
if (!left[i].Equals(right[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Bluetooth/BLEDeviceState.cs.meta
Normal file
11
Assets/Scripts/Bluetooth/BLEDeviceState.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e69a1efed6be8643ab6d0cf88657497
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
2105
Assets/Scripts/Bluetooth/BLE通信方案V2.2.md
Normal file
2105
Assets/Scripts/Bluetooth/BLE通信方案V2.2.md
Normal file
File diff suppressed because it is too large
Load Diff
7
Assets/Scripts/Bluetooth/BLE通信方案V2.2.md.meta
Normal file
7
Assets/Scripts/Bluetooth/BLE通信方案V2.2.md.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8db7a756f6f76fb4ebb36b63755dd084
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -27,8 +27,18 @@ namespace Kill.Bluetooth.Protocol
|
||||
public const byte CMD_UNREGISTER_USER = 0x08; // 注销用户
|
||||
|
||||
// 通知类型
|
||||
public const byte NOTIFY_DEVICE_STATE_CHANGE = 0x01; // 设备状态变化(工作模式变化)
|
||||
public const byte NOTIFY_ERROR_STATUS = 0x02; // 错误状态上报(7字节硬件错误状态)
|
||||
public const byte NOTIFY_STATISTICS_UPDATE = 0x03; // 统计数据更新
|
||||
public const byte NOTIFY_SECURITY_ALERT = 0x05; // 安全告警(人体检测等)
|
||||
public const byte NOTIFY_PARAMETER_CHANGE = 0x06; // 参数变化通知
|
||||
public const byte NOTIFY_CAPACITOR_STATE = 0x08; // 蓄能状态通知
|
||||
public const byte NOTIFY_FILL_LIGHT_CONNECTION = 0x09; // 补光灯连接状态通知
|
||||
public const byte NOTIFY_MOSQUITO_DATA = 0x0B; // 蚊虫数据通知
|
||||
|
||||
// 通知帧的"命令码"字段(与读写命令区分,统一使用 0xF0)
|
||||
public const byte NOTIFY_COMMAND_CODE = 0xF0;
|
||||
|
||||
// 命令码 - 设备设置类 (0x10-0x1F)
|
||||
public const byte CMD_LANGUAGE_SETTING = 0x10; // 语言设置
|
||||
public const byte CMD_TIME_SETTING = 0x11; // 时间设置
|
||||
@ -2259,5 +2269,118 @@ namespace Kill.Bluetooth.Protocol
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 通知数据 (0xF0 通知帧)
|
||||
|
||||
/// <summary>
|
||||
/// 设备状态变化通知
|
||||
/// 通知类型 0x01:data[0]=0x01,data[1]=工作模式状态码(0=待机 1=扫描 2=消杀)
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct DeviceStateChangeNotification
|
||||
{
|
||||
public byte WorkMode; // 0x00=待机, 0x01=扫描, 0x02=消杀
|
||||
|
||||
public static DeviceStateChangeNotification FromBytes(byte[] data)
|
||||
{
|
||||
var notify = new DeviceStateChangeNotification();
|
||||
if (data != null && data.Length >= 2)
|
||||
{
|
||||
notify.WorkMode = data[1];
|
||||
}
|
||||
return notify;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 错误状态上报通知
|
||||
/// 通知类型 0x02:data[0]=0x02,data[1..7]=7字节硬件错误状态(与 0xA1 读响应一致)
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct ErrorStatusNotification
|
||||
{
|
||||
public HardwareStatus Status;
|
||||
|
||||
public static ErrorStatusNotification FromBytes(byte[] data)
|
||||
{
|
||||
var notify = new ErrorStatusNotification();
|
||||
if (data != null && data.Length >= 8)
|
||||
{
|
||||
byte[] statusBytes = new byte[7];
|
||||
Buffer.BlockCopy(data, 1, statusBytes, 0, 7);
|
||||
notify.Status = HardwareStatus.FromBytes(statusBytes);
|
||||
}
|
||||
return notify;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 参数变化通知
|
||||
/// 通知类型 0x06:data[0]=0x06,data[1]=发生变化的命令码,data[2..]=参数数据(与对应读响应一致)
|
||||
/// 用于通知 APP 设备端参数被修改,无需 APP 主动查询即可同步
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct ParameterChangeNotification
|
||||
{
|
||||
public byte CommandCode; // 发生变化的命令码(如 0x10=语言, 0x12=定时任务, 0x22=RGB, 0x26=补光灯 等)
|
||||
public byte[] Payload; // 参数原始字节(不含通知类型与命令码字节)
|
||||
|
||||
public static ParameterChangeNotification FromBytes(byte[] data)
|
||||
{
|
||||
var notify = new ParameterChangeNotification();
|
||||
if (data != null && data.Length >= 2)
|
||||
{
|
||||
notify.CommandCode = data[1];
|
||||
if (data.Length > 2)
|
||||
{
|
||||
notify.Payload = new byte[data.Length - 2];
|
||||
Buffer.BlockCopy(data, 2, notify.Payload, 0, notify.Payload.Length);
|
||||
}
|
||||
}
|
||||
return notify;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 补光灯连接状态通知
|
||||
/// 通知类型 0x09:data[0]=0x09,data[1]=连接状态(0=断开 1=连接)
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct FillLightConnectionNotification
|
||||
{
|
||||
public bool IsConnected;
|
||||
|
||||
public static FillLightConnectionNotification FromBytes(byte[] data)
|
||||
{
|
||||
var notify = new FillLightConnectionNotification();
|
||||
if (data != null && data.Length >= 2)
|
||||
{
|
||||
notify.IsConnected = data[1] == 0x01;
|
||||
}
|
||||
return notify;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 蓄能状态通知
|
||||
/// 通知类型 0x08:data[0]=0x08,data[1]=状态(0=充电开始 1=充电完成)
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct CapacitorStateNotification
|
||||
{
|
||||
public byte State; // 0=充电开始, 1=充电完成
|
||||
|
||||
public static CapacitorStateNotification FromBytes(byte[] data)
|
||||
{
|
||||
var notify = new CapacitorStateNotification();
|
||||
if (data != null && data.Length >= 2)
|
||||
{
|
||||
notify.State = data[1];
|
||||
}
|
||||
return notify;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@ -161,6 +161,9 @@ namespace Kill.UI.Pages
|
||||
BLECommunicationManager.Instance.OnVisualDetectionSettingReceived += OnVisualDetectionReceived;
|
||||
// 订阅蚊虫数据事件
|
||||
BLECommunicationManager.Instance.OnMosquitoDataReceived += OnMosquitoDataReceived;
|
||||
|
||||
// 订阅本地状态变化事件(按字段局部刷新,保留首次全量拉取)
|
||||
BLECommunicationManager.Instance.OnDeviceStateChanged += OnDeviceStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,6 +198,9 @@ namespace Kill.UI.Pages
|
||||
BLECommunicationManager.Instance.OnVisualDetectionSettingReceived -= OnVisualDetectionReceived;
|
||||
// 取消订阅蚊虫数据事件
|
||||
BLECommunicationManager.Instance.OnMosquitoDataReceived -= OnMosquitoDataReceived;
|
||||
|
||||
// 取消订阅本地状态变化事件
|
||||
BLECommunicationManager.Instance.OnDeviceStateChanged -= OnDeviceStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@ -482,6 +488,106 @@ namespace Kill.UI.Pages
|
||||
|
||||
#endregion
|
||||
|
||||
#region 本地设备状态变化 (按字段局部刷新)
|
||||
|
||||
/// <summary>
|
||||
/// 按 MAC 隔离:当前设备不匹配(已切换/未连接/选中设备不同)则丢弃通知
|
||||
/// </summary>
|
||||
private bool IsNotificationForCurrentDevice()
|
||||
{
|
||||
string 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 本地设备状态变化回调(按字段局部刷新)
|
||||
/// 0x01/0x06 通知已由 BLECommunicationManager 解析后回写仓库并按字段广播
|
||||
/// 首次全量拉取阶段保留原路径,让读响应触发 UI 更新,避免重复刷新
|
||||
/// </summary>
|
||||
private void OnDeviceStateChanged(BLEDeviceState state, BLEDeviceStateField field)
|
||||
{
|
||||
if (state == null) return;
|
||||
if (!IsNotificationForCurrentDevice()) return;
|
||||
if (isDeviceStateInitializing) return;
|
||||
|
||||
switch (field)
|
||||
{
|
||||
case BLEDeviceStateField.WorkMode:
|
||||
if (state.WorkMode.HasValue)
|
||||
{
|
||||
int mode = (int)state.WorkMode.Value;
|
||||
currentWorkMode = state.WorkMode.Value;
|
||||
DataManager.Instance.deviceConfig.work_mode = DeviceConfig.ToServerString(state.WorkMode.Value);
|
||||
pendingUIUpdates.Add(() => deviceCtrl?.InitWrokMode(mode));
|
||||
}
|
||||
break;
|
||||
case BLEDeviceStateField.Angle:
|
||||
if (state.Angle.HasValue && !FovSettingPage.IsSettingPageActive())
|
||||
{
|
||||
int fov = (int)state.Angle.Value.ActualAngle;
|
||||
pendingUIUpdates.Add(() => deviceCtrl?.InitFovText(fov));
|
||||
}
|
||||
break;
|
||||
case BLEDeviceStateField.Distance:
|
||||
if (state.Distance.HasValue && !LensSettingPage.IsSettingPageActive())
|
||||
{
|
||||
float detectionDistance = state.Distance.Value.DetectionDistance / 10f;
|
||||
pendingUIUpdates.Add(() =>
|
||||
deviceCtrl?.InitLensText(detectionDistance, DataManager.Instance.userInfo.unit_system));
|
||||
}
|
||||
break;
|
||||
case BLEDeviceStateField.FillLight:
|
||||
if (state.FillLight.HasValue && !LightSettingPage.IsSettingPageActive())
|
||||
{
|
||||
bool enable = state.FillLight.Value.Enable;
|
||||
pendingUIUpdates.Add(() => deviceCtrl?.InitLightText(enable));
|
||||
}
|
||||
break;
|
||||
case BLEDeviceStateField.VisualDetection:
|
||||
if (state.VisualDetection.HasValue && !SafetySettingPage.IsSettingPageActive())
|
||||
{
|
||||
bool enable = state.VisualDetection.Value.Enable;
|
||||
pendingUIUpdates.Add(() => deviceCtrl?.InitSafeText(enable));
|
||||
}
|
||||
break;
|
||||
case BLEDeviceStateField.LockState:
|
||||
if (state.LockState.HasValue)
|
||||
{
|
||||
bool isLocked = state.LockState.Value.IsLocked;
|
||||
pendingUIUpdates.Add(() => deviceCtrl?.InitDeviceControl(!isLocked));
|
||||
}
|
||||
break;
|
||||
case BLEDeviceStateField.ScheduleTasks:
|
||||
if (!ScheduleSettingPage.IsSettingPageActive())
|
||||
{
|
||||
var tasks = state.ScheduleTasks;
|
||||
if (tasks != null && tasks.Length > 0)
|
||||
{
|
||||
pendingUIUpdates.Add(() => deviceSchedule?.Init(tasks));
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingUIUpdates.Add(() => deviceSchedule?.DisplayNoSchedule());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BLEDeviceStateField.Language:
|
||||
if (state.Language.HasValue)
|
||||
{
|
||||
DataManager.Instance.deviceConfig.language = state.Language.Value.IsChinese ? 1 : 0;
|
||||
DataManager.Instance.SyncDeviceConfigToServer();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// 其他字段(Connection / LockState / HardwareStatus / CapacitorState / FillLightConnection)暂不在主页直接刷新
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 网络状态事件处理
|
||||
|
||||
/// <summary>
|
||||
|
||||
2
SyntaxCheck/Program.cs
Normal file
2
SyntaxCheck/Program.cs
Normal file
@ -0,0 +1,2 @@
|
||||
// See https://aka.ms/new-console-template for more information
|
||||
Console.WriteLine("Hello, World!");
|
||||
61
SyntaxCheck/obj/SyntaxCheck.csproj.nuget.dgspec.json
Normal file
61
SyntaxCheck/obj/SyntaxCheck.csproj.nuget.dgspec.json
Normal file
@ -0,0 +1,61 @@
|
||||
{
|
||||
"format": 1,
|
||||
"restore": {
|
||||
"D:\\unity_project\\Kill\\SyntaxCheck\\SyntaxCheck.csproj": {}
|
||||
},
|
||||
"projects": {
|
||||
"D:\\unity_project\\Kill\\SyntaxCheck\\SyntaxCheck.csproj": {
|
||||
"version": "1.0.0",
|
||||
"restore": {
|
||||
"projectUniqueName": "D:\\unity_project\\Kill\\SyntaxCheck\\SyntaxCheck.csproj",
|
||||
"projectName": "SyntaxCheck",
|
||||
"projectPath": "D:\\unity_project\\Kill\\SyntaxCheck\\SyntaxCheck.csproj",
|
||||
"packagesPath": "C:\\Users\\YQC\\.nuget\\packages\\",
|
||||
"outputPath": "D:\\unity_project\\Kill\\SyntaxCheck\\obj\\",
|
||||
"projectStyle": "PackageReference",
|
||||
"configFilePaths": [
|
||||
"C:\\Users\\YQC\\AppData\\Roaming\\NuGet\\NuGet.Config"
|
||||
],
|
||||
"originalTargetFrameworks": [
|
||||
"net8.0"
|
||||
],
|
||||
"sources": {
|
||||
"https://api.nuget.org/v3/index.json": {}
|
||||
},
|
||||
"frameworks": {
|
||||
"net8.0": {
|
||||
"targetAlias": "net8.0",
|
||||
"projectReferences": {}
|
||||
}
|
||||
},
|
||||
"warningProperties": {
|
||||
"warnAsError": [
|
||||
"NU1605"
|
||||
]
|
||||
}
|
||||
},
|
||||
"frameworks": {
|
||||
"net8.0": {
|
||||
"targetAlias": "net8.0",
|
||||
"imports": [
|
||||
"net461",
|
||||
"net462",
|
||||
"net47",
|
||||
"net471",
|
||||
"net472",
|
||||
"net48",
|
||||
"net481"
|
||||
],
|
||||
"assetTargetFallback": true,
|
||||
"warn": true,
|
||||
"frameworkReferences": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.113/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
SyntaxCheck/obj/SyntaxCheck.csproj.nuget.g.props
Normal file
15
SyntaxCheck/obj/SyntaxCheck.csproj.nuget.g.props
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
|
||||
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
||||
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\YQC\.nuget\packages\</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.8.1</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\YQC\.nuget\packages\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
2
SyntaxCheck/obj/SyntaxCheck.csproj.nuget.g.targets
Normal file
2
SyntaxCheck/obj/SyntaxCheck.csproj.nuget.g.targets
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
|
||||
66
SyntaxCheck/obj/project.assets.json
Normal file
66
SyntaxCheck/obj/project.assets.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"version": 3,
|
||||
"targets": {
|
||||
"net8.0": {}
|
||||
},
|
||||
"libraries": {},
|
||||
"projectFileDependencyGroups": {
|
||||
"net8.0": []
|
||||
},
|
||||
"packageFolders": {
|
||||
"C:\\Users\\YQC\\.nuget\\packages\\": {}
|
||||
},
|
||||
"project": {
|
||||
"version": "1.0.0",
|
||||
"restore": {
|
||||
"projectUniqueName": "D:\\unity_project\\Kill\\SyntaxCheck\\SyntaxCheck.csproj",
|
||||
"projectName": "SyntaxCheck",
|
||||
"projectPath": "D:\\unity_project\\Kill\\SyntaxCheck\\SyntaxCheck.csproj",
|
||||
"packagesPath": "C:\\Users\\YQC\\.nuget\\packages\\",
|
||||
"outputPath": "D:\\unity_project\\Kill\\SyntaxCheck\\obj\\",
|
||||
"projectStyle": "PackageReference",
|
||||
"configFilePaths": [
|
||||
"C:\\Users\\YQC\\AppData\\Roaming\\NuGet\\NuGet.Config"
|
||||
],
|
||||
"originalTargetFrameworks": [
|
||||
"net8.0"
|
||||
],
|
||||
"sources": {
|
||||
"https://api.nuget.org/v3/index.json": {}
|
||||
},
|
||||
"frameworks": {
|
||||
"net8.0": {
|
||||
"targetAlias": "net8.0",
|
||||
"projectReferences": {}
|
||||
}
|
||||
},
|
||||
"warningProperties": {
|
||||
"warnAsError": [
|
||||
"NU1605"
|
||||
]
|
||||
}
|
||||
},
|
||||
"frameworks": {
|
||||
"net8.0": {
|
||||
"targetAlias": "net8.0",
|
||||
"imports": [
|
||||
"net461",
|
||||
"net462",
|
||||
"net47",
|
||||
"net471",
|
||||
"net472",
|
||||
"net48",
|
||||
"net481"
|
||||
],
|
||||
"assetTargetFallback": true,
|
||||
"warn": true,
|
||||
"frameworkReferences": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.113/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
SyntaxCheck/obj/project.nuget.cache
Normal file
8
SyntaxCheck/obj/project.nuget.cache
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dgSpecHash": "vIdeVNNgNyV9g05vzz3zmuhU/jhHrG+wZsLvVl3eSXOZZKrYzX6bKgJanV1KB2n7qJpxJWpw9zhuwF9gTFQftw==",
|
||||
"success": true,
|
||||
"projectFilePath": "D:\\unity_project\\Kill\\SyntaxCheck\\SyntaxCheck.csproj",
|
||||
"expectedPackageFiles": [],
|
||||
"logs": []
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user