killapp/Assets/Scripts/Bluetooth/BLECommunicationManager.cs
“虞渠成” 1710194d2f fix(ble&homepage): 修复蓝牙参数变化通知与主页状态同步问题
1. 调整BLE参数变化通知的数据长度校验,支持无载荷的通知场景
2. 当参数变化通知无载荷时,主动触发对应配置项的查询
3. 新增蓝牙状态补拉排队机制,解决串行查询期间请求被吞的问题
4. 优化主页配置轮询逻辑,使用独立快照避免BLE回写污染导致的循环查询
5. 新增定时任务变化检测与精准拉取逻辑
2026-09-11 13:42:23 +08:00

3300 lines
121 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
using System;
using System.Collections.Generic;
using Kill.Bluetooth.Protocol;
using Kill.UI.Components;
using Kill.Core;
namespace Kill.Bluetooth
{
/// <summary>
/// BLE通信管理器 - 处理协议层通信
/// 基于 BluetoothManager 进行协议通信
/// </summary>
public class BLECommunicationManager : MonoBehaviour
{
public static BLECommunicationManager Instance { get; private set; }
// 事件定义
public event Action<DeviceRegistrationStatus> OnRegistrationStatusReceived; // 收到注册状态
public event Action<bool, string> OnRegisterResult; // 注册结果 (成功/失败, 消息)
public event Action<UserListResponse> OnUserListReceived; // 收到用户列表
public event Action<UnregisterUserResponse> OnUnregisterUserResult; // 注销用户结果
public event Action<UserLoginResponse> OnUserLoginResult; // 用户登录结果
public event Action<FingerprintEnableResponse> OnFingerprintEnableResult; // 指纹使能设置结果
public event Action<FingerprintRecordResponse> OnFingerprintRecordResult; // 指纹录制结果
public event Action<LanguageSetting> OnLanguageSettingReceived; // 收到语言设置
public event Action<TimeSetting> OnTimeSettingReceived; // 收到时间设置
public event Action<ScheduleTaskListResponse> OnScheduleTaskListReceived; // 收到定时任务列表
public event Action<LCDSleepSetting> OnLCDSleepSettingReceived; // 收到LCD休眠设置
public event Action<LCDBrightnessSetting> OnLCDBrightnessSettingReceived; // 收到LCD亮度设置
public event Action<RGBControl> OnRGBControlReceived; // 收到RGB控制设置
public event Action<WIFIControl> OnWIFIControlReceived; // 收到WIFI控制设置
public event Action<BLEControl> OnBLEControlReceived; // 收到BLE控制设置
public event Action<MultimediaControl> OnMultimediaControlReceived; // 收到多媒体控制设置
public event Action<FillLightControl> OnFillLightControlReceived; // 收到补光灯控制设置
public event Action<FillLightConnectionStatus> OnFillLightConnectionStatusReceived; // 收到补光灯连接状态
public event Action<VisibleLaserControl> OnVisibleLaserControlReceived; // 收到可见光激光器控制设置
public event Action<DeviceLockControl> OnDeviceLockControlReceived; // 收到设备锁定/解锁设置
public event Action<AngleControl> OnAngleControlReceived; // 收到角度控制设置
public event Action<DistanceControl> OnDistanceControlReceived; // 收到距离控制设置
public event Action<MillimeterWaveSetting> OnMillimeterWaveSettingReceived; // 收到毫米波雷达设置
public event Action<VisualDetectionSetting> OnVisualDetectionSettingReceived; // 收到视觉检测设置
public event Action<AccelerometerSetting> OnAccelerometerSettingReceived; // 收到加速度传感器设置
public event Action<AccelerometerData> OnAccelerometerDataReceived; // 收到加速度传感器数据
public event Action<TemperatureMonitorSetting> OnTemperatureMonitorSettingReceived; // 收到温度监控设置
public event Action<EnvironmentChangeTest> OnEnvironmentChangeStatusReceived; // 收到环境变化状态
public event Action<WorkModeSetting> OnWorkModeSettingReceived; // 收到工作模式设置
public event Action<HardwareStatus> OnHardwareStatusReceived; // 收到硬件状态
public event Action<DeviceInfo> OnDeviceInfoReceived; // 收到设备信息
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; // 通信错误
// 状态
public bool IsWaitingResponse { get; private set; }
public byte LastCommand { get; private set; }
// 响应超时设置普通指令3秒OTA传输需要更长时间由OTAManager临时延长到10秒
public float ResponseTimeout = 3f;
// 数据接收缓冲区
private List<byte> _receiveBuffer = new List<byte>();
private readonly object _bufferLock = new object(); // 缓冲区锁
private float _responseTimer;
private Action<BLEResponse> _pendingCallback;
// 指令队列
private Queue<CommandTask> _commandQueue = new Queue<CommandTask>();
private bool _isProcessingQueue = false;
private readonly object _queueLock = new object();
/// <summary>
/// 指令任务
/// </summary>
private class CommandTask
{
public BLEFrame Frame;
public Action<BLEResponse> Callback;
public string Description;
public CommandTask(BLEFrame frame, Action<BLEResponse> callback, string description)
{
Frame = frame;
Callback = callback;
Description = description;
}
}
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, Func<BLEDeviceState, bool> 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, Func<BLEDeviceState, bool> update)
{
return response.IsSuccess && UpdateDeviceState(GetStateMac(), 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";
public string NotifyCharacteristicUUID = "0000ffe1-0000-1000-8000-00805f9b34fb";
// 是否自动初始化通信
public bool AutoInitialize = true;
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
private void Start()
{
Log("[BLECommunicationManager.Start] 开始执行...");
// 订阅蓝牙连接事件
if (BluetoothManager.Instance != null)
{
Log($"[BLECommunicationManager.Start] BluetoothManager.Instance 存在IsConnected={BluetoothManager.Instance.IsConnected}");
BluetoothManager.Instance.OnConnectedSuccess += OnBluetoothConnected;
BluetoothManager.Instance.OnDisconnected += OnBluetoothDisconnected;
BluetoothManager.Instance.OnError += OnBluetoothError;
Log("[BLECommunicationManager.Start] 事件订阅完成");
// 如果已经连接(可能是自动连接已完成),立即初始化
if (BluetoothManager.Instance.IsConnected)
{
Log("[BLECommunicationManager.Start] 检测到蓝牙已连接,立即初始化通信...");
OnBluetoothConnected();
}
else
{
Log("[BLECommunicationManager.Start] 蓝牙未连接,等待连接事件...");
}
}
else
{
LogError("[BLECommunicationManager.Start] BluetoothManager.Instance 为 null");
}
}
/// <summary>
/// 蓝牙错误回调
/// </summary>
private void OnBluetoothError(string error)
{
// 忽略 "Service not found" 错误,因为这是正常的(还没连接设备)
if (error.Contains("Service not found"))
{
Log("蓝牙服务未找到(正常:尚未连接设备)");
return;
}
LogError($"蓝牙错误: {error}");
}
private void OnDestroy()
{
// 取消订阅事件
if (BluetoothManager.Instance != null)
{
BluetoothManager.Instance.OnConnectedSuccess -= OnBluetoothConnected;
BluetoothManager.Instance.OnDisconnected -= OnBluetoothDisconnected;
BluetoothManager.Instance.OnError -= OnBluetoothError;
}
if (Instance == this)
{
Instance = null;
}
}
private void Update()
{
// 响应超时检测
if (IsWaitingResponse)
{
_responseTimer -= Time.deltaTime;
if (_responseTimer <= 0)
{
OnResponseTimeout();
}
}
}
#region
/// <summary>
/// 蓝牙连接成功回调(特征发现完成后调用)
/// </summary>
private void OnBluetoothConnected()
{
string stateMac = GetCurrentDeviceMac();
if (!string.IsNullOrEmpty(stateMac))
{
_lastStateMac = stateMac;
UpdateDeviceState(
stateMac,
BLEDeviceStateField.Connection,
state => state.SetConnection(true));
}
if (AutoInitialize)
InitializeCommunication();
}
/// <summary>
/// 蓝牙断开连接回调
/// </summary>
private void OnBluetoothDisconnected(string address)
{
Log($"蓝牙已断开: {address}");
// 断开参数通常是 BLE 地址,优先使用已记录的真实 MAC 作为状态键。
string stateMac = string.IsNullOrEmpty(_lastStateMac)
? NormalizeDeviceMac(address)
: _lastStateMac;
if (string.IsNullOrEmpty(stateMac))
{
stateMac = GetCurrentDeviceMac();
}
_lastStateMac = string.IsNullOrEmpty(stateMac) ? _lastStateMac : stateMac;
UpdateDeviceState(
stateMac,
BLEDeviceStateField.Connection,
state => state.SetConnection(false));
// 清理状态
IsWaitingResponse = false;
_responseTimer = 0;
_pendingCallback = null;
lock (_bufferLock)
{
_receiveBuffer.Clear();
}
// 清空指令队列
ClearCommandQueue();
}
/// <summary>
/// 初始化BLE通信 (连接成功后自动调用,也可手动调用)
/// </summary>
public void InitializeCommunication()
{
if (BluetoothManager.Instance == null || !BluetoothManager.Instance.IsConnected) return;
ServiceUUID = ServiceUUID.ToLower();
WriteCharacteristicUUID = WriteCharacteristicUUID.ToLower();
NotifyCharacteristicUUID = NotifyCharacteristicUUID.ToLower();
SubscribeToNotifications();
}
/// <summary>
/// 订阅设备通知
/// </summary>
private void SubscribeToNotifications()
{
string deviceAddress = BluetoothManager.Instance.ConnectedDeviceAddress;
ServiceUUID = ServiceUUID.ToLower();
WriteCharacteristicUUID = WriteCharacteristicUUID.ToLower();
NotifyCharacteristicUUID = NotifyCharacteristicUUID.ToLower();
Log($"[BLE-DEBUG] 订阅通知: 地址={deviceAddress}");
try
{
BluetoothLEHardwareInterface.SubscribeCharacteristicWithDeviceAddress(
deviceAddress,
ServiceUUID,
NotifyCharacteristicUUID,
(notifyAddress, notifyCharacteristic) =>
{
Log($"[BLE-DEBUG] 订阅成功: {notifyCharacteristic}");
},
(address, characteristicUUID, bytes) =>
{
OnNotificationReceived(bytes);
}
);
}
catch (System.Exception ex)
{
LogError($"[BLE-DEBUG] 订阅异常: {ex.Message}");
}
}
/// <summary>
/// 取消订阅通知
/// </summary>
public void UnsubscribeNotifications()
{
if (BluetoothManager.Instance == null || !BluetoothManager.Instance.IsConnected)
return;
string deviceAddress = BluetoothManager.Instance.ConnectedDeviceAddress;
BluetoothLEHardwareInterface.UnSubscribeCharacteristic(
deviceAddress,
ServiceUUID,
NotifyCharacteristicUUID,
null
);
}
#endregion
#region
/// <summary>
/// 查询设备注册状态 (命令 0x01)
/// </summary>
public void QueryRegistrationStatus(Action<bool,DeviceRegistrationStatus> callback = null)
{
if (!CheckConnection()) return;
// 构建查询帧
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_QUERY_REGISTRATION,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
if (response.IsSuccess && response.Data != null)
{
var status = DeviceRegistrationStatus.FromBytes(response.Data);
callback?.Invoke(true,status);
OnRegistrationStatusReceived?.Invoke(status);
}
else
{
LogError($"查询注册状态失败: 状态码={response.Status:X2}");
callback?.Invoke(false,new DeviceRegistrationStatus());
}
});
}
/// <summary>
/// 设备注册绑定 (命令 0x02)
/// 注意:根据协议,只发送用户名,不发送密码
/// </summary>
/// <param name="username">用户名</param>
/// <param name="callback">回调 (成功/失败, 消息)</param>
public void RegisterDevice(string username, Action<bool, string> callback = null)
{
if (!CheckConnection()) return;
// 验证输入
if (string.IsNullOrEmpty(username) || username.Length > BLEConstants.MAX_USERNAME_LENGTH)
{
callback?.Invoke(false, $"用户名长度必须在1-{BLEConstants.MAX_USERNAME_LENGTH}之间");
return;
}
// 构建注册请求(只发送用户名)
var request = new DeviceRegisterRequest
{
Username = username
};
byte[] requestData = request.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_DEVICE_REGISTER,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)requestData.Length,
Data = requestData
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
string message = GetStatusMessage(response.Status);
if (success)
{
Log($"设备注册成功: 用户={username}");
if (response.Data != null && response.Data.Length > 0)
{
Log($"注册响应数据: {BitConverter.ToString(response.Data)}");
}
}
else
{
LogError($"设备注册失败: {message}");
}
callback?.Invoke(success, message);
OnRegisterResult?.Invoke(success, message);
});
}
/// <summary>
/// 用户登录 (命令 0x03)
/// 数据格式:用户名(16字节) + 解锁状态(1字节)解锁状态固定为1
/// </summary>
/// <param name="username">用户名</param>
/// <param name="callback">回调 (响应结果)</param>
public void UserLogin(string username, Action<UserLoginResponse> callback = null)
{
if (!CheckConnection()) return;
// 验证输入
if (string.IsNullOrEmpty(username) || username.Length > BLEConstants.MAX_USERNAME_LENGTH)
{
LogError($"用户名长度必须在1-{BLEConstants.MAX_USERNAME_LENGTH}之间");
callback?.Invoke(new UserLoginResponse { Status = BLEConstants.STATUS_PARAM_ERROR });
return;
}
// 构建登录请求
var request = new UserLoginRequest
{
Username = username,
UnlockStatus = 0x01 // 解锁状态固定为1
};
byte[] requestData = request.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_USER_LOGIN,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)requestData.Length,
Data = requestData
};
SendFrame(frame, (response) =>
{
var loginResponse = UserLoginResponse.FromBytes(response.Status);
if (loginResponse.IsSuccess)
{
Log($"用户登录成功: {username}");
}
else
{
LogError($"用户登录失败: {username}, 状态码={response.Status:X2}");
}
callback?.Invoke(loginResponse);
OnUserLoginResult?.Invoke(loginResponse);
});
}
/// <summary>
/// 指纹登录使能设置 (命令 0x04)
/// 数据格式:用户名(16字节) + 使能状态(1字节)
/// </summary>
/// <param name="username">用户名</param>
/// <param name="enable">使能状态true=使能, false=禁用</param>
/// <param name="callback">回调 (响应结果)</param>
public void SetFingerprintEnable(string username, bool enable, Action<FingerprintEnableResponse> callback = null)
{
if (!CheckConnection()) return;
// 验证输入
if (string.IsNullOrEmpty(username) || username.Length > BLEConstants.MAX_USERNAME_LENGTH)
{
LogError($"用户名长度必须在1-{BLEConstants.MAX_USERNAME_LENGTH}之间");
callback?.Invoke(new FingerprintEnableResponse { Status = BLEConstants.STATUS_PARAM_ERROR });
return;
}
// 构建请求
var request = new FingerprintEnableRequest
{
Username = username,
Enable = enable
};
byte[] requestData = request.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_FINGERPRINT_ENABLE,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)requestData.Length,
Data = requestData
};
SendFrame(frame, (response) =>
{
var enableResponse = FingerprintEnableResponse.FromBytes(response.Status);
if (enableResponse.IsSuccess)
{
Log($"指纹登录使能设置成功: {username}, 状态={(enable ? "使" : "")}");
}
else
{
LogError($"指纹登录使能设置失败: {username}, 状态码={response.Status:X2}");
}
callback?.Invoke(enableResponse);
OnFingerprintEnableResult?.Invoke(enableResponse);
});
}
/// <summary>
/// 指纹录制请求 (命令 0x05)
/// 启动指纹注册流程
/// </summary>
/// <param name="username">用户名</param>
/// <param name="callback">回调 (响应结果)</param>
public void StartFingerprintRecord(string username, Action<FingerprintRecordResponse> callback = null)
{
if (!CheckConnection()) return;
// 验证输入
if (string.IsNullOrEmpty(username) || username.Length > BLEConstants.MAX_USERNAME_LENGTH)
{
LogError($"用户名长度必须在1-{BLEConstants.MAX_USERNAME_LENGTH}之间");
callback?.Invoke(new FingerprintRecordResponse { Status = BLEConstants.STATUS_PARAM_ERROR });
return;
}
// 构建请求
var request = new FingerprintRecordRequest
{
Username = username
};
byte[] requestData = request.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_FINGERPRINT_RECORD,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)requestData.Length,
Data = requestData
};
SendFrame(frame, (response) =>
{
var recordResponse = FingerprintRecordResponse.FromBytes(response.Status);
if (recordResponse.IsSuccess)
{
Log($"指纹录制请求成功: {username}");
}
else
{
LogError($"指纹录制请求失败: {username}, 状态码={response.Status:X2}");
}
callback?.Invoke(recordResponse);
OnFingerprintRecordResult?.Invoke(recordResponse);
});
}
#region (0x10-0x1F)
/// <summary>
/// 读取语言设置 (命令 0x10)
/// </summary>
public void ReadLanguageSetting(Action<LanguageSetting> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_LANGUAGE_SETTING,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
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);
});
}
/// <summary>
/// 写入语言设置 (命令 0x10)
/// </summary>
/// <param name="isChinese">true=中文, false=英文</param>
public void WriteLanguageSetting(bool isChinese, Action<bool> callback = null)
{
if (!CheckConnection()) return;
var setting = new LanguageSetting { Language = isChinese ? (byte)0x01 : (byte)0x00 };
byte[] data = setting.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_LANGUAGE_SETTING,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
{
Log($"语言设置成功: {(isChinese ? "" : "")}");
UpdateStateFromResponse(response, BLEDeviceStateField.Language,
state => state.SetLanguage(setting));
}
else
{
LogError($"语言设置失败, 状态码={response.Status:X2}");
}
callback?.Invoke(success);
});
}
/// <summary>
/// 读取时间设置 (命令 0x11)
/// </summary>
public void ReadTimeSetting(Action<TimeSetting> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_TIME_SETTING,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var setting = TimeSetting.FromBytes(response.Data);
callback?.Invoke(setting);
OnTimeSettingReceived?.Invoke(setting);
});
}
/// <summary>
/// 写入时间设置 (命令 0x11)
/// </summary>
public void WriteTimeSetting(TimeSetting setting, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = setting.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_TIME_SETTING,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
Log($"时间设置成功: {setting}");
else
LogError($"时间设置失败, 状态码={response.Status:X2}");
callback?.Invoke(success);
});
}
/// <summary>
/// 读取定时任务列表 (命令 0x12)
/// </summary>
public void ReadScheduleTasks(Action<ScheduleTaskListResponse> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_SCHEDULE_TASK,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
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);
});
}
/// <summary>
/// 写入定时任务 (命令 0x12)
/// 支持一次写入单条或多条定时任务最多5条
/// </summary>
/// <param name="task">单条定时任务</param>
/// <param name="callback">回调 (成功/失败)</param>
public void WriteScheduleTask(ScheduleTask task, Action<bool> callback = null)
{
WriteScheduleTasks(new ScheduleTask[] { task }, callback);
}
/// <summary>
/// 写入多条定时任务 (命令 0x12)
/// 支持一次写入最多5组定时任务硬件已支持批量写入
/// </summary>
/// <param name="tasks">定时任务数组最多5条每条8字节</param>
/// <param name="callback">回调 (成功/失败)</param>
public void WriteScheduleTasks(ScheduleTask[] tasks, Action<bool> callback = null)
{
if (!CheckConnection()) return;
if (tasks == null || tasks.Length == 0)
{
LogError("定时任务数组为空");
callback?.Invoke(false);
return;
}
if (tasks.Length > 5)
{
LogError("定时任务数量不能超过5条");
callback?.Invoke(false);
return;
}
// 计算总数据长度每条任务7字节不包含TaskId与读取格式一致
int totalLength = tasks.Length * 7;
byte[] data = new byte[totalLength];
// 将所有任务序列化到字节数组
for (int i = 0; i < tasks.Length; i++)
{
byte[] taskBytes = tasks[i].ToBytesWithoutTaskId();
Buffer.BlockCopy(taskBytes, 0, data, i * 7, 7);
// 调试:打印每个任务的详细信息
Debug.Log($"[WriteScheduleTasks] Task[{i}]: Enabled={tasks[i].Enabled} (0x{taskBytes[0]:X2}), " +
$"Time={tasks[i].StartHour:D2}:{tasks[i].StartMinute:D2}-{tasks[i].EndHour:D2}:{tasks[i].EndMinute:D2}, " +
$"Mode={(byte)tasks[i].Mode}, Repeat=0x{tasks[i].Repeat:X2}");
}
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_SCHEDULE_TASK,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
Debug.Log($"[WriteScheduleTasks] 发送数据: {BitConverter.ToString(data)}");
Debug.Log("定时任务:"+frame.ToString());
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
{
if (tasks.Length == 1)
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
{
LogError($"定时任务设置失败, 状态码={response.Status:X2}");
}
callback?.Invoke(success);
});
}
#endregion
#region (0x20-0x3F)
/// <summary>
/// 读取LCD休眠设置 (命令 0x20)
/// </summary>
public void ReadLCDSleepSetting(Action<LCDSleepSetting> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_LCD_SLEEP_SETTING,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var setting = LCDSleepSetting.FromBytes(response.Data);
callback?.Invoke(setting);
OnLCDSleepSettingReceived?.Invoke(setting);
});
}
/// <summary>
/// 写入LCD休眠设置 (命令 0x20)
/// </summary>
public void WriteLCDSleepSetting(LCDSleepSetting setting, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = setting.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_LCD_SLEEP_SETTING,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
Log($"LCD休眠设置成功: 开关={(setting.Enable ? "" : "")}, 时间={setting.GetSleepTimeString()}");
else
LogError($"LCD休眠设置失败, 状态码={response.Status:X2}");
callback?.Invoke(success);
});
}
/// <summary>
/// 读取LCD亮度设置 (命令 0x21)
/// </summary>
public void ReadLCDBrightnessSetting(Action<LCDBrightnessSetting> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_LCD_BRIGHTNESS_SETTING,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var setting = LCDBrightnessSetting.FromBytes(response.Data);
callback?.Invoke(setting);
OnLCDBrightnessSettingReceived?.Invoke(setting);
});
}
/// <summary>
/// 写入LCD亮度设置 (命令 0x21)
/// </summary>
public void WriteLCDBrightnessSetting(LCDBrightnessSetting setting, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = setting.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_LCD_BRIGHTNESS_SETTING,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
Log($"LCD亮度设置响应: 状态码={response.Status:X2}, 成功={success}");
if (success)
Log($"LCD亮度设置成功: 自适应={(setting.AutoBrightness ? "" : "")}, 亮度={setting.Brightness}%");
else
LogError($"LCD亮度设置失败, 状态码={response.Status:X2}");
callback?.Invoke(success);
});
}
/// <summary>
/// 读取RGB指示灯控制 (命令 0x22)
/// </summary>
public void ReadRGBControl(Action<RGBControl> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_RGB_CONTROL,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var control = RGBControl.FromBytes(response.Data);
callback?.Invoke(control);
OnRGBControlReceived?.Invoke(control);
});
}
/// <summary>
/// 写入RGB指示灯控制 (命令 0x22)
/// </summary>
public void WriteRGBControl(RGBControl control, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = control.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_RGB_CONTROL,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
Log($"RGB控制设置成功: 开关={(control.Enable ? "" : "")}, 颜色={control.Red},{control.Green},{control.Blue}");
else
LogError($"RGB控制设置失败, 状态码={response.Status:X2}");
callback?.Invoke(success);
});
}
/// <summary>
/// 读取WIFI控制 (命令 0x23)
/// </summary>
public void ReadWIFIControl(Action<WIFIControl> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_WIFI_CONTROL,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var control = WIFIControl.FromBytes(response.Data);
callback?.Invoke(control);
OnWIFIControlReceived?.Invoke(control);
});
}
/// <summary>
/// 写入WIFI控制 (命令 0x23)
/// </summary>
public void WriteWIFIControl(WIFIControl control, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = control.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_WIFI_CONTROL,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
Log($"WIFI控制设置成功: 开关={(control.Enable ? "" : "")}, SSID={control.SSID}");
else
LogError($"WIFI控制设置失败, 状态码={response.Status:X2}");
callback?.Invoke(success);
});
}
/// <summary>
/// 读取BLE控制 (命令 0x24)
/// </summary>
public void ReadBLEControl(Action<BLEControl> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_BLE_CONTROL,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var control = BLEControl.FromBytes(response.Data);
callback?.Invoke(control);
OnBLEControlReceived?.Invoke(control);
});
}
/// <summary>
/// 写入BLE控制 (命令 0x24)
/// </summary>
public void WriteBLEControl(BLEControl control, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = control.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_BLE_CONTROL,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
Log($"BLE控制设置成功: 设备名称={control.DeviceName}");
else
LogError($"BLE控制设置失败, 状态码={response.Status:X2}");
callback?.Invoke(success);
});
}
/// <summary>
/// 读取多媒体控制 (命令 0x25)
/// </summary>
public void ReadMultimediaControl(Action<MultimediaControl> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_MULTIMEDIA_CONTROL,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var control = MultimediaControl.FromBytes(response.Data);
callback?.Invoke(control);
OnMultimediaControlReceived?.Invoke(control);
});
}
/// <summary>
/// 写入多媒体控制 (命令 0x25)
/// </summary>
public void WriteMultimediaControl(MultimediaControl control, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = control.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_MULTIMEDIA_CONTROL,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
Log($"多媒体控制设置成功: 视频录制={(control.VideoRecordEnable ? "" : "")}, 音效={(control.SoundEnable ? "" : "")}");
else
LogError($"多媒体控制设置失败, 状态码={response.Status:X2}");
callback?.Invoke(success);
});
}
/// <summary>
/// 读取补光灯控制 (命令 0x26)
/// </summary>
public void ReadFillLightControl(Action<FillLightControl> callback = null)
{
Loom.QueueOnMainThread(() =>
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_FILL_LIGHT_CONTROL,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var control = FillLightControl.FromBytes(response.Data);
UpdateStateFromResponse(response, BLEDeviceStateField.FillLight,
state => state.SetFillLight(control));
callback?.Invoke(control);
OnFillLightControlReceived?.Invoke(control);
});
});
}
/// <summary>
/// 写入补光灯控制 (命令 0x26)
/// </summary>
public void WriteFillLightControl(FillLightControl control, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = control.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_FILL_LIGHT_CONTROL,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
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);
});
}
/// <summary>
/// 读取补光灯连接状态 (命令 0x27)
/// </summary>
public void ReadFillLightConnectionStatus(Action<FillLightConnectionStatus> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_FILL_LIGHT_CONNECTION_STATUS,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var status = FillLightConnectionStatus.FromBytes(response.Data);
UpdateStateFromResponse(response, BLEDeviceStateField.FillLightConnection,
state => state.SetFillLightConnection(status));
callback?.Invoke(status);
OnFillLightConnectionStatusReceived?.Invoke(status);
});
}
/// <summary>
/// 读取可见光激光器控制 (命令 0x28)
/// </summary>
public void ReadVisibleLaserControl(Action<VisibleLaserControl> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_VISIBLE_LASER_CONTROL,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var control = VisibleLaserControl.FromBytes(response.Data);
callback?.Invoke(control);
OnVisibleLaserControlReceived?.Invoke(control);
});
}
/// <summary>
/// 写入可见光激光器控制 (命令 0x28)
/// </summary>
public void WriteVisibleLaserControl(VisibleLaserControl control, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = control.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_VISIBLE_LASER_CONTROL,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
Log($"可见光激光器控制设置成功: 开关={(control.Enable ? "" : "")}");
else
LogError($"可见光激光器控制设置失败, 状态码={response.Status:X2}");
callback?.Invoke(success);
});
}
/// <summary>
/// 读取设备锁定状态 (命令 0x29)
/// </summary>
public void ReadDeviceLockControl(Action<DeviceLockControl> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_DEVICE_LOCK,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
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);
});
}
/// <summary>
/// 写入设备锁定状态 (命令 0x29)
/// </summary>
/// <param name="isLocked">true=锁定, false=解锁</param>
public void WriteDeviceLockControl(bool isLocked, Action<bool> callback = null)
{
if (!CheckConnection()) return;
var control = new DeviceLockControl { IsLocked = isLocked };
byte[] data = control.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_DEVICE_LOCK,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
Debug.Log($"[WriteDeviceLockControl] 发送: IsLocked={isLocked}, Data={BitConverter.ToString(data)}");
SendFrame(frame, (response) =>
{
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);
});
}
#endregion
#region (0x40-0x4F)
/// <summary>
/// 读取角度控制设置 (命令 0x40)
/// </summary>
public void ReadAngleControl(Action<AngleControl> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_ANGLE_CONTROL,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
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}");
});
}
/// <summary>
/// 写入角度控制设置 (命令 0x40)
/// </summary>
/// <param name="control">角度控制设置</param>
public void WriteAngleControl(AngleControl control, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = control.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_ANGLE_CONTROL,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
{
Log($"角度控制设置成功: {control}");
UpdateStateFromResponse(response, BLEDeviceStateField.Angle,
state => state.SetAngle(control));
}
else
{
LogError($"角度控制设置失败, 状态码={response.Status:X2}");
}
callback?.Invoke(success);
});
}
/// <summary>
/// 读取距离控制设置 (命令 0x41)
/// </summary>
public void ReadDistanceControl(Action<DistanceControl> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_DISTANCE_CONTROL,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
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}");
});
}
/// <summary>
/// 写入距离控制设置 (命令 0x41)
/// </summary>
/// <param name="control">距离控制设置</param>
public void WriteDistanceControl(DistanceControl control, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = control.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_DISTANCE_CONTROL,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
{
Log($"距离控制设置成功: {control}");
UpdateStateFromResponse(response, BLEDeviceStateField.Distance,
state => state.SetDistance(control));
}
else
{
LogError($"距离控制设置失败, 状态码={response.Status:X2}");
}
callback?.Invoke(success);
});
}
#endregion
#region (0x50-0x5F)
/// <summary>
/// 读取毫米波雷达设置 (命令 0x50)
/// </summary>
public void ReadMillimeterWaveSetting(Action<MillimeterWaveSetting> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_MILLIMETER_WAVE_SETTING,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
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}");
});
}
/// <summary>
/// 写入毫米波雷达设置 (命令 0x50)
/// </summary>
public void WriteMillimeterWaveSetting(MillimeterWaveSetting setting, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = setting.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_MILLIMETER_WAVE_SETTING,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
{
Log($"毫米波设置成功: {setting}");
UpdateStateFromResponse(response, BLEDeviceStateField.MillimeterWave,
state => state.SetMillimeterWave(setting));
}
else
{
LogError($"毫米波设置失败, 状态码={response.Status:X2}");
}
callback?.Invoke(success);
});
}
/// <summary>
/// 读取视觉检测设置 (命令 0x51)
/// </summary>
public void ReadVisualDetectionSetting(Action<VisualDetectionSetting> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_VISUAL_DETECTION_SETTING,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
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}");
});
}
/// <summary>
/// 写入视觉检测设置 (命令 0x51)
/// </summary>
public void WriteVisualDetectionSetting(VisualDetectionSetting setting, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = setting.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_VISUAL_DETECTION_SETTING,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
{
Log($"视觉检测设置成功: {setting}");
UpdateStateFromResponse(response, BLEDeviceStateField.VisualDetection,
state => state.SetVisualDetection(setting));
}
else
{
LogError($"视觉检测设置失败, 状态码={response.Status:X2}");
}
callback?.Invoke(success);
});
}
/// <summary>
/// 读取加速度传感器设置 (命令 0x52)
/// </summary>
public void ReadAccelerometerSetting(Action<AccelerometerSetting> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_ACCELEROMETER_SETTING,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var setting = AccelerometerSetting.FromBytes(response.Data);
callback?.Invoke(setting);
OnAccelerometerSettingReceived?.Invoke(setting);
Log($"读取加速度传感器设置: {setting}");
});
}
/// <summary>
/// 写入加速度传感器设置 (命令 0x52)
/// </summary>
public void WriteAccelerometerSetting(AccelerometerSetting setting, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = setting.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_ACCELEROMETER_SETTING,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
Log($"加速度传感器设置成功: {setting}");
else
LogError($"加速度传感器设置失败, 状态码={response.Status:X2}");
callback?.Invoke(success);
});
}
/// <summary>
/// 查询加速度传感器数据 (命令 0x53)
/// </summary>
public void ReadAccelerometerData(Action<AccelerometerData> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_ACCELEROMETER_DATA,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var data = AccelerometerData.FromBytes(response.Data);
callback?.Invoke(data);
OnAccelerometerDataReceived?.Invoke(data);
Log($"读取加速度传感器数据: {data}");
});
}
/// <summary>
/// 读取温度监控设置 (命令 0x55)
/// </summary>
public void ReadTemperatureMonitorSetting(Action<TemperatureMonitorSetting> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_TEMPERATURE_MONITOR_SETTING,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var setting = TemperatureMonitorSetting.FromBytes(response.Data);
callback?.Invoke(setting);
OnTemperatureMonitorSettingReceived?.Invoke(setting);
Log($"读取温度监控设置: {setting}");
});
}
/// <summary>
/// 写入温度监控设置 (命令 0x55)
/// </summary>
public void WriteTemperatureMonitorSetting(TemperatureMonitorSetting setting, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = setting.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_TEMPERATURE_MONITOR_SETTING,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
Log($"温度监控设置成功: {setting}");
else
LogError($"温度监控设置失败, 状态码={response.Status:X2}");
callback?.Invoke(success);
});
}
#endregion
#region (0xA0-0xBF)
/// <summary>
/// 读取工作模式 (命令 0xA0)
/// </summary>
public void ReadWorkMode(Action<WorkModeSetting> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_WORK_MODE_SETTING,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
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}");
});
}
/// <summary>
/// 写入工作模式 (命令 0xA0)
/// </summary>
public void WriteWorkMode(WorkModeSetting setting, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = setting.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_WORK_MODE_SETTING,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
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);
});
}
/// <summary>
/// 查询硬件状态 (命令 0xA1)
/// </summary>
public void ReadHardwareStatus(Action<HardwareStatus> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_HARDWARE_STATUS,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
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}");
});
}
/// <summary>
/// 查询设备信息 (命令 0xA2)
/// </summary>
public void ReadDeviceInfo(Action<DeviceInfo> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_DEVICE_INFO,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var info = DeviceInfo.FromBytes(response.Data);
callback?.Invoke(info);
OnDeviceInfoReceived?.Invoke(info);
Log($"查询设备信息: {info}");
});
}
/// <summary>
/// 写入设备信息 (命令 0xA2 写操作)
/// 写操作接收56字节设备信息
/// </summary>
public void WriteDeviceInfo(DeviceInfoWrite deviceInfo, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = deviceInfo.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_DEVICE_INFO,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
Log($"设备信息写入成功: {deviceInfo}");
else
LogError($"设备信息写入失败, 状态码={response.Status:X2}");
callback?.Invoke(success);
});
}
/// <summary>
/// 查询统计数据 (命令 0xA3)
/// </summary>
public void ReadStatisticsData(Action<StatisticsData> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_STATISTICS_DATA,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var stats = StatisticsData.FromBytes(response.Data);
callback?.Invoke(stats);
OnStatisticsDataReceived?.Invoke(stats);
Log($"查询统计数据: {stats}");
});
}
/// <summary>
/// 查询蚊虫数据 (命令 0xA4)
/// </summary>
public void QueryMosquitoData(MosquitoDataQuery query, Action<bool> callback = null)
{
if (!CheckConnection()) return;
byte[] data = query.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_MOSQUITO_DATA_QUERY,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
Log($"蚊虫数据查询成功: {query}");
else
LogError($"蚊虫数据查询失败, 状态码={response.Status:X2}");
callback?.Invoke(success);
});
}
/// <summary>
/// 查询传感器数据 (命令 0xA5)
/// </summary>
public void ReadSensorData(Action<SensorData> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_SENSOR_DATA,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var data = SensorData.FromBytes(response.Data);
callback?.Invoke(data);
OnSensorDataReceived?.Invoke(data);
Log($"查询传感器数据: {data}");
});
}
#endregion
#region (0xA4 + 0x0B)
/// <summary>
/// 查询蚊虫数据数量 (命令 0xA4 读操作)
/// </summary>
public void ReadMosquitoDataCount(Action<MosquitoDataCount> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_MOSQUITO_DATA_QUERY,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var count = MosquitoDataCount.FromBytes(response.Data);
callback?.Invoke(count);
Log($"查询蚊虫数据数量: {count}");
});
}
/// <summary>
/// 请求蚊虫数据 (命令 0xA4 写操作)
/// 写操作成功后,设备通过通知机制发送数据
/// </summary>
public void RequestMosquitoData(uint count, Action<bool> callback = null)
{
if (!CheckConnection()) return;
// 构建4字节数据读取数量小端序
byte[] data = new byte[]
{
(byte)(count & 0xFF),
(byte)((count >> 8) & 0xFF),
(byte)((count >> 16) & 0xFF),
(byte)((count >> 24) & 0xFF)
};
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_MOSQUITO_DATA_QUERY,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
{
mosquitoDataCount=(int)count;
Log($"蚊虫数据请求成功,期望接收 {count} 条数据");
}
else
LogError($"蚊虫数据请求失败, 状态码={response.Status:X2}");
callback?.Invoke(success);
});
}
public int mosquitoDataCount=0;
/// <summary>
/// 处理蚊虫数据通知
/// </summary>
private void HandleMosquitoDataNotification(byte[] notifyData)
{
if(mosquitoDataCount<=0)
return;
if (notifyData == null || notifyData.Length < 20)
{
LogError("蚊虫数据通知格式错误数据长度不足20字节");
return;
}
mosquitoDataCount--;
var mosquitoData = MosquitoData.FromBytes(notifyData);
Log($"收到蚊虫数据通知: {mosquitoData} 剩余{mosquitoDataCount}");
OnMosquitoDataReceived?.Invoke(mosquitoData);
}
/// <summary>
/// 读取环境变化状态 (命令 0x56 读操作)
/// </summary>
public void ReadEnvironmentChangeStatus(Action<EnvironmentChangeTest> callback = null)
{
if (!CheckConnection()) return;
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_ENVIRONMENT_CHANGE_TEST,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
var status = EnvironmentChangeTest.FromBytes(response.Data);
callback?.Invoke(status);
OnEnvironmentChangeStatusReceived?.Invoke(status);
Log($"读取环境变化状态: {status}");
});
}
/// <summary>
/// 触发激光试射测试 (命令 0x56 写操作)
/// 触发激光进行3次试射测试每次间隔1秒每次持续10ms
/// </summary>
public void TriggerLaserTest(Action<bool> callback = null)
{
if (!CheckConnection()) return;
var test = new EnvironmentChangeTest { TriggerTest = true };
byte[] data = test.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_ENVIRONMENT_CHANGE_TEST,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)data.Length,
Data = data
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
Log("激光试射测试已触发将进行3次试射每次间隔1秒每次持续10ms");
else
LogError($"激光试射测试触发失败, 状态码={response.Status:X2}");
callback?.Invoke(success);
});
}
/// <summary>
/// 恢复出厂设置 (命令 0x60)
/// 需要管理员权限
/// 写操作发送1字节确认码 0x01
/// </summary>
public void FactoryReset(Action<bool> callback = null)
{
if (!CheckConnection()) return;
// 构建恢复出厂设置帧,发送确认码 0x01
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_FACTORY_RESET,
ReadWrite = BLEConstants.RW_WRITE,
Length = 1,
Data = new byte[] { 0x01 }
};
SendFrame(frame, (response) =>
{
bool success = response.IsSuccess;
if (success)
Log("恢复出厂设置命令发送成功,设备将恢复出厂设置");
else if (response.Status == BLEConstants.STATUS_PERMISSION_ERROR)
LogError("恢复出厂设置失败:需要管理员权限");
else
LogError($"恢复出厂设置失败, 状态码=0x{response.Status:X2}");
callback?.Invoke(success);
});
}
#endregion
/// <summary>
/// 查询用户列表 (命令 0x07)
/// </summary>
public void QueryUserList(Action<UserListResponse> callback = null)
{
if (!CheckConnection()) return;
// 构建查询帧
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_QUERY_USER_LIST,
ReadWrite = BLEConstants.RW_READ,
Length = 0,
Data = null
};
SendFrame(frame, (response) =>
{
if (response.IsSuccess && response.Data != null)
{
var userList = UserListResponse.FromBytes(response.Status, response.Data);
callback?.Invoke(userList);
OnUserListReceived?.Invoke(userList);
}
else
{
LogError($"查询用户列表失败: 状态码={response.Status:X2}");
callback?.Invoke(new UserListResponse() { Status = response.Status });
}
});
}
/// <summary>
/// 注销用户 (命令 0x08)
/// </summary>
/// <param name="username">要注销的用户名</param>
/// <param name="callback">回调 (响应结果)</param>
public void UnregisterUser(string username, Action<UnregisterUserResponse> callback = null)
{
if (!CheckConnection()) return;
// 验证输入
if (string.IsNullOrEmpty(username) || username.Length > BLEConstants.MAX_USERNAME_LENGTH)
{
LogError($"用户名长度必须在1-{BLEConstants.MAX_USERNAME_LENGTH}之间");
callback?.Invoke(new UnregisterUserResponse { Status = BLEConstants.STATUS_PARAM_ERROR });
return;
}
// 构建注销请求
var request = new UnregisterUserRequest
{
Username = username
};
byte[] requestData = request.ToBytes();
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_UNREGISTER_USER,
ReadWrite = BLEConstants.RW_WRITE,
Length = (byte)requestData.Length,
Data = requestData
};
SendFrame(frame, (response) =>
{
var unregisterResponse = UnregisterUserResponse.FromBytes(response.Status);
if (unregisterResponse.IsSuccess)
{
Log($"用户注销成功: {username}");
}
else
{
LogError($"用户注销失败: {username}, 状态码={response.Status:X2}");
}
callback?.Invoke(unregisterResponse);
OnUnregisterUserResult?.Invoke(unregisterResponse);
});
}
/// <summary>
/// 主动断开连接 (命令 0x70)
/// 注意设备收到0x70后会立即断开BLE所以不会收到响应需要特殊处理
/// </summary>
public void Disconnect(Action<bool> callback = null)
{
if (!CheckConnection()) return;
// 构建断开连接帧(无数据)
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = BLEConstants.CMD_DISCONNECT,
ReadWrite = BLEConstants.RW_WRITE,
Length = 0,
Data = null
};
Log($"[BLE-DEBUG] Disconnect: 发送0x70命令: {BitConverter.ToString(frame.ToBytes())}");
SendFrameWithoutResponse(frame);
callback?.Invoke(true);
}
#endregion
#region
/// <summary>
/// 发送协议帧(不等待响应)
/// 用于0x70断开命令等特殊场景
/// </summary>
private void SendFrameWithoutResponse(BLEFrame frame)
{
if (!CheckConnection()) return;
byte[] frameBytes = frame.ToBytes();
string deviceAddress = BluetoothManager.Instance.ConnectedDeviceAddress;
Log($"[BLE-DEBUG] SendFrame: 地址={deviceAddress}, 数据={BitConverter.ToString(frameBytes)}, withResponse=false");
try
{
BluetoothLEHardwareInterface.WriteCharacteristic(
deviceAddress,
ServiceUUID.ToLower(),
WriteCharacteristicUUID.ToLower(),
frameBytes,
frameBytes.Length,
false,
(characteristicUUID) =>
{
Log($"[BLE-DEBUG] SendFrame完成");
}
);
}
catch (System.Exception ex)
{
LogError($"[BLE-DEBUG] SendFrame异常: {ex.Message}");
}
}
/// <summary>
/// 发送协议帧(内部方法,供队列使用)
/// </summary>
public void SendFrameInternal(BLEFrame frame, Action<BLEResponse> callback)
{
SendFrameDirect(frame, callback);
}
/// <summary>
/// 发送协议帧(使用队列,顺序执行)
/// 这是推荐的公共接口
/// </summary>
public void SendFrame(BLEFrame frame, Action<BLEResponse> callback, string description = "")
{
// 检查基本连接状态
if (!CheckConnection())
{
LogError($"[BLE-DEBUG] 蓝牙未就绪,无法发送指令: {description}");
callback?.Invoke(new BLEResponse { Status = BLEConstants.STATUS_DEVICE_ERROR, Data = new byte[0] });
return;
}
// 加入队列顺序执行
EnqueueCommand(frame, callback, description);
}
/// <summary>
/// 发送协议帧(底层实现,直接发送)
/// </summary>
private void SendFrameDirect(BLEFrame frame, Action<BLEResponse> callback)
{
if (!CheckConnection())
{
LogError("[BLE-DEBUG] CheckConnection failed, invoking callback with error");
callback?.Invoke(new BLEResponse { Status = BLEConstants.STATUS_DEVICE_ERROR, Data = new byte[0] });
return;
}
byte[] frameBytes = frame.ToBytes();
// 调试:打印完整的发送帧
if (frame.Command == BLEConstants.CMD_SCHEDULE_TASK)
{
Debug.Log($"[SendFrame] 完整帧数据: {BitConverter.ToString(frameBytes)}");
}
IsWaitingResponse = true;
_responseTimer = ResponseTimeout;
LastCommand = frame.Command;
_pendingCallback = callback;
// 清空接收缓冲区
lock (_bufferLock)
{
_receiveBuffer.Clear();
}
// 通过BLE发送数据
string deviceAddress = BluetoothManager.Instance.ConnectedDeviceAddress;
// 确保UUID是小写
ServiceUUID = ServiceUUID.ToLower();
WriteCharacteristicUUID = WriteCharacteristicUUID.ToLower();
string hexData = BitConverter.ToString(frameBytes);
// 触发发送数据事件用于UI显示
OnRawDataSent?.Invoke(hexData);
try
{
BluetoothLEHardwareInterface.WriteCharacteristic(
deviceAddress,
ServiceUUID,
WriteCharacteristicUUID,
frameBytes,
frameBytes.Length,
true,
(characteristicUUID) => { }
);
}
catch (System.Exception ex)
{
LogError($"发送帧异常: {ex.Message}");
IsWaitingResponse = false;
callback?.Invoke(new BLEResponse { Status = BLEConstants.STATUS_DEVICE_ERROR, Data = new byte[0] });
}
}
/// <summary>
/// 处理收到的通知数据
/// </summary>
private void OnNotificationReceived(byte[] data)
{
if (data == null || data.Length == 0) return;
string hexString = BitConverter.ToString(data);
// 触发原始数据事件用于UI显示
OnRawDataReceived?.Invoke(hexString);
// 添加到接收缓冲区并尝试解析
lock (_bufferLock)
{
_receiveBuffer.AddRange(data);
TryParseFrameInternal();
}
}
/// <summary>
/// 尝试从缓冲区解析帧(内部方法,需要在锁内调用)
/// </summary>
private void TryParseFrameInternal()
{
// 收集所有完整帧,避免频繁 StartCoroutine 和 return 导致积压
var frames = new List<BLEFrame>();
int totalRemoved = 0;
while (totalRemoved < _receiveBuffer.Count)
{
int searchStart = totalRemoved;
byte[] buffer = _receiveBuffer.ToArray();
bool found = false;
for (int i = searchStart; i < buffer.Length - 1; i++)
{
if (buffer[i] == BLEConstants.FRAME_HEADER_1 &&
buffer[i + 1] == BLEConstants.FRAME_HEADER_2)
{
if (i + 4 >= buffer.Length) continue;
byte dataLength = buffer[i + 4];
int totalFrameLength = 7 + dataLength;
if (i + totalFrameLength > buffer.Length) continue;
byte[] frameBytes = new byte[totalFrameLength];
Buffer.BlockCopy(buffer, i, frameBytes, 0, totalFrameLength);
if (BLEFrame.TryParse(frameBytes, out BLEFrame frame))
{
frames.Add(frame);
totalRemoved += (i - searchStart) + totalFrameLength;
searchStart = totalRemoved;
found = true;
break;
}
else
{
LogWarning("帧解析失败 (CRC校验可能失败)");
}
}
}
if (!found) break;
}
// 移除已解析的数据
if (totalRemoved > 0)
_receiveBuffer.RemoveRange(0, totalRemoved);
// 清理过期缓冲区
if (_receiveBuffer.Count > 200)
_receiveBuffer.RemoveRange(0, _receiveBuffer.Count - 100);
// 在锁外批量处理帧
foreach (var frame in frames)
StartCoroutine(HandleFrameAsync(frame));
}
/// <summary>
/// 异步处理帧(避免在锁内处理)
/// </summary>
private System.Collections.IEnumerator HandleFrameAsync(BLEFrame frame)
{
yield return null; // 等待一帧,确保锁已释放
HandleReceivedFrame(frame);
}
/// <summary>
/// 尝试从缓冲区解析帧(公共方法)
/// </summary>
private void TryParseFrame()
{
lock (_bufferLock)
{
TryParseFrameInternal();
}
}
/// <summary>
/// 处理收到的帧
/// </summary>
private void HandleReceivedFrame(BLEFrame frame)
{
bool wasWaitingResponse = IsWaitingResponse;
// 通知帧优先处理并且绝对不影响命令队列IsWaitingResponse / _pendingCallback / _responseTimer
// 设备主动通知是异步旁路通道,与当前等待响应的命令是两条独立链路
if (frame.ReadWrite == BLEConstants.RW_NOTIFY && frame.Command == BLEConstants.NOTIFY_COMMAND_CODE)
{
HandleNotification(frame.Data);
return;
}
// 响应帧:优先匹配当前等待的命令。
if (IsWaitingResponse && frame.Command == LastCommand)
{
IsWaitingResponse = false;
_responseTimer = 0;
var response = BLEResponse.FromFrame(frame);
_pendingCallback?.Invoke(response);
_pendingCallback = null;
}
else if (IsWaitingResponse)
{
// 保留原兼容行为:设备响应延迟时仍允许结束当前等待任务。
IsWaitingResponse = false;
_responseTimer = 0;
var response = BLEResponse.FromFrame(frame);
_pendingCallback?.Invoke(response);
_pendingCallback = null;
}
if (wasWaitingResponse)
{
ProcessCommandQueue();
}
}
/// <summary>
/// 通用的设备通知分发器,按通知类型路由到对应处理逻辑
/// 通知帧约定data[0]=通知类型data[1..]=通知数据
/// </summary>
private void HandleNotification(byte[] data)
{
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;
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)
{
if (data == null || data.Length < 2)
{
LogWarning("设备状态变化通知数据长度不足");
return;
}
var notify = DeviceStateChangeNotification.FromBytes(data);
if (notify.WorkMode > (byte)WorkMode.Eliminate)
{
LogWarning($"设备状态变化通知包含无效工作模式: 0x{notify.WorkMode:X2}");
return;
}
Log($"设备状态变化通知: 工作模式=0x{notify.WorkMode:X2}");
UpdateDeviceState(
GetStateMac(),
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)
{
if (data == null || data.Length < 8)
{
LogWarning("错误状态通知数据长度不足");
return;
}
var notify = ErrorStatusNotification.FromBytes(data);
if (data != null && data.Length >= 8)
{
UpdateDeviceState(
GetStateMac(),
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)
{
// 最少2字节通知类型 + 命令码(载荷允许为空,如固件上报 06-A0 不带新值)
if (data == null || data.Length < 2)
{
LogWarning("参数变化通知数据长度不足");
return;
}
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);
try
{
OnParameterChangeNotification?.Invoke(notify);
}
catch (System.Exception ex)
{
LogError($"OnParameterChangeNotification 处理异常: {ex.Message}");
}
}
/// <summary>
/// 把 0x06 参数变化通知解析后回写到 BLEDeviceState 仓库,仅在值变化时通过 OnDeviceStateChanged 按字段广播
/// </summary>
private void ApplyParameterChangeToDeviceState(ParameterChangeNotification notify)
{
string deviceMac = GetStateMac();
if (string.IsNullOrEmpty(deviceMac) || notify.Payload == null)
{
return;
}
try
{
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;
}
}
catch (System.Exception ex)
{
LogError($"参数变化通知写仓库异常(命令=0x{notify.CommandCode:X2}): {ex.Message}");
}
}
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(
GetStateMac(),
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)
{
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(
GetStateMac(),
BLEDeviceStateField.FillLightConnection,
state => state.SetFillLightConnection(status));
}
Log($"补光灯连接状态通知: IsConnected={notify.IsConnected}");
try
{
OnFillLightConnectionNotification?.Invoke(notify);
}
catch (System.Exception ex)
{
LogError($"OnFillLightConnectionNotification 处理异常: {ex.Message}");
}
}
/// <summary>
/// 响应超时处理
/// </summary>
private void OnResponseTimeout()
{
IsWaitingResponse = false;
_responseTimer = 0;
LogError($"命令超时: 命令={LastCommand:X2}");
var timeoutResponse = new BLEResponse
{
Status = BLEConstants.STATUS_TIMEOUT,
// 超时无数据,给空数组避免回调里 BitConverter.ToString(response.Data) 抛 ArgumentNullException
Data = new byte[0]
};
_pendingCallback?.Invoke(timeoutResponse);
_pendingCallback = null;
// 超时后继续处理队列
ProcessCommandQueue();
}
#endregion
#region
/// <summary>
/// 检查蓝牙连接状态(带自动初始化)
/// </summary>
private bool CheckConnection()
{
// 检查蓝牙管理器
if (BluetoothManager.Instance == null)
{
LogError("BluetoothManager.Instance 为 null");
LoadingUI.Hide();
return false;
}
// 如果蓝牙未初始化,尝试自动初始化
if (!BluetoothManager.Instance.IsInitialized)
{
Log("蓝牙未初始化,尝试自动初始化...");
BluetoothManager.Instance.Initialize();
LoadingUI.Hide();
return false;
}
// 如果蓝牙未连接
if (!BluetoothManager.Instance.IsConnected)
{
LogError("蓝牙未连接");
ToastUI.Show("100081"); // "请先连接蓝牙设备"
LoadingUI.Hide(); // 未连接时立即隐藏Loading避免一直转圈
return false;
}
return true;
}
/// <summary>
/// 将指令加入队列,顺序执行
/// </summary>
public void EnqueueCommand(BLEFrame frame, Action<BLEResponse> callback, string description = "")
{
lock (_queueLock)
{
_commandQueue.Enqueue(new CommandTask(frame, callback, description));
Log($"指令已加入队列: {description}, 当前队列长度: {_commandQueue.Count}");
}
// 尝试处理队列
ProcessCommandQueue();
}
/// <summary>
/// 处理指令队列
/// </summary>
private void ProcessCommandQueue()
{
lock (_queueLock)
{
// 如果正在处理或队列为空,直接返回
if (_isProcessingQueue || _commandQueue.Count == 0)
{
return;
}
// 如果正在等待响应,等待完成后再处理
if (IsWaitingResponse)
{
Log("等待当前指令响应完成...");
return;
}
_isProcessingQueue = true;
}
// 获取下一个指令
CommandTask task = null;
lock (_queueLock)
{
if (_commandQueue.Count > 0)
{
task = _commandQueue.Dequeue();
}
}
if (task != null)
{
Log($"开始执行队列指令: {task.Description}, 剩余队列长度: {_commandQueue.Count}");
SendFrameInternal(task.Frame, task.Callback);
}
lock (_queueLock)
{
_isProcessingQueue = false;
}
}
/// <summary>
/// 清空指令队列
/// </summary>
public void ClearCommandQueue()
{
lock (_queueLock)
{
int count = _commandQueue.Count;
_commandQueue.Clear();
Log($"指令队列已清空,清除了 {count} 个指令");
}
}
/// <summary>
/// 获取队列长度
/// </summary>
public int GetCommandQueueCount()
{
lock (_queueLock)
{
return _commandQueue.Count;
}
}
/// <summary>
/// 确保蓝牙已初始化并连接(异步)
/// </summary>
public void EnsureBluetoothReady(System.Action onReady)
{
if (BluetoothManager.Instance == null)
{
LogError("BluetoothManager.Instance 为 null");
return;
}
// 如果已经连接,直接回调
if (BluetoothManager.Instance.IsConnected)
{
onReady?.Invoke();
return;
}
// 如果未初始化,先初始化
if (!BluetoothManager.Instance.IsInitialized)
{
Log("蓝牙未初始化,开始初始化...");
BluetoothManager.Instance.Initialize();
// 订阅初始化完成事件
System.Action<bool> onInitialized = null;
onInitialized = (success) =>
{
BluetoothManager.Instance.OnInitialized -= onInitialized;
if (success)
{
Log("蓝牙初始化成功,可以开始扫描设备");
onReady?.Invoke();
}
else
{
LogError("蓝牙初始化失败");
}
};
BluetoothManager.Instance.OnInitialized += onInitialized;
}
else
{
// 已初始化但未连接
onReady?.Invoke();
}
}
/// <summary>
/// 获取状态码描述
/// </summary>
private string GetStatusMessage(byte status)
{
switch (status)
{
case BLEConstants.STATUS_SUCCESS: return "成功";
case BLEConstants.STATUS_PARAM_ERROR: return "参数错误";
case BLEConstants.STATUS_PERMISSION_ERROR: return "权限错误";
case BLEConstants.STATUS_DEVICE_BUSY: return "设备忙";
case BLEConstants.STATUS_CMD_NOT_SUPPORT: return "命令不支持";
case BLEConstants.STATUS_DATA_CHECK_FAIL: return "数据校验失败";
case BLEConstants.STATUS_TIMEOUT: return "超时";
case BLEConstants.STATUS_DEVICE_ERROR: return "设备错误";
default: return $"未知错误 (0x{status:X2})";
}
}
#region WiFi指令获取接口
/// <summary>
/// 获取完整蓝牙指令字节数组用于WiFi发送
/// 将BLE协议帧转换为字节数组可直接通过TCP发送给服务器
/// </summary>
/// <param name="command">命令码</param>
/// <param name="readWrite">读写标识 0x00=读, 0x01=写, 0x02=通知</param>
/// <param name="data">数据字段可为null</param>
/// <returns>完整的协议帧字节数组包含帧头、命令、数据、CRC</returns>
public byte[] GetCommandBytes(byte command, byte readWrite, byte[] data)
{
var frame = new BLEFrame
{
Header1 = BLEConstants.FRAME_HEADER_1,
Header2 = BLEConstants.FRAME_HEADER_2,
Command = command,
ReadWrite = readWrite,
Length = (byte)(data?.Length ?? 0),
Data = data
};
return frame.ToBytes();
}
/// <summary>
/// 获取完整蓝牙指令字节数组(简化版,默认写操作)
/// </summary>
/// <param name="command">命令码</param>
/// <param name="data">数据字段可为null</param>
/// <returns>完整的协议帧字节数组</returns>
public byte[] GetCommandBytes(byte command, byte[] data)
{
return GetCommandBytes(command, BLEConstants.RW_WRITE, data);
}
/// <summary>
/// 获取读取指令字节数组(简化版,读操作无数据)
/// </summary>
/// <param name="command">命令码</param>
/// <returns>完整的协议帧字节数组</returns>
public byte[] GetReadCommandBytes(byte command)
{
return GetCommandBytes(command, BLEConstants.RW_READ, null);
}
/// <summary>
/// 获取指令的十六进制字符串表示(用于调试)
/// </summary>
/// <param name="command">命令码</param>
/// <param name="readWrite">读写标识</param>
/// <param name="data">数据字段</param>
/// <returns>十六进制字符串,如 "AA-55-23-01-04-..."</returns>
public string GetCommandHexString(byte command, byte readWrite, byte[] data)
{
byte[] bytes = GetCommandBytes(command, readWrite, data);
return BitConverter.ToString(bytes);
}
/// <summary>
/// 从BLEFrame获取完整指令字节数组
/// </summary>
/// <param name="frame">协议帧结构</param>
/// <returns>完整的协议帧字节数组</returns>
public byte[] GetFrameBytes(BLEFrame frame)
{
return frame.ToBytes();
}
#endregion
private void Log(string message)
{
Debug.Log($"[BLECommunication] {message}");
}
private void LogError(string message)
{
Debug.LogError($"[BLECommunication] {message}");
OnCommunicationError?.Invoke(message);
}
private void LogWarning(string message)
{
Debug.LogWarning($"[BLECommunication] {message}");
}
#endregion
}
}