using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace MegaRobo.C00225155.ControlDevices { public class HighTemperatureUartHCM { #region 协议常量定义 private const byte ST = 0x5E; // 帧头 private const byte HELLO = 0x01; // 握手指令 private const byte RDTEMP = 0x08; // 读取温度 private const byte RDADC = 0x09; // 读取传感器AD private const byte RDEE = 0x11; // 读取EEPROM private const byte WREE = 0x12; // 写入EEPROM private const byte Calb = 0x15; // 校准温度 private const byte SETHOTTEMP = 0x16; // 加热温度控制 private const byte RESPONSE_FLAG = 0x80; // 上传指令标志 #endregion #region 校验和计算(协议第6点:前n-1字节之和的低8位) /// /// 计算帧的校验和(核心:前length字节之和的低8位) /// private static byte CalculateChecksum(byte[] data, int length) { if (data == null || length <= 0 || length > data.Length) throw new ArgumentException("校验和计算参数无效"); int sum = 0; for (int i = 0; i < length; i++) sum += data[i]; return (byte)(sum & 0xFF); // 取低8位,确保1字节 } #endregion #region 帧间隔控制(协议1.1g:帧与帧间隔>2ms,用Thread.Sleep确保阻塞) /// /// 帧间隔时间(预留3ms余量,确保符合协议要求) /// private static void AddFrameInterval() { Task.Delay(3); } #endregion #region 下传指令构建(PC→主控板,严格遵循协议指令格式) /// /// 握手指令(系统开机必发) /// public static byte[] SendHello(byte deviceId = 0) { byte[] frame = new byte[5]; // 协议固定帧长5字节 // 帧结构:ST → HELLO(0x01) → 设备ID → LTH=5 → 校验和 frame[0] = ST; frame[1] = HELLO; // 移除序号或运算,CMD为固定指令码(协议3.1) frame[2] = deviceId; frame[3] = 5; // LTH固定为5 frame[4] = CalculateChecksum(frame, 4); // 前4字节求和 AddFrameInterval(); return frame; } /// /// 读取温度指令 /// public static byte[] SendReadTempRequest(byte deviceId = 0) { byte[] frame = new byte[5]; // 协议固定帧长5字节 // 帧结构:ST → RDTEMP(0x08) → 设备ID → LTH=5 → 校验和 frame[0] = ST; frame[1] = RDTEMP; frame[2] = deviceId; frame[3] = 5; frame[4] = CalculateChecksum(frame, 4); AddFrameInterval(); return frame; } /// /// 读取传感器AD指令 /// public static byte[] SendReadAdcRequest(byte deviceId = 0) { byte[] frame = new byte[5]; // 协议固定帧长5字节 // 帧结构:ST → RDADC(0x09) → 设备ID → LTH=5 → 校验和 frame[0] = ST; frame[1] = RDADC; frame[2] = deviceId; frame[3] = 5; frame[4] = CalculateChecksum(frame, 4); AddFrameInterval(); return frame; } /// /// 读取EEPROM指令 /// public static byte[] SendReadEepromRequest(byte addrHigh, byte addrMid, byte addrLow, int readLength, byte deviceId = 0) { if (readLength < 1) throw new ArgumentOutOfRangeException(nameof(readLength), "读取长度必须≥1"); int frameLength = 9 + readLength; // 帧长=9(固定段)+ 读取长度(协议3.4) byte[] frame = new byte[frameLength]; // 帧结构:ST → RDEE(0x11) → 设备ID → LTH=N → 地址高→中→低 → 读取长度 → 校验和 frame[0] = ST; frame[1] = RDEE; frame[2] = deviceId; frame[3] = (byte)frameLength; frame[4] = addrHigh; frame[5] = addrMid; frame[6] = addrLow; frame[7] = (byte)readLength; frame[frameLength - 1] = CalculateChecksum(frame, frameLength - 1); AddFrameInterval(); return frame; } /// /// 写入EEPROM指令 /// public static byte[] SendWriteEepromRequest(byte addrHigh, byte addrMid, byte addrLow, byte[] writeData, byte deviceId = 0) { if (writeData == null || writeData.Length == 0) throw new ArgumentException("写入数据不能为空", nameof(writeData)); int frameLength = 9 + writeData.Length; // 帧长=9(固定段)+ 数据长度(协议3.5) byte[] frame = new byte[frameLength]; // 帧结构:ST → WREE(0x12) → 设备ID → LTH=N → 地址高→中→低 → 写入数据 → 校验和 frame[0] = ST; frame[1] = WREE; frame[2] = deviceId; frame[3] = (byte)frameLength; frame[4] = addrHigh; frame[5] = addrMid; frame[6] = addrLow; Array.Copy(writeData, 0, frame, 7, writeData.Length); frame[frameLength - 1] = CalculateChecksum(frame, frameLength - 1); AddFrameInterval(); return frame; } /// /// 校准温度指令(设置B值=(实际温度-目标温度)×100) /// public static byte[] SendCalibrateTempRequest(int calibBValue, byte deviceId = 0) { if (calibBValue < short.MinValue || calibBValue > short.MaxValue) throw new ArgumentOutOfRangeException(nameof(calibBValue), "校准B值必须在-32768~32767之间"); byte[] frame = new byte[7]; // 协议固定帧长7字节 short bParam = (short)calibBValue; // 16位有符号数(支持负值) // 帧结构:ST → Calb(0x15) → 设备ID → LTH=7 → 参数1(高8) → 参数2(低8) → 校验和 frame[0] = ST; frame[1] = Calb; frame[2] = deviceId; frame[3] = 7; frame[4] = (byte)(bParam >> 8); // 高位在前(协议3.6) frame[5] = (byte)(bParam & 0xFF); frame[6] = CalculateChecksum(frame, 6); AddFrameInterval(); return frame; } /// /// 加热温度控制(设置目标温度或释放控制) /// public static byte[] SendSetHotTemp(double targetTemp, byte deviceId = 0, bool releaseControl = false) { byte[] frame = new byte[7]; // 协议固定帧长7字节 ushort tempParam; if (releaseControl) { tempParam = 0xFFFF; // 协议约定:释放控制=0xFFFF(关断功率) } else { // 协议范围:室温~100℃(对应室温×100~10000),室温暂以20℃为参考 if (targetTemp < 20.0 || targetTemp > 100.0) throw new ArgumentOutOfRangeException(nameof(targetTemp), "温度必须在[20.0, 100.0]℃之间(符合协议室温要求)"); tempParam = (ushort)(targetTemp * 100); // 放大100倍(协议3.7) } // 帧结构:ST → SETHOTTEMP(0x16) → 设备ID → LTH=7 → 参数1(高8) → 参数2(低8) → 校验和 frame[0] = ST; frame[1] = SETHOTTEMP; frame[2] = deviceId; frame[3] = 7; frame[4] = (byte)(tempParam >> 8); // 高位在前(协议3.7) frame[5] = (byte)(tempParam & 0xFF); frame[6] = CalculateChecksum(frame, 6); AddFrameInterval(); return frame; } #endregion #region 指令解析 /// /// 解析主控板返回的帧数据(入口方法) /// public static ParseResult ParseReceivedFrame(byte[] receivedFrame) { // 基础校验:帧非空、最短帧≥6字节(上传指令最短为握手响应6字节) if (receivedFrame == null || receivedFrame.Length < 6) return new ParseResult(false, "无效帧:数据为空或长度不足", null); // 1. 校验帧头 if (receivedFrame[0] != ST) return new ParseResult(false, $"帧头错误:实际0x{receivedFrame[0]:X2},期望0x{ST:X2}", null); // 2. 提取核心字段 byte cmdWithFlag = receivedFrame[1]; byte deviceId = receivedFrame[2]; byte frameLength = receivedFrame[3]; byte checksum = receivedFrame[frameLength - 1]; // 3. 校验帧长(实际长度=帧长字段) if (receivedFrame.Length != frameLength) return new ParseResult(false, $"帧长不匹配:实际{receivedFrame.Length}字节,期望{frameLength}字节", null); // 4. 校验和验证 byte calculatedChecksum = CalculateChecksum(receivedFrame, frameLength - 1); if (calculatedChecksum != checksum) return new ParseResult(false, $"校验和错误:实际0x{checksum:X2},计算0x{calculatedChecksum:X2}", null); // 5. 去除响应标志,获取原始指令码 byte originalCmd = (byte)(cmdWithFlag & ~RESPONSE_FLAG); // 6. 按指令类型分发解析 switch (originalCmd) { case HELLO: return ParseHelloResponse(receivedFrame, deviceId); case RDTEMP: return ParseReadTempResponse(receivedFrame, deviceId); case RDADC: return ParseReadAdcResponse(receivedFrame, deviceId); case RDEE: return ParseReadEepromResponse(receivedFrame, deviceId); case WREE: return ParseWriteEepromResponse(receivedFrame, deviceId); case Calb: return ParseCalibrateTempResponse(receivedFrame, deviceId); case SETHOTTEMP: return ParseSetHotTempResponse(receivedFrame, deviceId); default: return new ParseResult(false, $"未知指令:0x{originalCmd:X2}", null); } } #region 各指令解析实现(严格对齐协议响应格式) /// /// 解析握手响应 /// private static ParseResult ParseHelloResponse(byte[] frame, byte deviceId) { if (frame.Length != 6) // 协议固定响应帧长6字节 return new ParseResult(false, "握手响应帧长错误(需6字节)", null); byte status = frame[4]; // 状态位(协议3.1) var data = new Dictionary { { "设备ID", deviceId }, { "状态码", status }, { "状态描述", status == 0 ? "握手成功" : $"握手失败(错误码:{status})" } }; return new ParseResult(true, "握手响应解析成功", data); } /// /// 解析读取温度响应 /// private static ParseResult ParseReadTempResponse(byte[] frame, byte deviceId) { if (frame.Length != 0x0D) // 协议固定响应帧长13字节(0x0D) return new ParseResult(false, $"读取温度响应帧长错误:需13字节,实际{frame.Length}字节", null); // 提取数据:参数1(设置温度)、参数2(实际温度)、参数3(预留)、标志位、状态位(协议3.2) ushort setTempParam = (ushort)((frame[4] << 8) | frame[5]); // 高位在前 ushort actualTempParam = (ushort)((frame[6] << 8) | frame[7]); // 高位在前 byte flag = frame[10]; // 预留标志位(NC) byte status = frame[11]; // 转换为实际温度(除以100,精度0.1℃) double setTemp = setTempParam / 100.0; double actualTemp = actualTempParam / 100.0; var data = new Dictionary { { "设备ID", deviceId }, { "状态码", status }, { "状态描述", status == 0 ? "读取温度成功" : $"读取失败(错误码:{status})" }, { "设置温度(℃)", setTemp }, { "实际温度(℃)", actualTemp }, { "预留标志位", flag }, { "原始设置温度参数", $"0x{setTempParam:X4}" }, { "原始实际温度参数", $"0x{actualTempParam:X4}" } }; return new ParseResult(true, "读取温度响应解析成功", data); } /// /// 解析读取AD响应 /// private static ParseResult ParseReadAdcResponse(byte[] frame, byte deviceId) { if (frame.Length != 0x0A) // 协议固定响应帧长10字节(0x0A),修正原13字节错误 return new ParseResult(false, $"读取AD响应帧长错误:需10字节,实际{frame.Length}字节", null); // 提取数据:AD值(5-6字节)、预留(7-8字节)、状态位(9字节)(协议3.3) ushort adcValue = (ushort)((frame[4] << 8) | frame[5]); // 高位在前 byte status = frame[8]; var data = new Dictionary { { "设备ID", deviceId }, { "状态码", status }, { "状态描述", status == 0 ? "读取AD成功" : $"读取失败(错误码:{status})" }, { "AD值", adcValue }, { "原始AD参数", $"0x{adcValue:X4}" } }; return new ParseResult(true, "读取AD响应解析成功", data); } /// /// 解析读取EEPROM响应 /// private static ParseResult ParseReadEepromResponse(byte[] frame, byte deviceId) { if (frame.Length < 6) // 协议响应帧长≥6字节 return new ParseResult(false, "读取EEPROM响应帧长不足(需≥6字节)", null); // 提取数据:参数1~n(5到N-2字节)、状态位(N-2字节)(协议3.4) int dataLength = frame.Length - 6; // 数据字节数=N-6 byte[] eepromData = new byte[dataLength]; Array.Copy(frame, 4, eepromData, 0, dataLength); byte status = frame[frame.Length - 2]; var data = new Dictionary { { "设备ID", deviceId }, { "状态码", status }, { "状态描述", status == 0 ? "读取EEPROM成功" : $"读取失败(错误码:{status})" }, { "读取数据长度(字节)", dataLength }, { "读取数据(十六进制)", BitConverter.ToString(eepromData).Replace("-", " ") }, { "原始数据数组", eepromData } }; return new ParseResult(true, "读取EEPROM响应解析成功", data); } /// /// 解析写入EEPROM响应 /// private static ParseResult ParseWriteEepromResponse(byte[] frame, byte deviceId) { if (frame.Length != 6) // 协议固定响应帧长6字节 return new ParseResult(false, "写入EEPROM响应帧长错误(需6字节)", null); byte status = frame[4]; // 状态位(协议3.5) var data = new Dictionary { { "设备ID", deviceId }, { "状态码", status }, { "状态描述", status == 0 ? "写入EEPROM成功" : $"写入失败(错误码:{status})" } }; return new ParseResult(true, "写入EEPROM响应解析成功", data); } /// /// 解析校准温度响应 /// private static ParseResult ParseCalibrateTempResponse(byte[] frame, byte deviceId) { if (frame.Length != 6) // 协议固定响应帧长6字节 return new ParseResult(false, "校准温度响应帧长错误(需6字节)", null); byte status = frame[4]; // 状态位(协议3.6) var data = new Dictionary { { "设备ID", deviceId }, { "状态码", status }, { "状态描述", status == 0 ? "温度校准成功" : $"校准失败(错误码:{status})" } }; return new ParseResult(true, "校准温度响应解析成功", data); } /// /// 解析加热温度控制响应 /// private static ParseResult ParseSetHotTempResponse(byte[] frame, byte deviceId) { if (frame.Length != 6) // 协议固定响应帧长6字节 return new ParseResult(false, "加热温度响应帧长错误(需6字节)", null); byte status = frame[4]; // 状态位(协议3.7) var data = new Dictionary { { "设备ID", deviceId }, { "状态码", status }, { "状态描述", status == 0 ? "加热温度指令接收成功" : $"接收失败(错误码:{status})" } }; return new ParseResult(true, "加热温度响应解析成功", data); } #endregion #endregion #region 预留CRC16校验(协议未使用,保留以备扩展) public static byte[] CRC16(byte[] data) { int len = data.Length; if (len <= 0) return new byte[] { 0, 0 }; ushort crc = 0xFFFF; for (int i = 0; i < len; i++) { crc = (ushort)(crc ^ data[i]); for (int j = 0; j < 8; j++) { crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1); } } byte hi = (byte)((crc & 0xFF00) >> 8); byte lo = (byte)(crc & 0x00FF); return BitConverter.IsLittleEndian ? new byte[] { lo, hi } : new byte[] { hi, lo }; } #endregion } }