using System;
using UnityEngine;
namespace BLEProtocol
{
///
/// BLE通信协议常量定义
///
public static class BLEConstants
{
// 帧头
public const byte FRAME_HEADER_1 = 0xAA;
public const byte FRAME_HEADER_2 = 0x55;
// 读/写标识
public const byte RW_READ = 0x00; // 读操作
public const byte RW_WRITE = 0x01; // 写操作
public const byte RW_NOTIFY = 0x02; // 通知
// 命令码 - 设备注册与认证类 (0x01-0x0F)
public const byte CMD_QUERY_REGISTRATION = 0x01; // 查询设备注册状态
public const byte CMD_DEVICE_REGISTER = 0x02; // 设备注册绑定
public const byte CMD_USER_LOGIN = 0x03; // 用户登录
public const byte CMD_FINGERPRINT_ENABLE = 0x04; // 指纹登录使能设置
public const byte CMD_FINGERPRINT_RECORD = 0x05; // 指纹录制请求
public const byte CMD_QUERY_USER_LIST = 0x07; // 查询用户列表
public const byte CMD_UNREGISTER_USER = 0x08; // 注销用户
// 通知类型
public const byte NOTIFY_MOSQUITO_DATA = 0x0B; // 蚊虫数据通知
// 命令码 - 设备设置类 (0x10-0x1F)
public const byte CMD_LANGUAGE_SETTING = 0x10; // 语言设置
public const byte CMD_TIME_SETTING = 0x11; // 时间设置
public const byte CMD_SCHEDULE_TASK = 0x12; // 定时任务设置
// 命令码 - 外设控制类 (0x20-0x3F)
public const byte CMD_LCD_SLEEP_SETTING = 0x20; // LCD休眠设置
public const byte CMD_LCD_BRIGHTNESS_SETTING = 0x21; // LCD亮度设置
public const byte CMD_RGB_CONTROL = 0x22; // RGB指示灯控制
public const byte CMD_WIFI_CONTROL = 0x23; // WIFI控制
public const byte CMD_BLE_CONTROL = 0x24; // BLE控制
public const byte CMD_MULTIMEDIA_CONTROL = 0x25; // 多媒体控制
public const byte CMD_FILL_LIGHT_CONTROL = 0x26; // 补光灯控制
public const byte CMD_FILL_LIGHT_CONNECTION_STATUS = 0x27; // 查询补光灯连接状态
public const byte CMD_VISIBLE_LASER_CONTROL = 0x28; // 可见光激光器控制
// 命令码 - 害虫消灭控制类 (0x40-0x4F)
public const byte CMD_ANGLE_CONTROL = 0x40; // 角度控制
public const byte CMD_DISTANCE_CONTROL = 0x41; // 距离控制
// 命令码 - 安全设置类 (0x50-0x5F)
public const byte CMD_MILLIMETER_WAVE_SETTING = 0x50; // 毫米波雷达设置
public const byte CMD_VISUAL_DETECTION_SETTING = 0x51; // 视觉检测设置
public const byte CMD_ACCELEROMETER_SETTING = 0x52; // 加速度传感器设置
public const byte CMD_ACCELEROMETER_DATA = 0x53; // 查询加速度传感器数据
public const byte CMD_TEMPERATURE_MONITOR_SETTING = 0x55; // 温度监控设置
public const byte CMD_ENVIRONMENT_CHANGE_TEST = 0x56; // 环境变化检测和试射测试
// 命令码 - 设备管理类 (0x60-0x6F)
public const byte CMD_FACTORY_RESET = 0x60; // 恢复出厂设置
// 命令码 - 状态查询类 (0xA0-0xBF)
public const byte CMD_WORK_MODE_SETTING = 0xA0; // 工作模式设置
public const byte CMD_HARDWARE_STATUS = 0xA1; // 查询硬件状态
public const byte CMD_DEVICE_INFO = 0xA2; // 查询设备信息
public const byte CMD_STATISTICS_DATA = 0xA3; // 查询统计数据
public const byte CMD_MOSQUITO_DATA_QUERY = 0xA4; // 查询蚊虫数据
public const byte CMD_SENSOR_DATA = 0xA5; // 查询设备传感器数据
// 命令码 - 连接管理类 (0x70-0x7F)
public const byte CMD_DISCONNECT = 0x70; // 主动断开连接
// 命令码 - OTA升级类 (0x80-0x9F)
public const byte CMD_OTA_QUERY_VERSION = 0x80; // 查询OTA版本信息
public const byte CMD_OTA_START = 0x81; // 开始升级
public const byte CMD_OTA_TRANSFER_DATA = 0x82; // 传输固件数据
public const byte CMD_OTA_END_TRANSFER = 0x83; // 结束传输
public const byte CMD_OTA_CANCEL = 0x85; // 取消升级
// 状态码(0=成功,非0=错误)
public const byte STATUS_SUCCESS = 0x00; // 成功
public const byte STATUS_PARAM_ERROR = 0x01; // 参数错误
public const byte STATUS_PERMISSION_ERROR = 0x02; // 权限错误
public const byte STATUS_DEVICE_BUSY = 0x03; // 设备忙
public const byte STATUS_CMD_NOT_SUPPORT = 0x04; // 命令不支持
public const byte STATUS_DATA_CHECK_FAIL = 0x05; // 数据校验失败
public const byte STATUS_TIMEOUT = 0x06; // 超时
public const byte STATUS_DEVICE_ERROR = 0x07; // 设备错误
// 最大用户名长度
public const int MAX_USERNAME_LENGTH = 16;
}
///
/// 通信帧结构
///
[Serializable]
public struct BLEFrame
{
public byte Header1; // 帧头1: 0xAA
public byte Header2; // 帧头2: 0x55
public byte Command; // 命令码
public byte ReadWrite; // 读/写标识: 0x00=读, 0x01=写, 0x02=通知
public byte Length; // 数据长度
public byte[] Data; // 数据字段
public ushort Crc16; // CRC16校验
///
/// 将帧序列化为字节数组
///
public byte[] ToBytes()
{
int dataLength = Data != null ? Data.Length : 0;
byte[] bytes = new byte[6 + dataLength + 2]; // 帧头(2) + 命令(1) + 读写(1) + 长度(1) + 数据 + CRC(2)
bytes[0] = Header1;
bytes[1] = Header2;
bytes[2] = Command;
bytes[3] = ReadWrite;
bytes[4] = (byte)dataLength;
if (dataLength > 0)
{
Buffer.BlockCopy(Data, 0, bytes, 5, dataLength);
}
// 计算CRC16 (帧头到数据字段)
ushort crc = CRC16.Calculate(bytes, 0, 5 + dataLength);
bytes[5 + dataLength] = (byte)(crc & 0xFF);
bytes[6 + dataLength] = (byte)((crc >> 8) & 0xFF);
// 调试日志:打印发送的CRC
UnityEngine.Debug.Log($"[CRC-SEND] 命令=0x{Command:X2}, 数据长度={dataLength}, CRC=0x{crc:X4}, CRC字节={bytes[5 + dataLength]:X2}-{bytes[6 + dataLength]:X2}");
return bytes;
}
///
/// 从字节数组解析帧
///
public static bool TryParse(byte[] bytes, out BLEFrame frame)
{
frame = new BLEFrame();
if (bytes == null || bytes.Length < 7) // 最小帧长度: 帧头(2) + 命令(1) + 读写(1) + 长度(1) + CRC(2) = 7字节
{
return false;
}
// 验证帧头
if (bytes[0] != BLEConstants.FRAME_HEADER_1 || bytes[1] != BLEConstants.FRAME_HEADER_2)
{
return false;
}
frame.Header1 = bytes[0];
frame.Header2 = bytes[1];
frame.Command = bytes[2];
frame.ReadWrite = bytes[3];
frame.Length = bytes[4];
int dataLength = frame.Length;
int expectedLength = 7 + dataLength; // 帧头(2) + 命令(1) + 读写(1) + 长度(1) + 数据(dataLength) + CRC(2)
if (bytes.Length < expectedLength)
{
Debug.LogWarning($"数据长度不足: 需要 {expectedLength} 字节, 实际 {bytes.Length} 字节");
return false;
}
// 提取数据
if (dataLength > 0)
{
frame.Data = new byte[dataLength];
Buffer.BlockCopy(bytes, 5, frame.Data, 0, dataLength);
}
// 提取CRC
frame.Crc16 = (ushort)(bytes[5 + dataLength] | (bytes[6 + dataLength] << 8));
// 验证CRC
ushort calculatedCrc = CRC16.Calculate(bytes, 0, 5 + dataLength);
Debug.Log($"[CRC调试] 计算值={calculatedCrc:X4}, 接收值={frame.Crc16:X4}");
Debug.Log($"[CRC调试] 计算数据: {System.BitConverter.ToString(bytes, 0, 5 + dataLength)}");
Debug.Log($"[CRC调试] 接收CRC字节: {bytes[5 + dataLength]:X2}-{bytes[6 + dataLength]:X2}");
if (calculatedCrc != frame.Crc16)
{
Debug.LogWarning($"CRC校验失败: 计算值={calculatedCrc:X4} ({calculatedCrc & 0xFF:X2}-{(calculatedCrc >> 8) & 0xFF:X2}), 接收值={frame.Crc16:X4} ({frame.Crc16 & 0xFF:X2}-{(frame.Crc16 >> 8) & 0xFF:X2})");
return false;
}
return true;
}
}
///
/// 设备注册状态响应
/// 命令 0x01 返回:
/// - 文档标准:1字节(0x00=未注册, 0x01=已注册)
/// - 当前固件:2字节(00-01,第一位状态码,第二位注册状态)
///
[Serializable]
public struct DeviceRegistrationStatus
{
public byte Status; // 响应状态码(0x00=成功)
public byte RegistrationState; // 0x00=否/未注册, 0x01=是/已注册
///
/// 从数据字节解析
/// 兼容1字节和2字节格式
///
public static DeviceRegistrationStatus FromBytes(byte[] data)
{
var status = new DeviceRegistrationStatus();
if (data == null || data.Length == 0)
return status;
if (data.Length == 1)
{
// 标准格式:1字节,直接是注册状态
status.Status = 0x00; // 假设成功
status.RegistrationState = data[0];
}
else if (data.Length >= 2)
{
// 当前固件格式:2字节(00-01)
status.Status = data[0]; // 第一位:状态码
status.RegistrationState = data[1]; // 第二位:注册状态
}
return status;
}
///
/// 是否已注册(0=否,1=是)
///
public bool IsRegistered => RegistrationState == 0x01;
}
///
/// 设备注册请求
/// 注意:根据协议,只发送用户名,不发送密码
///
[Serializable]
public struct DeviceRegisterRequest
{
public string Username; // 用户名 (最多16字节ASCII)
///
/// 序列化为字节数组
/// 只发送用户名(16字节),不发送密码
///
public byte[] ToBytes()
{
byte[] bytes = new byte[16]; // 仅用户名(16字节)
// 用户名 (16字节, 不足补0)
byte[] usernameBytes = System.Text.Encoding.ASCII.GetBytes(Username ?? "");
int usernameLen = System.Math.Min(usernameBytes.Length, 16);
System.Buffer.BlockCopy(usernameBytes, 0, bytes, 0, usernameLen);
return bytes;
}
}
///
/// 注销用户请求
/// 命令 0x08:注销指定用户
///
[Serializable]
public struct UnregisterUserRequest
{
public string Username; // 要注销的用户名 (最多16字节ASCII)
///
/// 序列化为字节数组
/// 用户名(16字节,不足补0)
///
public byte[] ToBytes()
{
byte[] bytes = new byte[16]; // 用户名(16字节)
// 用户名 (16字节, 不足补0)
byte[] usernameBytes = System.Text.Encoding.ASCII.GetBytes(Username ?? "");
int usernameLen = System.Math.Min(usernameBytes.Length, 16);
System.Buffer.BlockCopy(usernameBytes, 0, bytes, 0, usernameLen);
return bytes;
}
}
///
/// 注销用户响应
///
[Serializable]
public struct UnregisterUserResponse
{
public byte Status; // 响应状态码(0x00=成功)
///
/// 是否成功
///
public bool IsSuccess => Status == 0x00;
///
/// 从数据字节解析
///
public static UnregisterUserResponse FromBytes(byte status)
{
return new UnregisterUserResponse { Status = status };
}
}
///
/// 用户登录请求
/// 命令 0x03:用户登录(密码)
/// 数据格式:用户名(16字节) + 解锁状态(1字节),解锁状态固定为1
///
[Serializable]
public struct UserLoginRequest
{
public string Username; // 用户名 (最多16字节ASCII)
public byte UnlockStatus; // 解锁状态,固定为1
///
/// 序列化为字节数组
/// 用户名(16字节,不足补0)+ 解锁状态(1字节,固定为1)
///
public byte[] ToBytes()
{
byte[] bytes = new byte[17]; // 用户名(16字节) + 解锁状态(1字节)
// 用户名 (16字节, 不足补0)
byte[] usernameBytes = System.Text.Encoding.ASCII.GetBytes(Username ?? "");
int usernameLen = System.Math.Min(usernameBytes.Length, 16);
System.Buffer.BlockCopy(usernameBytes, 0, bytes, 0, usernameLen);
// 解锁状态固定为1
bytes[16] = 0x01;
return bytes;
}
}
///
/// 用户登录响应
///
[Serializable]
public struct UserLoginResponse
{
public byte Status; // 响应状态码(0x00=成功)
///
/// 是否成功
///
public bool IsSuccess => Status == 0x00;
///
/// 从数据字节解析
///
public static UserLoginResponse FromBytes(byte status)
{
return new UserLoginResponse { Status = status };
}
}
///
/// 指纹登录使能设置请求
/// 命令 0x04:设置用户指纹登录使能状态
/// 数据格式:用户名(16字节) + 使能状态(1字节)
/// 使能状态:0x00=禁用, 0x01=使能
///
[Serializable]
public struct FingerprintEnableRequest
{
public string Username; // 用户名 (最多16字节ASCII)
public bool Enable; // 使能状态:true=使能, false=禁用
///
/// 序列化为字节数组
/// 用户名(16字节,不足补0)+ 使能状态(1字节)
///
public byte[] ToBytes()
{
byte[] bytes = new byte[17]; // 用户名(16字节) + 使能状态(1字节)
// 用户名 (16字节, 不足补0)
byte[] usernameBytes = System.Text.Encoding.ASCII.GetBytes(Username ?? "");
int usernameLen = System.Math.Min(usernameBytes.Length, 16);
System.Buffer.BlockCopy(usernameBytes, 0, bytes, 0, usernameLen);
// 使能状态
bytes[16] = Enable ? (byte)0x01 : (byte)0x00;
return bytes;
}
}
///
/// 指纹登录使能设置响应
///
[Serializable]
public struct FingerprintEnableResponse
{
public byte Status; // 响应状态码(0x00=成功)
///
/// 是否成功
///
public bool IsSuccess => Status == 0x00;
///
/// 从数据字节解析
///
public static FingerprintEnableResponse FromBytes(byte status)
{
return new FingerprintEnableResponse { Status = status };
}
}
///
/// 指纹录制请求
/// 命令 0x05:启动指纹注册流程
/// 数据格式:用户名(16字节)
///
[Serializable]
public struct FingerprintRecordRequest
{
public string Username; // 用户名 (最多16字节ASCII)
///
/// 序列化为字节数组
/// 用户名(16字节,不足补0)
///
public byte[] ToBytes()
{
byte[] bytes = new byte[16]; // 用户名(16字节)
// 用户名 (16字节, 不足补0)
byte[] usernameBytes = System.Text.Encoding.ASCII.GetBytes(Username ?? "");
int usernameLen = System.Math.Min(usernameBytes.Length, 16);
System.Buffer.BlockCopy(usernameBytes, 0, bytes, 0, usernameLen);
return bytes;
}
}
///
/// 指纹录制响应
///
[Serializable]
public struct FingerprintRecordResponse
{
public byte Status; // 响应状态码(0x00=成功)
///
/// 是否成功
///
public bool IsSuccess => Status == 0x00;
///
/// 从数据字节解析
///
public static FingerprintRecordResponse FromBytes(byte status)
{
return new FingerprintRecordResponse { Status = status };
}
}
///
/// 通用响应结构
///
[Serializable]
public struct BLEResponse
{
public byte Status; // 状态码(0=成功,其他=错误码)
public byte[] Data; // 响应数据
///
/// 是否成功(状态码0=成功,非0=失败)
///
public bool IsSuccess => Status == 0x00;
public static BLEResponse FromFrame(BLEFrame frame)
{
var response = new BLEResponse();
if (frame.Data != null && frame.Data.Length > 0)
{
response.Status = frame.Data[0];
// 兼容返回2字节的情况,取剩余字节作为数据
if (frame.Data.Length > 1)
{
response.Data = new byte[frame.Data.Length - 1];
System.Buffer.BlockCopy(frame.Data, 1, response.Data, 0, frame.Data.Length - 1);
}
}
return response;
}
}
///
/// 用户信息
///
[Serializable]
public struct UserInfo
{
public string Username; // 用户名
public byte FingerprintId; // 指纹ID
public bool HasFingerprint; // 是否有指纹
}
///
/// 用户列表响应
/// 命令 0x07 返回:
/// - 文档标准:用户数量(1字节) + 每个用户:用户名(16字节) + 指纹ID(1字节) + 指纹ID有效标志(1字节)
/// - 当前固件:用户数量(2字节,低字节在前) + 每个用户信息
///
[Serializable]
public struct UserListResponse
{
public byte Status; // 响应状态码(0x00=成功)
public ushort UserCount; // 用户数量(兼容1字节和2字节)
public UserInfo[] Users; // 用户列表
///
/// 从数据字节解析
/// 数据格式(不包含状态码,因为BLEResponse已经提取了):
/// - 标准格式:用户数量(1) + [用户名(16) + 指纹ID(1) + 指纹有效标志(1)] * N
/// - 当前固件:用户数量(2) + [用户名(16) + 指纹ID(1) + 指纹有效标志(1)] * N
///
/// 响应状态码
/// 数据内容(不包含状态码)
public static UserListResponse FromBytes(byte status, byte[] data)
{
var response = new UserListResponse();
response.Status = status;
if (data == null || data.Length < 1)
return response;
// 注意:传入的data不包含状态码,因为BLEResponse.FromFrame已经提取了
// 判断用户数量是1字节还是2字节
int userSize = 16 + 1 + 1; // 用户名(16) + 指纹ID(1) + 指纹有效标志(1) = 18字节
// 尝试按1字节解析
byte userCount1Byte = data[0];
int expectedLen1Byte = 1 + userCount1Byte * userSize;
// 尝试按2字节解析(小端序)
ushort userCount2Byte = 0;
if (data.Length >= 2)
{
userCount2Byte = (ushort)(data[0] | (data[1] << 8));
}
int expectedLen2Byte = 2 + userCount2Byte * userSize;
int offset;
if (data.Length == expectedLen1Byte)
{
// 1字节格式
response.UserCount = userCount1Byte;
offset = 1;
}
else if (data.Length == expectedLen2Byte)
{
// 2字节格式
response.UserCount = userCount2Byte;
offset = 2;
}
else
{
// 默认按1字节解析
response.UserCount = userCount1Byte;
offset = 1;
}
if (response.UserCount == 0 || data.Length < offset + response.UserCount * userSize)
{
response.Users = new UserInfo[0];
return response;
}
response.Users = new UserInfo[response.UserCount];
for (int i = 0; i < response.UserCount; i++)
{
// 用户名 (16字节)
byte[] usernameBytes = new byte[16];
Buffer.BlockCopy(data, offset, usernameBytes, 0, 16);
// 去除末尾的0字节
int nameLen = 0;
while (nameLen < 16 && usernameBytes[nameLen] != 0)
nameLen++;
response.Users[i].Username = System.Text.Encoding.ASCII.GetString(usernameBytes, 0, nameLen);
offset += 16;
// 指纹ID
response.Users[i].FingerprintId = data[offset++];
// 指纹ID有效标志
response.Users[i].HasFingerprint = data[offset++] == 0x01;
}
return response;
}
///
/// 是否成功
///
public bool IsSuccess => Status == 0x00;
}
#region 设备设置类结构体 (0x10-0x1F)
///
/// 语言设置
/// 命令 0x10:0x00=英文, 0x01=中文
///
[Serializable]
public struct LanguageSetting
{
public byte Language; // 0x00=英文, 0x01=中文
public bool IsChinese => Language == 0x01;
public bool IsEnglish => Language == 0x00;
public static LanguageSetting FromBytes(byte[] data)
{
var setting = new LanguageSetting();
if (data != null && data.Length > 0)
{
setting.Language = data[0];
}
return setting;
}
public byte[] ToBytes()
{
return new byte[] { Language };
}
}
///
/// 时间设置
/// 命令 0x11:年(1) + 月(1) + 日(1) + 时(1) + 分(1) + 秒(1)
/// 年:相对于2000年的偏移(0-255对应2000-2255年)
///
[Serializable]
public struct TimeSetting
{
public byte Year; // 相对于2000年的偏移
public byte Month; // 1-12
public byte Day; // 1-31
public byte Hour; // 0-23
public byte Minute; // 0-59
public byte Second; // 0-59
public int ActualYear => 2000 + Year;
public static TimeSetting FromBytes(byte[] data)
{
var setting = new TimeSetting();
if (data != null && data.Length >= 6)
{
setting.Year = data[0];
setting.Month = data[1];
setting.Day = data[2];
setting.Hour = data[3];
setting.Minute = data[4];
setting.Second = data[5];
}
return setting;
}
public byte[] ToBytes()
{
return new byte[] { Year, Month, Day, Hour, Minute, Second };
}
public override string ToString()
{
return $"{ActualYear}-{Month:D2}-{Day:D2} {Hour:D2}:{Minute:D2}:{Second:D2}";
}
}
///
/// 定时任务模式
///
public enum ScheduleTaskMode : byte
{
Standby = 0x00, // 待机(用于定时关机)
Scan = 0x01, // 扫描
Sterilize = 0x02 // 消杀
}
///
/// 定时任务
/// 命令 0x12:任务ID(1) + 开关(1) + 开始小时(1) + 开始分钟(1) + 结束小时(1) + 结束分钟(1) + 模式(1) + 重复(1)
///
[Serializable]
public struct ScheduleTask
{
public byte TaskId; // 任务ID:0-4
public bool Enabled; // 开关:true=启用, false=禁用
public byte StartHour; // 开始小时:0-23
public byte StartMinute; // 开始分钟:0-59
public byte EndHour; // 结束小时:0-23
public byte EndMinute; // 结束分钟:0-59
public ScheduleTaskMode Mode; // 模式
public byte Repeat; // 重复:按位表示星期几
// 重复位定义
public const byte REPEAT_MONDAY = 0x01;
public const byte REPEAT_TUESDAY = 0x02;
public const byte REPEAT_WEDNESDAY = 0x04;
public const byte REPEAT_THURSDAY = 0x08;
public const byte REPEAT_FRIDAY = 0x10;
public const byte REPEAT_SATURDAY = 0x20;
public const byte REPEAT_SUNDAY = 0x40;
///
/// 从字节数组解析(写入格式,包含TaskId,8字节)
/// 格式:任务ID(1) + 开关(1) + 开始小时(1) + 开始分钟(1) + 结束小时(1) + 结束分钟(1) + 模式(1) + 重复(1)
///
public static ScheduleTask FromBytes(byte[] data, int offset = 0)
{
var task = new ScheduleTask();
if (data != null && data.Length >= offset + 8)
{
task.TaskId = data[offset];
task.Enabled = data[offset + 1] == 0x01;
task.StartHour = data[offset + 2];
task.StartMinute = data[offset + 3];
task.EndHour = data[offset + 4];
task.EndMinute = data[offset + 5];
task.Mode = (ScheduleTaskMode)data[offset + 6];
task.Repeat = data[offset + 7];
}
return task;
}
///
/// 从字节数组解析(读取格式,不包含TaskId,7字节)
/// 格式:开关(1) + 开始小时(1) + 开始分钟(1) + 结束小时(1) + 结束分钟(1) + 模式(1) + 重复(1)
///
public static ScheduleTask FromBytesRead(byte[] data, int offset, byte taskId)
{
var task = new ScheduleTask();
if (data != null && data.Length >= offset + 7)
{
task.TaskId = taskId; // 读取时TaskId从数组索引推断
task.Enabled = data[offset] == 0x01;
task.StartHour = data[offset + 1];
task.StartMinute = data[offset + 2];
task.EndHour = data[offset + 3];
task.EndMinute = data[offset + 4];
task.Mode = (ScheduleTaskMode)data[offset + 5];
task.Repeat = data[offset + 6];
}
return task;
}
public byte[] ToBytes()
{
return new byte[]
{
TaskId,
Enabled ? (byte)0x01 : (byte)0x00,
StartHour,
StartMinute,
EndHour,
EndMinute,
(byte)Mode,
Repeat
};
}
public bool IsRepeatMonday => (Repeat & REPEAT_MONDAY) != 0;
public bool IsRepeatTuesday => (Repeat & REPEAT_TUESDAY) != 0;
public bool IsRepeatWednesday => (Repeat & REPEAT_WEDNESDAY) != 0;
public bool IsRepeatThursday => (Repeat & REPEAT_THURSDAY) != 0;
public bool IsRepeatFriday => (Repeat & REPEAT_FRIDAY) != 0;
public bool IsRepeatSaturday => (Repeat & REPEAT_SATURDAY) != 0;
public bool IsRepeatSunday => (Repeat & REPEAT_SUNDAY) != 0;
public string GetRepeatString()
{
if (Repeat == 0) return "不重复";
var days = new System.Collections.Generic.List();
if (IsRepeatMonday) days.Add("周一");
if (IsRepeatTuesday) days.Add("周二");
if (IsRepeatWednesday) days.Add("周三");
if (IsRepeatThursday) days.Add("周四");
if (IsRepeatFriday) days.Add("周五");
if (IsRepeatSaturday) days.Add("周六");
if (IsRepeatSunday) days.Add("周日");
return string.Join(",", days);
}
public override string ToString()
{
string modeStr = Mode switch
{
ScheduleTaskMode.Standby => "待机",
ScheduleTaskMode.Scan => "扫描",
ScheduleTaskMode.Sterilize => "消杀",
_ => "未知"
};
return $"任务{TaskId}: {(Enabled ? "启用" : "禁用")} {StartHour:D2}:{StartMinute:D2}-{EndHour:D2}:{EndMinute:D2} {modeStr} [{GetRepeatString()}]";
}
}
///
/// 定时任务列表响应
/// 读取返回格式:每个任务7字节(不包含TaskId)
/// 开关(1) + 开始小时(1) + 开始分钟(1) + 结束小时(1) + 结束分钟(1) + 模式(1) + 重复(1)
///
[Serializable]
public struct ScheduleTaskListResponse
{
public byte Status; // 响应状态码
public ScheduleTask[] Tasks; // 定时任务列表
public static ScheduleTaskListResponse FromBytes(byte status, byte[] data)
{
var response = new ScheduleTaskListResponse();
response.Status = status;
if (data == null || data.Length < 7)
{
response.Tasks = new ScheduleTask[0];
return response;
}
int taskCount = data.Length / 7; // 每个任务7字节(不包含TaskId)
response.Tasks = new ScheduleTask[taskCount];
for (int i = 0; i < taskCount; i++)
{
response.Tasks[i] = ScheduleTask.FromBytesRead(data, i * 7, (byte)i);
}
return response;
}
public bool IsSuccess => Status == 0x00;
}
#endregion
#region 外设控制类结构体 (0x20-0x3F)
///
/// LCD休眠设置
/// 命令 0x20:休眠开关(1) + 休眠时间(1)
/// 休眠开关:0x00=关闭,0x01=开启
/// 休眠时间:0x01=1分钟,0x05=5分钟,0x0A=10分钟,0x1E=30分钟
///
[Serializable]
public struct LCDSleepSetting
{
public bool Enable; // 休眠开关
public byte SleepTime; // 休眠时间
public static LCDSleepSetting FromBytes(byte[] data)
{
var setting = new LCDSleepSetting();
if (data != null && data.Length >= 2)
{
setting.Enable = data[0] == 0x01;
setting.SleepTime = data[1];
}
return setting;
}
public byte[] ToBytes()
{
return new byte[] { Enable ? (byte)0x01 : (byte)0x00, SleepTime };
}
public string GetSleepTimeString()
{
return SleepTime switch
{
0x01 => "1分钟",
0x05 => "5分钟",
0x0A => "10分钟",
0x1E => "30分钟",
_ => "未知"
};
}
}
///
/// LCD亮度设置
/// 命令 0x21:自适应开关(1) + 亮度值(1)
/// 自适应开关:0x00=关闭,0x01=开启
/// 亮度值:10-100%(仅在自适应关闭时有效)
///
[Serializable]
public struct LCDBrightnessSetting
{
public bool AutoBrightness; // 自适应开关
public byte Brightness; // 亮度值(10-100%)
public static LCDBrightnessSetting FromBytes(byte[] data)
{
var setting = new LCDBrightnessSetting();
if (data != null && data.Length >= 2)
{
setting.AutoBrightness = data[0] == 0x01;
setting.Brightness = data[1];
}
return setting;
}
public byte[] ToBytes()
{
return new byte[] { AutoBrightness ? (byte)0x01 : (byte)0x00, Brightness };
}
}
///
/// RGB指示灯效果模式
///
public enum RGBEffectMode : byte
{
Off = 0x00, // 关闭
Solid = 0x01, // 常亮
Blinking = 0x02, // 闪烁
Warning = 0x03 // 警示
}
///
/// RGB指示灯控制
/// 命令 0x22:开关(1) + 红色(1) + 绿色(1) + 蓝色(1) + 效果模式(1)
/// 开关:0x00=关闭,0x01=开启
///
[Serializable]
public struct RGBControl
{
public bool Enable; // 开关
public byte Red; // 红色值(0-255)
public byte Green; // 绿色值(0-255)
public byte Blue; // 蓝色值(0-255)
public RGBEffectMode Effect; // 效果模式
public static RGBControl FromBytes(byte[] data)
{
var control = new RGBControl();
if (data == null || data.Length == 0)
return control;
// 兼容不同格式的数据返回
// 完整格式: 开关(1) + R(1) + G(1) + B(1) + 效果(1) = 5字节
// 简化格式: R(1) + G(1) + B(1) = 3字节 (没有状态码前缀的情况)
// 带状态码: 状态码(1) + [开关 +] R + G + B [+ 效果]
int offset = 0;
// 如果数据长度大于3且第一个字节是0或1,可能是开关字节
if (data.Length >= 5)
{
// 完整格式: 开关 + R + G + B + 效果
control.Enable = data[0] == 0x01;
control.Red = data[1];
control.Green = data[2];
control.Blue = data[3];
control.Effect = (RGBEffectMode)data[4];
}
else if (data.Length >= 4)
{
// 可能是: 开关 + R + G + B (无效果模式)
control.Enable = data[0] == 0x01;
control.Red = data[1];
control.Green = data[2];
control.Blue = data[3];
}
else if (data.Length >= 3)
{
// 简化格式: 只有 R + G + B
control.Enable = true; // 默认开启
control.Red = data[0];
control.Green = data[1];
control.Blue = data[2];
}
return control;
}
public byte[] ToBytes()
{
return new byte[]
{
Enable ? (byte)0x01 : (byte)0x00,
Red,
Green,
Blue,
(byte)Effect
};
}
}
///
/// WIFI控制
/// 命令 0x23:
/// 读操作:返回65字节(开关1字节 + SSID 32字节 + 密码32字节)
/// 写操作:可变长度,至少1字节(开关),可选包含SSID和密码
///
[Serializable]
public struct WIFIControl
{
public bool Enable; // 开关
public string SSID; // SSID
public string Password; // 密码
public static WIFIControl FromBytes(byte[] data)
{
var control = new WIFIControl();
if (data != null && data.Length >= 1)
{
control.Enable = data[0] == 0x01;
if (data.Length >= 33)
{
// SSID (32字节)
byte[] ssidBytes = new byte[32];
Buffer.BlockCopy(data, 1, ssidBytes, 0, 32);
int ssidLen = 0;
while (ssidLen < 32 && ssidBytes[ssidLen] != 0)
ssidLen++;
control.SSID = System.Text.Encoding.ASCII.GetString(ssidBytes, 0, ssidLen);
}
if (data.Length >= 65)
{
// 密码 (32字节)
byte[] passwordBytes = new byte[32];
Buffer.BlockCopy(data, 33, passwordBytes, 0, 32);
int passwordLen = 0;
while (passwordLen < 32 && passwordBytes[passwordLen] != 0)
passwordLen++;
control.Password = System.Text.Encoding.ASCII.GetString(passwordBytes, 0, passwordLen);
}
}
return control;
}
public byte[] ToBytes()
{
// 计算长度:至少1字节(开关)
int length = 1;
if (!string.IsNullOrEmpty(SSID))
length += SSID.Length + 1; // SSID + 结束符
if (!string.IsNullOrEmpty(Password))
length += Password.Length + 1; // 密码 + 结束符
byte[] bytes = new byte[length];
bytes[0] = Enable ? (byte)0x01 : (byte)0x00;
int offset = 1;
if (!string.IsNullOrEmpty(SSID))
{
byte[] ssidBytes = System.Text.Encoding.ASCII.GetBytes(SSID);
int ssidLen = System.Math.Min(ssidBytes.Length, 32);
Buffer.BlockCopy(ssidBytes, 0, bytes, offset, ssidLen);
offset += ssidLen;
bytes[offset++] = 0; // 结束符
}
if (!string.IsNullOrEmpty(Password))
{
byte[] passwordBytes = System.Text.Encoding.ASCII.GetBytes(Password);
int passwordLen = System.Math.Min(passwordBytes.Length, 32);
Buffer.BlockCopy(passwordBytes, 0, bytes, offset, passwordLen);
offset += passwordLen;
bytes[offset++] = 0; // 结束符
}
return bytes;
}
}
///
/// BLE控制
/// 命令 0x24:设置BLE广播名称
///
[Serializable]
public struct BLEControl
{
public string DeviceName; // 设备名称
public static BLEControl FromBytes(byte[] data)
{
var control = new BLEControl();
if (data != null && data.Length > 0)
{
// 去除末尾的0字节
int nameLen = 0;
while (nameLen < data.Length && data[nameLen] != 0)
nameLen++;
control.DeviceName = System.Text.Encoding.ASCII.GetString(data, 0, nameLen);
}
return control;
}
public byte[] ToBytes()
{
byte[] nameBytes = System.Text.Encoding.ASCII.GetBytes(DeviceName ?? "");
byte[] bytes = new byte[nameBytes.Length + 1]; // 名称 + 结束符
Buffer.BlockCopy(nameBytes, 0, bytes, 0, nameBytes.Length);
bytes[nameBytes.Length] = 0; // 结束符
return bytes;
}
}
///
/// 音效类型
///
public enum SoundEffectType : byte
{
Hello = 0x00, // Hello音效
Goodbye = 0x01, // Goodbye音效
Test = 0x02, // Test音效
BIU = 0x03, // BIU音效
Main = 0x04 // Main音效
}
///
/// 多媒体控制
/// 命令 0x25:
/// 读操作:返回5字节(视频录制开关1 + 录制时长1 + 音效开关1 + 音效类型1 + 音量1)
/// 写操作:可变长度,至少1字节(视频录制开关)
///
[Serializable]
public struct MultimediaControl
{
public bool VideoRecordEnable; // 视频录制开关
public byte RecordDuration; // 录制时长(0x03=3秒, 0x05=5秒, 0x0A=10秒)
public bool SoundEnable; // 音效开关
public SoundEffectType SoundType; // 音效类型
public byte Volume; // 音量(0-15)
public static MultimediaControl FromBytes(byte[] data)
{
var control = new MultimediaControl();
if (data != null && data.Length >= 1)
{
control.VideoRecordEnable = data[0] == 0x01;
if (data.Length >= 2)
control.RecordDuration = data[1];
if (data.Length >= 3)
control.SoundEnable = data[2] == 0x01;
if (data.Length >= 4)
control.SoundType = (SoundEffectType)data[3];
if (data.Length >= 5)
control.Volume = data[4];
}
return control;
}
public byte[] ToBytes()
{
// 可变长度,至少1字节
byte[] bytes = new byte[5]; // 完整数据
bytes[0] = VideoRecordEnable ? (byte)0x01 : (byte)0x00;
bytes[1] = RecordDuration;
bytes[2] = SoundEnable ? (byte)0x01 : (byte)0x00;
bytes[3] = (byte)SoundType;
bytes[4] = Volume;
return bytes;
}
public string GetDurationString()
{
return RecordDuration switch
{
0x03 => "3秒",
0x05 => "5秒",
0x0A => "10秒",
_ => "未知"
};
}
public string GetSoundTypeString()
{
return SoundType switch
{
SoundEffectType.Hello => "Hello",
SoundEffectType.Goodbye => "Goodbye",
SoundEffectType.Test => "Test",
SoundEffectType.BIU => "BIU",
SoundEffectType.Main => "Main",
_ => "未知"
};
}
}
///
/// 补光类型
///
public enum FillLightType : byte
{
Infrared = 0x00, // 红外光
White = 0x01 // 白光
}
///
/// 光照强度
///
public enum LightIntensity : byte
{
Low = 0x00, // 低
Medium = 0x01, // 中
High = 0x02 // 高
}
///
/// 补光灯控制
/// 命令 0x26:开关(1) + 补光类型(1) + 光照强度(1)
///
[Serializable]
public struct FillLightControl
{
public bool Enable; // 开关
public FillLightType LightType; // 补光类型
public LightIntensity Intensity; // 光照强度
public static FillLightControl FromBytes(byte[] data)
{
var control = new FillLightControl();
if (data != null && data.Length >= 3)
{
control.Enable = data[0] == 0x01;
control.LightType = (FillLightType)data[1];
control.Intensity = (LightIntensity)data[2];
}
return control;
}
public byte[] ToBytes()
{
return new byte[]
{
Enable ? (byte)0x01 : (byte)0x00,
(byte)LightType,
(byte)Intensity
};
}
public string GetLightTypeString()
{
return LightType == FillLightType.Infrared ? "红外光" : "白光";
}
public string GetIntensityString()
{
return Intensity switch
{
LightIntensity.Low => "低",
LightIntensity.Medium => "中",
LightIntensity.High => "高",
_ => "未知"
};
}
}
///
/// 补光灯连接状态
/// 命令 0x27:返回连接状态(1字节)
/// 0=未连接, 1=已连接
///
[Serializable]
public struct FillLightConnectionStatus
{
public bool IsConnected; // 连接状态
public static FillLightConnectionStatus FromBytes(byte[] data)
{
var status = new FillLightConnectionStatus();
if (data != null && data.Length >= 1)
{
status.IsConnected = data[0] == 0x01;
}
return status;
}
}
///
/// 可见光激光器控制
/// 命令 0x28:开关(1)
/// 开关:0x00=关闭,0x01=开启
///
[Serializable]
public struct VisibleLaserControl
{
public bool Enable; // 开关
public static VisibleLaserControl FromBytes(byte[] data)
{
var control = new VisibleLaserControl();
if (data != null && data.Length >= 1)
{
control.Enable = data[0] == 0x01;
}
return control;
}
public byte[] ToBytes()
{
return new byte[] { Enable ? (byte)0x01 : (byte)0x00 };
}
}
#endregion
#region 害虫消灭控制类结构体 (0x40-0x4F)
///
/// 角度控制
/// 命令 0x40:角度范围(2字节)
/// 数据格式:2字节,小端序,单位:0.1度,范围:1-900(对应0.1度-90度)
///
[Serializable]
public struct AngleControl
{
public ushort AngleRange; // 角度范围(单位:0.1度,范围1-900)
///
/// 实际角度值(度)
///
public float ActualAngle => AngleRange * 0.1f;
///
/// 从字节数组解析(小端序)
///
public static AngleControl FromBytes(byte[] data)
{
var control = new AngleControl();
if (data != null && data.Length >= 2)
{
// 小端序:低字节在前,高字节在后
control.AngleRange = (ushort)(data[0] | (data[1] << 8));
}
return control;
}
///
/// 序列化为字节数组(小端序)
///
public byte[] ToBytes()
{
return new byte[]
{
(byte)(AngleRange & 0xFF), // 低字节
(byte)((AngleRange >> 8) & 0xFF) // 高字节
};
}
///
/// 设置实际角度值
///
/// 角度值(度),范围0.1-90.0
public void SetAngle(float angle)
{
AngleRange = (ushort)Mathf.Round(angle * 10);
// 限制范围
if (AngleRange < 1) AngleRange = 1;
if (AngleRange > 900) AngleRange = 900;
}
public override string ToString()
{
return $"{ActualAngle:F1}度 ({AngleRange})";
}
}
///
/// 距离控制
/// 命令 0x41:检测距离(2字节) + 瞄准距离(2字节)
/// 数据格式:4字节,小端序
///
[Serializable]
public struct DistanceControl
{
public ushort DetectionDistance; // 检测距离(单位:米)
public ushort AimDistance; // 瞄准距离(单位:米)
///
/// 从字节数组解析(小端序)
///
public static DistanceControl FromBytes(byte[] data)
{
var control = new DistanceControl();
if (data != null && data.Length >= 4)
{
// 小端序解析
control.DetectionDistance = (ushort)(data[0] | (data[1] << 8));
control.AimDistance = (ushort)(data[2] | (data[3] << 8));
}
return control;
}
///
/// 序列化为字节数组(小端序)
///
public byte[] ToBytes()
{
return new byte[]
{
(byte)(DetectionDistance & 0xFF),
(byte)((DetectionDistance >> 8) & 0xFF),
(byte)(AimDistance & 0xFF),
(byte)((AimDistance >> 8) & 0xFF)
};
}
public override string ToString()
{
return $"检测距离={DetectionDistance}米, 瞄准距离={AimDistance}米";
}
}
#endregion
#region 安全设置类结构体 (0x50-0x5F)
///
/// 灵敏度等级枚举
///
public enum SensitivityLevel : byte
{
Low = 0x00, // 低灵敏度
Medium = 0x01, // 中灵敏度
High = 0x02 // 高灵敏度
}
///
/// 毫米波雷达设置
/// 命令 0x50:开关(1) + 灵敏度(1) + 安全距离(2)
/// 数据格式:4字节,小端序
///
[Serializable]
public struct MillimeterWaveSetting
{
public bool Enable; // 开关
public SensitivityLevel Sensitivity; // 灵敏度
public ushort SafeDistance; // 安全距离(单位:厘米)
public static MillimeterWaveSetting FromBytes(byte[] data)
{
var setting = new MillimeterWaveSetting();
if (data != null && data.Length >= 4)
{
setting.Enable = data[0] == 0x01;
setting.Sensitivity = (SensitivityLevel)data[1];
setting.SafeDistance = (ushort)(data[2] | (data[3] << 8));
}
return setting;
}
public byte[] ToBytes()
{
return new byte[]
{
(byte)(Enable ? 0x01 : 0x00),
(byte)Sensitivity,
(byte)(SafeDistance & 0xFF),
(byte)((SafeDistance >> 8) & 0xFF)
};
}
public override string ToString()
{
string sensStr = Sensitivity switch
{
SensitivityLevel.Low => "低",
SensitivityLevel.Medium => "中",
SensitivityLevel.High => "高",
_ => "未知"
};
return $"开关={(Enable ? "开启" : "关闭")}, 灵敏度={sensStr}, 安全距离={SafeDistance}cm";
}
}
///
/// 视觉检测设置
/// 命令 0x51:开关(1) + 灵敏度(1)
/// 数据格式:2字节
///
[Serializable]
public struct VisualDetectionSetting
{
public bool Enable; // 开关
public SensitivityLevel Sensitivity; // 灵敏度
public static VisualDetectionSetting FromBytes(byte[] data)
{
var setting = new VisualDetectionSetting();
if (data != null && data.Length >= 2)
{
setting.Enable = data[0] == 0x01;
setting.Sensitivity = (SensitivityLevel)data[1];
}
return setting;
}
public byte[] ToBytes()
{
return new byte[]
{
(byte)(Enable ? 0x01 : 0x00),
(byte)Sensitivity
};
}
public override string ToString()
{
string sensStr = Sensitivity switch
{
SensitivityLevel.Low => "低",
SensitivityLevel.Medium => "中",
SensitivityLevel.High => "高",
_ => "未知"
};
return $"开关={(Enable ? "开启" : "关闭")}, 灵敏度={sensStr}";
}
}
///
/// 加速度传感器设置
/// 命令 0x52:开关(1) + 灵敏度(1) + 振动阈值(1)
/// 数据格式:3字节
///
[Serializable]
public struct AccelerometerSetting
{
public bool Enable; // 开关
public SensitivityLevel Sensitivity; // 灵敏度
public byte VibrationThreshold; // 振动阈值 (1-255)
public static AccelerometerSetting FromBytes(byte[] data)
{
var setting = new AccelerometerSetting();
if (data != null && data.Length >= 3)
{
setting.Enable = data[0] == 0x01;
setting.Sensitivity = (SensitivityLevel)data[1];
setting.VibrationThreshold = data[2];
}
return setting;
}
public byte[] ToBytes()
{
return new byte[]
{
(byte)(Enable ? 0x01 : 0x00),
(byte)Sensitivity,
VibrationThreshold
};
}
public override string ToString()
{
string sensStr = Sensitivity switch
{
SensitivityLevel.Low => "低",
SensitivityLevel.Medium => "中",
SensitivityLevel.High => "高",
_ => "未知"
};
return $"开关={(Enable ? "开启" : "关闭")}, 灵敏度={sensStr}, 振动阈值={VibrationThreshold}";
}
}
///
/// 加速度传感器数据
/// 命令 0x53:X轴(2) + Y轴(2) + Z轴(2)
/// 数据格式:6字节,小端序,有符号整数,单位:mg
///
[Serializable]
public struct AccelerometerData
{
public short X; // X轴加速度 (mg)
public short Y; // Y轴加速度 (mg)
public short Z; // Z轴加速度 (mg)
public static AccelerometerData FromBytes(byte[] data)
{
var accel = new AccelerometerData();
if (data != null && data.Length >= 6)
{
accel.X = (short)(data[0] | (data[1] << 8));
accel.Y = (short)(data[2] | (data[3] << 8));
accel.Z = (short)(data[4] | (data[5] << 8));
}
return accel;
}
public byte[] ToBytes()
{
return new byte[]
{
(byte)(X & 0xFF), (byte)((X >> 8) & 0xFF),
(byte)(Y & 0xFF), (byte)((Y >> 8) & 0xFF),
(byte)(Z & 0xFF), (byte)((Z >> 8) & 0xFF)
};
}
public override string ToString()
{
return $"X={X}mg, Y={Y}mg, Z={Z}mg";
}
}
///
/// 温度监控设置
/// 命令 0x55:告警阈值(1) + 停止阈值(1) + 开关(1)
/// 数据格式:3字节
///
[Serializable]
public struct TemperatureMonitorSetting
{
public byte AlarmThreshold; // 告警阈值 (摄氏度)
public byte StopThreshold; // 停止阈值 (摄氏度)
public bool Enable; // 开关
public static TemperatureMonitorSetting FromBytes(byte[] data)
{
var setting = new TemperatureMonitorSetting();
if (data != null && data.Length >= 3)
{
setting.AlarmThreshold = data[0];
setting.StopThreshold = data[1];
setting.Enable = data[2] == 0x01;
}
return setting;
}
public byte[] ToBytes()
{
return new byte[]
{
AlarmThreshold,
StopThreshold,
(byte)(Enable ? 0x01 : 0x00)
};
}
public override string ToString()
{
return $"开关={(Enable ? "开启" : "关闭")}, 告警阈值={AlarmThreshold}°C, 停止阈值={StopThreshold}°C";
}
}
///
/// 环境变化检测和试射测试
/// 命令 0x56:读操作返回1字节环境变化状态,写操作触发激光试射测试
/// 数据格式:1字节
///
[Serializable]
public struct EnvironmentChangeTest
{
public bool EnvironmentChanged; // 环境变化状态(读操作返回)
public bool TriggerTest; // 触发测试(写操作发送0x01)
public static EnvironmentChangeTest FromBytes(byte[] data)
{
var test = new EnvironmentChangeTest();
if (data != null && data.Length >= 1)
{
test.EnvironmentChanged = data[0] == 0x01;
}
return test;
}
public byte[] ToBytes()
{
return new byte[] { (byte)(TriggerTest ? 0x01 : 0x00) };
}
public override string ToString()
{
return $"环境变化={(EnvironmentChanged ? "发生变化" : "没有变化")}";
}
}
#endregion
#region 状态查询类结构体 (0xA0-0xBF)
///
/// 工作模式枚举
///
public enum WorkMode : byte
{
Standby = 0x00, // 待机模式
Scan = 0x01, // 扫描模式
Eliminate = 0x02 // 消杀模式
}
///
/// 工作模式设置
/// 命令 0xA0:1字节工作模式
///
[Serializable]
public struct WorkModeSetting
{
public WorkMode Mode;
public static WorkModeSetting FromBytes(byte[] data)
{
var setting = new WorkModeSetting();
if (data != null && data.Length >= 1)
{
setting.Mode = (WorkMode)data[0];
}
return setting;
}
public byte[] ToBytes()
{
return new byte[] { (byte)Mode };
}
public override string ToString()
{
string modeStr = Mode switch
{
WorkMode.Standby => "待机",
WorkMode.Scan => "扫描",
WorkMode.Eliminate => "消杀",
_ => "未知"
};
return $"工作模式={modeStr}";
}
}
///
/// 硬件状态
/// 命令 0xA1:返回7字节硬件错误状态
///
[Serializable]
public struct HardwareStatus
{
public byte TemperatureError; // 温度错误 (0=无, 1=过高, 2=过低)
public byte MotorError; // 电机错误 (0=无, 1=温度, 2=反馈)
public byte LaserError; // 激光器错误 (0=无, 1=温度, 2=电压低, 3=电压高)
public byte VisionError; // 视觉错误 (0=无, 1=通信)
public byte AccelerometerError; // 加速度错误 (0=无, 1=通信)
public byte MillimeterWaveError; // 毫米波雷达错误 (0=无, 1=通信)
public byte LidarError; // 激光雷达错误 (0=无, 1=通信)
public static HardwareStatus FromBytes(byte[] data)
{
var status = new HardwareStatus();
if (data != null && data.Length >= 7)
{
status.TemperatureError = data[0];
status.MotorError = data[1];
status.LaserError = data[2];
status.VisionError = data[3];
status.AccelerometerError = data[4];
status.MillimeterWaveError = data[5];
status.LidarError = data[6];
}
return status;
}
public bool HasError => TemperatureError != 0 || MotorError != 0 || LaserError != 0 ||
VisionError != 0 || AccelerometerError != 0 || MillimeterWaveError != 0 || LidarError != 0;
public override string ToString()
{
if (!HasError) return "所有硬件正常";
var errors = new System.Collections.Generic.List();
if (TemperatureError != 0) errors.Add($"温度错误({TemperatureError})");
if (MotorError != 0) errors.Add($"电机错误({MotorError})");
if (LaserError != 0) errors.Add($"激光器错误({LaserError})");
if (VisionError != 0) errors.Add($"视觉错误({VisionError})");
if (AccelerometerError != 0) errors.Add($"加速度错误({AccelerometerError})");
if (MillimeterWaveError != 0) errors.Add($"毫米波错误({MillimeterWaveError})");
if (LidarError != 0) errors.Add($"激光雷达错误({LidarError})");
return string.Join(", ", errors);
}
}
///
/// 设备信息
/// 命令 0xA2:返回76字节设备信息
/// 数据格式(76字节,小端序):
/// 字节0-15:设备序列号(16字节,ASCII编码)
/// 字节16-31:设备型号(16字节,ASCII编码)
/// 字节32-47:设备ID(16字节,ASCII编码)
/// 字节48-53:BLE MAC地址(6字节)
/// 字节54-59:WiFi MAC地址(6字节)
/// 字节60-63:固件版本(uint32_t,4字节小端序)
/// 字节64-67:硬件版本(uint32_t,4字节小端序)
/// 字节68-71:OTA版本(uint32_t,4字节小端序)
///
[Serializable]
public struct DeviceInfo
{
public byte[] SerialNumber; // 设备序列号 (16字节)
public byte[] DeviceModel; // 设备型号 (16字节)
public byte[] DeviceId; // 设备ID (16字节)
public byte[] BleMacAddress; // BLE MAC地址 (6字节)
public byte[] WifiMacAddress; // WiFi MAC地址 (6字节)
public uint FirmwareVersion; // 固件版本 (4字节)
public uint HardwareVersion; // 硬件版本 (4字节)
public uint OtaVersion; // OTA版本 (4字节)
public static DeviceInfo FromBytes(byte[] data)
{
var info = new DeviceInfo();
// 始终初始化数组,避免null引用
info.SerialNumber = new byte[16];
info.DeviceModel = new byte[16];
info.DeviceId = new byte[16];
info.BleMacAddress = new byte[6];
info.WifiMacAddress = new byte[6];
if (data != null)
{
// 字节0-15:设备序列号
if (data.Length >= 16)
System.Buffer.BlockCopy(data, 0, info.SerialNumber, 0, 16);
// 字节16-31:设备型号
if (data.Length >= 32)
System.Buffer.BlockCopy(data, 16, info.DeviceModel, 0, 16);
// 字节32-47:设备ID
if (data.Length >= 48)
System.Buffer.BlockCopy(data, 32, info.DeviceId, 0, 16);
// 字节48-53:BLE MAC地址
if (data.Length >= 54)
System.Buffer.BlockCopy(data, 48, info.BleMacAddress, 0, 6);
// 字节54-59:WiFi MAC地址
if (data.Length >= 60)
System.Buffer.BlockCopy(data, 54, info.WifiMacAddress, 0, 6);
// 字节60-63:固件版本(4字节小端序,每位代表一个数字,如 1.0.2.3)
if (data.Length >= 64)
info.FirmwareVersion = BitConverter.ToUInt32(data, 60);
// 字节64-67:硬件版本(4字节小端序,每位代表一个数字,如 0.2.0.0)
if (data.Length >= 68)
info.HardwareVersion = BitConverter.ToUInt32(data, 64);
// 字节68-71:OTA版本(4字节小端序,每位代表一个数字)
if (data.Length >= 72)
info.OtaVersion = BitConverter.ToUInt32(data, 68);
}
return info;
}
public string GetSerialNumberString() => SerialNumber != null ? System.Text.Encoding.ASCII.GetString(SerialNumber).TrimEnd('\0') : "N/A";
public string GetDeviceModelString() => DeviceModel != null ? System.Text.Encoding.ASCII.GetString(DeviceModel).TrimEnd('\0') : "N/A";
public string GetDeviceIdString() => DeviceId != null ? System.Text.Encoding.ASCII.GetString(DeviceId).TrimEnd('\0') : "N/A";
public string GetBleMacAddressString() => BleMacAddress != null ? BitConverter.ToString(BleMacAddress).Replace("-", ":") : "N/A";
public string GetWifiMacAddressString() => WifiMacAddress != null ? BitConverter.ToString(WifiMacAddress).Replace("-", ":") : "N/A";
///
/// 将版本号转换为 x.x.x.x 格式字符串
///
private string GetVersionString(uint version)
{
byte v1 = (byte)((version >> 24) & 0xFF);
byte v2 = (byte)((version >> 16) & 0xFF);
byte v3 = (byte)((version >> 8) & 0xFF);
byte v4 = (byte)(version & 0xFF);
return $"{v1}.{v2}.{v3}.{v4}";
}
public string GetFirmwareVersionString() => GetVersionString(FirmwareVersion);
public string GetHardwareVersionString() => GetVersionString(HardwareVersion);
public string GetOtaVersionString() => GetVersionString(OtaVersion);
public override string ToString()
{
return $"序列号={GetSerialNumberString()}, 型号={GetDeviceModelString()}, ID={GetDeviceIdString()}, BLE_MAC={GetBleMacAddressString()}, WiFi_MAC={GetWifiMacAddressString()}, 固件={GetFirmwareVersionString()}, 硬件={GetHardwareVersionString()}, OTA={GetOtaVersionString()}";
}
}
///
/// 设备信息写操作
/// 命令 0xA2 写操作:接收56字节设备信息
/// 数据格式(56字节,小端序):
/// 字节0-15:设备序列号(16字节,ASCII编码)
/// 字节16-31:设备型号(16字节,ASCII编码)
/// 字节32-47:设备ID(16字节,ASCII编码)
/// 字节48-51:固件版本(uint32_t,4字节小端序)
/// 字节52-55:硬件版本(uint32_t,4字节小端序)
///
[Serializable]
public struct DeviceInfoWrite
{
public byte[] SerialNumber; // 设备序列号 (16字节)
public byte[] DeviceModel; // 设备型号 (16字节)
public byte[] DeviceId; // 设备ID (16字节)
public uint FirmwareVersion; // 固件版本 (4字节)
public uint HardwareVersion; // 硬件版本 (4字节)
public DeviceInfoWrite(string serialNumber = "", string deviceModel = "", string deviceId = "", uint firmwareVersion = 0, uint hardwareVersion = 0)
{
SerialNumber = new byte[16];
DeviceModel = new byte[16];
DeviceId = new byte[16];
// 填充字符串数据
if (!string.IsNullOrEmpty(serialNumber))
{
byte[] serialBytes = System.Text.Encoding.ASCII.GetBytes(serialNumber);
System.Buffer.BlockCopy(serialBytes, 0, SerialNumber, 0, Mathf.Min(serialBytes.Length, 16));
}
if (!string.IsNullOrEmpty(deviceModel))
{
byte[] modelBytes = System.Text.Encoding.ASCII.GetBytes(deviceModel);
System.Buffer.BlockCopy(modelBytes, 0, DeviceModel, 0, Mathf.Min(modelBytes.Length, 16));
}
if (!string.IsNullOrEmpty(deviceId))
{
byte[] idBytes = System.Text.Encoding.ASCII.GetBytes(deviceId);
System.Buffer.BlockCopy(idBytes, 0, DeviceId, 0, Mathf.Min(idBytes.Length, 16));
}
FirmwareVersion = firmwareVersion;
HardwareVersion = hardwareVersion;
}
public byte[] ToBytes()
{
byte[] data = new byte[56];
// 字节0-15:设备序列号
System.Buffer.BlockCopy(SerialNumber, 0, data, 0, 16);
// 字节16-31:设备型号
System.Buffer.BlockCopy(DeviceModel, 0, data, 16, 16);
// 字节32-47:设备ID
System.Buffer.BlockCopy(DeviceId, 0, data, 32, 16);
// 字节48-51:固件版本(小端序)
data[48] = (byte)(FirmwareVersion & 0xFF);
data[49] = (byte)((FirmwareVersion >> 8) & 0xFF);
data[50] = (byte)((FirmwareVersion >> 16) & 0xFF);
data[51] = (byte)((FirmwareVersion >> 24) & 0xFF);
// 字节52-55:硬件版本(小端序)
data[52] = (byte)(HardwareVersion & 0xFF);
data[53] = (byte)((HardwareVersion >> 8) & 0xFF);
data[54] = (byte)((HardwareVersion >> 16) & 0xFF);
data[55] = (byte)((HardwareVersion >> 24) & 0xFF);
return data;
}
public string GetSerialNumberString() => System.Text.Encoding.ASCII.GetString(SerialNumber).TrimEnd('\0');
public string GetDeviceModelString() => System.Text.Encoding.ASCII.GetString(DeviceModel).TrimEnd('\0');
public string GetDeviceIdString() => System.Text.Encoding.ASCII.GetString(DeviceId).TrimEnd('\0');
public override string ToString()
{
return $"序列号={GetSerialNumberString()}, 型号={GetDeviceModelString()}, ID={GetDeviceIdString()}, 固件版本={FirmwareVersion}, 硬件版本={HardwareVersion}";
}
}
///
/// 统计数据
/// 命令 0xA3:返回32字节统计数据
///
[Serializable]
public struct StatisticsData
{
public uint EliminateCount; // 灭虫数量
public uint TotalWorkTime; // 总工作时间(秒)
public uint TodayWorkTime; // 今日工作时间(秒)
public uint ScanCount; // 扫描次数
public uint LaserFireCount; // 激光发射次数
public uint MosquitoDataCount; // 蚊虫数据数量
public uint DatalogCount; // datalog日志数量
public static StatisticsData FromBytes(byte[] data)
{
var stats = new StatisticsData();
if (data != null && data.Length >= 28)
{
stats.EliminateCount = (uint)(data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24));
stats.TotalWorkTime = (uint)(data[4] | (data[5] << 8) | (data[6] << 16) | (data[7] << 24));
stats.TodayWorkTime = (uint)(data[8] | (data[9] << 8) | (data[10] << 16) | (data[11] << 24));
stats.ScanCount = (uint)(data[12] | (data[13] << 8) | (data[14] << 16) | (data[15] << 24));
stats.LaserFireCount = (uint)(data[16] | (data[17] << 8) | (data[18] << 16) | (data[19] << 24));
stats.MosquitoDataCount = (uint)(data[20] | (data[21] << 8) | (data[22] << 16) | (data[23] << 24));
stats.DatalogCount = (uint)(data[24] | (data[25] << 8) | (data[26] << 16) | (data[27] << 24));
}
return stats;
}
public override string ToString()
{
return $"灭虫={EliminateCount}, 总工时={TotalWorkTime/3600}h, 今日={TodayWorkTime/60}min, 扫描={ScanCount}, 激光={LaserFireCount}";
}
}
///
/// 蚊虫数据查询
/// 命令 0xA4:写操作接收8字节(start_index + end_index)
///
[Serializable]
public struct MosquitoDataQuery
{
public uint StartIndex; // 起始索引
public uint EndIndex; // 结束索引
public static MosquitoDataQuery FromBytes(byte[] data)
{
var query = new MosquitoDataQuery();
if (data != null && data.Length >= 8)
{
query.StartIndex = (uint)(data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24));
query.EndIndex = (uint)(data[4] | (data[5] << 8) | (data[6] << 16) | (data[7] << 24));
}
return query;
}
public byte[] ToBytes()
{
return new byte[]
{
(byte)(StartIndex & 0xFF), (byte)((StartIndex >> 8) & 0xFF),
(byte)((StartIndex >> 16) & 0xFF), (byte)((StartIndex >> 24) & 0xFF),
(byte)(EndIndex & 0xFF), (byte)((EndIndex >> 8) & 0xFF),
(byte)((EndIndex >> 16) & 0xFF), (byte)((EndIndex >> 24) & 0xFF)
};
}
public override string ToString()
{
return $"索引范围[{StartIndex}-{EndIndex}]";
}
}
///
/// 传感器数据
/// 命令 0xA5:返回10字节传感器数据
///
[Serializable]
public struct SensorData
{
public ushort Temperature; // 温度 (单位: 0.1°C)
public short AccelX; // X轴加速度 (mg)
public short AccelY; // Y轴加速度 (mg)
public short AccelZ; // Z轴加速度 (mg)
public ushort CapacitorVoltage; // 电容电压 (单位: 0.01V)
public static SensorData FromBytes(byte[] data)
{
var sensor = new SensorData();
if (data != null && data.Length >= 10)
{
sensor.Temperature = (ushort)(data[0] | (data[1] << 8));
sensor.AccelX = (short)(data[2] | (data[3] << 8));
sensor.AccelY = (short)(data[4] | (data[5] << 8));
sensor.AccelZ = (short)(data[6] | (data[7] << 8));
sensor.CapacitorVoltage = (ushort)(data[8] | (data[9] << 8));
}
return sensor;
}
public float GetTemperatureCelsius() => Temperature * 0.1f;
public float GetCapacitorVoltage() => CapacitorVoltage * 0.01f;
public override string ToString()
{
return $"温度={GetTemperatureCelsius():F1}°C, 加速度=({AccelX},{AccelY},{AccelZ})mg, 电容={GetCapacitorVoltage():F2}V";
}
}
///
/// 蚊虫数据
/// 通知类型 0x0B:22字节 = 索引(4字节) + 蚊虫数据(18字节)
/// 蚊虫数据结构:角度(4字节,*1000度) + 距离(2字节,毫米) + 大小(2字节,毫米) + 时间(6字节) + 保留(4字节)
///
[Serializable]
public struct MosquitoData
{
public uint Index; // 数据索引
public int Angle; // 角度 (*1000度,支持负数,如90000表示90.000度)
public ushort Distance; // 距离 (毫米)
public ushort Size; // 大小 (毫米)
public byte Year; // 年 (减去2000后的值)
public byte Month; // 月
public byte Day; // 日
public byte Hour; // 时
public byte Minute; // 分
public byte Second; // 秒
public byte[] Reserved; // 保留字节 (4字节)
public static MosquitoData FromBytes(byte[] data)
{
var mosquito = new MosquitoData();
if (data != null && data.Length >= 22)
{
// 索引 (4字节小端序)
mosquito.Index = (uint)(data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24));
// 角度 (4字节有符号整数,小端序)
mosquito.Angle = data[4] | (data[5] << 8) | (data[6] << 16) | (data[7] << 24);
// 距离 (2字节小端序)
mosquito.Distance = (ushort)(data[8] | (data[9] << 8));
// 大小 (2字节小端序)
mosquito.Size = (ushort)(data[10] | (data[11] << 8));
// 时间 (6字节)
mosquito.Year = data[12];
mosquito.Month = data[13];
mosquito.Day = data[14];
mosquito.Hour = data[15];
mosquito.Minute = data[16];
mosquito.Second = data[17];
// 保留字节 (4字节)
mosquito.Reserved = new byte[4];
System.Buffer.BlockCopy(data, 18, mosquito.Reserved, 0, 4);
}
return mosquito;
}
///
/// 获取实际角度值(度)
///
public float GetActualAngle() => Angle / 1000.0f;
///
/// 获取完整年份
///
public int GetFullYear() => 2000 + Year;
///
/// 获取时间字符串
///
public string GetTimeString() => $"{GetFullYear():D4}-{Month:D2}-{Day:D2} {Hour:D2}:{Minute:D2}:{Second:D2}";
public override string ToString()
{
return $"[{Index}] 角度={GetActualAngle():F3}°, 距离={Distance}mm, 大小={Size}mm, 时间={GetTimeString()}";
}
}
///
/// 蚊虫数据数量查询结果
/// 命令 0xA4 读操作返回:4字节(设备本地数据条数,小端序)
///
[Serializable]
public struct MosquitoDataCount
{
public uint Count; // 数据条数
public static MosquitoDataCount FromBytes(byte[] data)
{
var count = new MosquitoDataCount();
if (data != null && data.Length >= 4)
{
count.Count = (uint)(data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24));
}
return count;
}
public override string ToString()
{
return $"蚊虫数据条数: {Count}";
}
}
#endregion
}