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 }