C00225155-02/C00225155/MegaRobo.C00225155/MegaRobo.C00225155.ControlD.../TemperatureControl/LowTemperatureUartHCM.cs

853 lines
37 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

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

using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices
{
/// <summary>
/// 高温温度模块 485 uart 定制HCM通信协议封装类PC→主控板指令构建 + 主控板→PC指令解析
/// </summary>
public static class LowTemperatureUartHCM
{
#region
private const byte ST = 0x5E; // 帧头
private const byte HELLO = 0x01; // 握手指令
private const byte STATUS = 0x05; // 读取报错状态
private const byte SETTEMP = 0x06; // 制冷温度控制
private const byte MOTOR = 0x07; // 电机控制
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 RDMotSpeed = 0x13; // 获取实际转速
private const byte RDIOStatus = 0x14; // 获取开关盖状态
private const byte Calb = 0x15; // 校准温度
private const byte SETHOTTEMP = 0x16; // 加热温度控制(补充遗漏指令)
private const byte RelayControl = 0x17; // 电磁阀控制(补充遗漏指令)
private const byte RESPONSE_FLAG = 0x80; // 上传指令标志(指令码 | 0x80
#endregion
#region
private static byte _cmdSequence = 0; // 指令序号0-255自动溢出
private static readonly object _lock = new object();// 线程安全锁(避免多线程序号混乱)
#endregion
#region 6n-18
/// <summary>
/// 计算帧的校验和核心前length字节之和的低8位
/// </summary>
/// <param name="data">待计算的帧数据</param>
/// <param name="length">参与计算的字节数前n-1字节</param>
/// <returns>校验和字节</returns>
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 线
/// <summary>
/// 指令序号自增0-255自动溢出确保除溢出外无重复指令
/// </summary>
private static void IncrementCmdSequence()
{
lock (_lock)
{
_cmdSequence++; // 字节类型自动溢出255→0
// 确保序号不超过0x7F避免下发指令CMD被误认为上传指令
if (_cmdSequence >= 0x80)
_cmdSequence = 0;
}
}
/// <summary>
/// 获取当前指令序号(带线程安全)
/// </summary>
private static byte GetCurrentCmdSequence()
{
lock (_lock)
{
return _cmdSequence;
}
}
#endregion
#region PCbyte[]
/// <summary>
/// 握手指令(系统开机必发)
/// </summary>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>握手指令帧5字节</returns>
public static byte[] SendHello(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 帧长固定5字节
byte seq = GetCurrentCmdSequence();
// 按协议填充帧结构ST → HELLO+序号 → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = (byte)(HELLO | seq);
frame[2] = deviceId;
frame[3] = 5; // LTH固定为5
frame[4] = CalculateChecksum(frame, 4); // 前4字节求和取低8位
IncrementCmdSequence(); // 序号自增
AddFrameInterval(); // 帧间隔>2ms
return frame;
}
/// <summary>
/// 读取报错状态指令
/// </summary>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>报错状态指令帧5字节</returns>
public static byte[] SendStatusRequest(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 帧长固定5字节
byte seq = GetCurrentCmdSequence();
// 帧结构ST → STATUS+序号 → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = (byte)(STATUS | seq);
frame[2] = deviceId;
frame[3] = 5; // LTH固定为5
frame[4] = CalculateChecksum(frame, 4);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 温度控制指令(下发后主控板直接执行温度控制)
/// </summary>
/// <param name="targetTemp">目标温度10.0-180.0℃精度0.1</param>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <param name="releaseControl">是否释放控制true=0xFFFF关断功率关闭温度</param>
/// <returns>温度控制指令帧7字节</returns>
public static byte[] SendSetTemp(double targetTemp, byte deviceId = 0, bool releaseControl=false)
{
byte[] frame = new byte[7]; // 帧长固定7字节
byte seq = GetCurrentCmdSequence();
int tempParam = 0; // 温度参数16位高8位+低8位
// 处理参数:释放控制/正常温度
if (releaseControl)
{
tempParam = 0xFFFF; // 协议约定:释放控制=0xFFFF
}
else
{
// 参数校验温度范围10.0-180.0℃对应1000-18000
if (targetTemp < -50.0 || targetTemp > 50.0)
throw new ArgumentOutOfRangeException(nameof(targetTemp), "温度必须在[-50 , 50]℃之间");
tempParam = (short)(targetTemp * 100); // 放大100倍精度0.1
}
// 帧结构ST → SETTEMP+序号 → 设备ID → LTH=7 → 参数1(高8) → 参数2(低8) → 校验和
frame[0] = ST;
frame[1] = (byte)(SETTEMP | seq);
frame[2] = deviceId;
frame[3] = 7; // LTH固定为7
frame[4] = (byte)(tempParam >> 8); // 参数1高8位
frame[5] = (byte)(tempParam & 0xFF); // 参数2低8位
frame[6] = CalculateChecksum(frame, 6); // 前6字节求和
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 电机控制指令(设置电机转速)
/// </summary>
/// <param name="motorSpeed">电机转速200-150016位参数</param>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>电机控制指令帧11字节0x0B=11</returns>
public static byte[] SendMotorControl(int motorSpeed, byte deviceId = 0)
{
// 参数校验转速范围100-1500
if (motorSpeed < 100 || motorSpeed > 1500)
throw new ArgumentOutOfRangeException(nameof(motorSpeed), "转速必须在100-1500之间");
byte[] frame = new byte[11]; // 帧长固定0x0B11字节
byte seq = GetCurrentCmdSequence();
ushort speedParam = (ushort)motorSpeed; // 转速参数16位
// 帧结构ST → MOTOR+序号 → 设备ID → LTH=0x0B → 0x10 → 预留0×3 → 参数4(高8) → 参数5(低8) → 校验和
frame[0] = ST;
frame[1] = (byte)(MOTOR | seq);
frame[2] = deviceId;
frame[3] = 0x0B; // LTH固定为11
frame[4] = 0x10; // 协议固定值
frame[5] = 0; // 预留位1
frame[6] = 0; // 预留位2
frame[7] = 0; // 预留位3
frame[8] = (byte)(speedParam >> 8); // 参数4高8位
frame[9] = (byte)(speedParam & 0xFF); // 参数5低8位
frame[10] = CalculateChecksum(frame, 10); // 前10字节求和
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 读取温度指令(获取设置温度、实际温度、到温标志)
/// </summary>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>读取温度指令帧5字节</returns>
public static byte[] SendReadTempRequest(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 帧长固定5字节
byte seq = GetCurrentCmdSequence();
// 帧结构ST → RDTEMP+序号 → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = (byte)(RDTEMP | seq);
frame[2] = deviceId;
frame[3] = 5; // LTH固定为5
frame[4] = CalculateChecksum(frame, 4);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 读取传感器AD指令获取AD值
/// </summary>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>读取AD指令帧5字节</returns>
public static byte[] SendReadAdcRequest(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 帧长固定5字节协议3.6
byte seq = GetCurrentCmdSequence();
// 帧结构ST → RDADC+序号 → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = (byte)(RDADC | seq);
frame[2] = deviceId;
frame[3] = 5; // LTH固定为5
frame[4] = CalculateChecksum(frame, 4);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 读取EEPROM指令获取指定地址的硬件/调试信息)
/// </summary>
/// <param name="addrHigh">地址高位字节</param>
/// <param name="addrMid">地址中位字节</param>
/// <param name="addrLow">地址低位字节</param>
/// <param name="readLength">读取长度(字节数)</param>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>读取EEPROM指令帧变长9 + readLength字节</returns>
public static byte[] SendReadEepromRequest(byte addrHigh, byte addrMid, byte addrLow, int readLength, byte deviceId = 0)
{
// 参数校验读取长度≥1避免空数据
if (readLength < 1)
throw new ArgumentOutOfRangeException(nameof(readLength), "读取长度必须≥1");
int frameLength = 9 + readLength; // 帧长N=9固定段+ 读取长度
byte[] frame = new byte[frameLength];
byte seq = GetCurrentCmdSequence();
// 帧结构ST → RDEE+序号 → 设备ID → LTH=N → 地址高→中→低 → 读取长度 → 校验和
frame[0] = ST;
frame[1] = (byte)(RDEE | seq);
frame[2] = deviceId;
frame[3] = (byte)frameLength; // LTH=N实际帧长
frame[4] = addrHigh; // 地址高位
frame[5] = addrMid; // 地址中位
frame[6] = addrLow; // 地址低位
frame[7] = (byte)readLength; // 读取长度
// 校验和frameLength-1字节求和帧长-1 = 8 + readLength
frame[frameLength - 1] = CalculateChecksum(frame, frameLength - 1);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 写入EEPROM指令写入硬件/调试/标定参数)
/// </summary>
/// <param name="addrHigh">地址高位字节</param>
/// <param name="addrMid">地址中位字节</param>
/// <param name="addrLow">地址低位字节</param>
/// <param name="writeData">待写入的数据(字节数组)</param>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>写入EEPROM指令帧变长9 + writeData.Length字节</returns>
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; // 帧长N=9固定段+ 数据长度
byte[] frame = new byte[frameLength];
byte seq = GetCurrentCmdSequence();
// 帧结构ST → WREE+序号 → 设备ID → LTH=N → 地址高→中→低 → 写入数据 → 校验和
frame[0] = ST;
frame[1] = (byte)(WREE | seq);
frame[2] = deviceId;
frame[3] = (byte)frameLength; // LTH=N实际帧长
frame[4] = addrHigh; // 地址高位
frame[5] = addrMid; // 地址中位
frame[6] = addrLow; // 地址低位
// 填充写入数据从第8字节开始索引7
Array.Copy(writeData, 0, frame, 7, writeData.Length);
// 校验和frameLength-1字节求和
frame[frameLength - 1] = CalculateChecksum(frame, frameLength - 1);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 获取实际转速指令
/// </summary>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>获取转速指令帧5字节</returns>
public static byte[] SendReadMotorSpeedRequest(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 帧长固定5字节
byte seq = GetCurrentCmdSequence();
// 帧结构ST → RDMotSpeed+序号 → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = (byte)(RDMotSpeed | seq);
frame[2] = deviceId;
frame[3] = 5; // LTH固定为5
frame[4] = CalculateChecksum(frame, 4);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 获取开关盖状态指令
/// </summary>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>获取开关盖状态指令帧5字节</returns>
public static byte[] SendReadIoStatusRequest(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 帧长固定5字节
byte seq = GetCurrentCmdSequence();
// 帧结构ST → RDIOStatus+序号 → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = (byte)(RDIOStatus | seq);
frame[2] = deviceId;
frame[3] = 5; // LTH固定为5
frame[4] = CalculateChecksum(frame, 4);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 校准温度指令设置温度校准B值
/// </summary>
/// <param name="calibBValue">校准B值16位B=(实际温度-目标温度)×100</param>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>校准温度指令帧7字节</returns>
public static byte[] SendCalibrateTempRequest(int calibBValue, byte deviceId = 0)
{
// 参数校验B值为16位有符号范围-32768~32767
if (calibBValue < short.MinValue || calibBValue > short.MaxValue)
throw new ArgumentOutOfRangeException(nameof(calibBValue), "校准B值必须在-32768~32767之间");
byte[] frame = new byte[7]; // 帧长固定7字节
byte seq = GetCurrentCmdSequence();
ushort bParam = (ushort)calibBValue; // 转换为16位无符号高位在前
// 帧结构ST → Calb+序号 → 设备ID → LTH=7 → 参数1(高8) → 参数2(低8) → 校验和
frame[0] = ST;
frame[1] = (byte)(Calb | seq);
frame[2] = deviceId;
frame[3] = 7; // LTH固定为7
frame[4] = (byte)(bParam >> 8); // 参数1高8位
frame[5] = (byte)(bParam & 0xFF); // 参数2低8位
frame[6] = CalculateChecksum(frame, 6); // 前6字节求和
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 加热温度控制
/// </summary>
public static byte[] SendSetHotTemp(double targetTemp, byte deviceId = 0, bool releaseControl = false)
{
byte[] frame = new byte[7];
byte seq = GetCurrentCmdSequence();
ushort tempParam;
if (releaseControl)
{
tempParam = 0xFFFF; // 释放控制
}
else
{
// 协议范围:室温~100℃对应室温*100~10000
if (targetTemp < 20.0 || targetTemp > 100.0) // 室温暂以20℃为参考下限
throw new ArgumentOutOfRangeException(nameof(targetTemp), "温度必须在[20.0 , 100]℃之间(参考室温)");
tempParam = (ushort)(targetTemp * 100);
}
frame[0] = ST;
frame[1] = (byte)(SETHOTTEMP | seq);
frame[2] = deviceId;
frame[3] = 7; // LTH=7
frame[4] = (byte)(tempParam >> 8);
frame[5] = (byte)(tempParam & 0xFF);
frame[6] = CalculateChecksum(frame, 6);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 电磁阀控制指令
/// </summary>
public static byte[] SendRelayControl(bool isOpen, byte deviceId = 0)
{
byte[] frame = new byte[6];
byte seq = GetCurrentCmdSequence();
byte param = (byte)(isOpen ? 1 : 0); // 1=开启0=关闭
frame[0] = ST;
frame[1] = (byte)(RelayControl | seq);
frame[2] = deviceId;
frame[3] = 6; // LTH=6
frame[4] = param;
frame[5] = CalculateChecksum(frame, 5);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
#endregion
#region PC
/// <summary>
/// 解析主控板返回的帧数据(入口方法)
/// </summary>
/// <param name="receivedFrame">主控板返回的原始字节数组</param>
/// <returns>解析结果(结构化对象,包含指令类型、状态、数据)</returns>
public static ParseResult ParseReceivedFrame(byte[] receivedFrame)
{
// 基础校验帧非空、帧长≥5最短帧为5字节
if (receivedFrame == null || receivedFrame.Length < 5)
return new ParseResult(false, "无效帧:数据为空或长度不足", null);
// 1. 校验帧头必须为ST=0x5E
if (receivedFrame[0] != ST)
return new ParseResult(false, $"帧头错误实际0x{receivedFrame[0]:X2}期望0x{ST:X2}", null);
// 2. 提取核心字段
byte cmdWithFlag = receivedFrame[1]; // 带0x80标志的指令码
byte deviceId = receivedFrame[2]; // 设备ID
byte frameLength = receivedFrame[3]; // 帧长LTH
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 STATUS:
return ParseStatusResponse(receivedFrame, deviceId);
case SETTEMP:
return ParseSetTempResponse(receivedFrame, deviceId);
case MOTOR:
return ParseMotorResponse(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 RDMotSpeed:
return ParseReadMotorSpeedResponse(receivedFrame, deviceId);
case RDIOStatus:
return ParseReadIoStatusResponse(receivedFrame, deviceId);
case Calb:
return ParseCalibrateTempResponse(receivedFrame, deviceId);
case SETHOTTEMP:
return ParseSetHotTempResponse(receivedFrame, deviceId);
case RelayControl:
return ParseRelayControlResponse(receivedFrame, deviceId);
default:
return new ParseResult(false, $"未知指令0x{originalCmd:X2}", null);
}
}
#region
/// <summary>
/// 解析握手响应
/// </summary>
private static ParseResult ParseHelloResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定6字节协议3.1
if (frame.Length != 6)
return new ParseResult(false, "握手响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位0=成功)
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "握手成功" : $"握手失败(错误码:{status}" }
};
return new ParseResult(true, "握手响应解析成功", data);
}
/// <summary>
/// 解析报错状态响应
/// </summary>
private static ParseResult ParseStatusResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定8字节协议3.2
if (frame.Length != 8)
return new ParseResult(false, "报错状态响应帧长错误需8字节", null);
// 提取Error2字节Bit0-11和状态位
ushort error = (ushort)((frame[4] << 8) | frame[5]);
byte status = frame[6];
// 解析Error按位含义协议3.2
var errorDesc = new Dictionary<string, bool>
{
{ "测温传感器故障Bit0", (error & (1 << 0)) != 0 },
{ "温度过高>200℃Bit1", (error & (1 << 1)) != 0 },
{ "温度过低≤0℃Bit2", (error & (1 << 2)) != 0 },
{ "电机故障Bit3", (error & (1 << 3)) != 0 },
{ "预留Bit4", (error & (1 << 4)) != 0 },
{ "预留Bit5", (error & (1 << 5)) != 0 }
};
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "读取报错状态成功" : $"读取失败(错误码:{status}" },
{ "报错详情", errorDesc },
{ "原始Error值", $"0x{error:X4}" }
};
return new ParseResult(true, "报错状态响应解析成功", data);
}
/// <summary>
/// 解析温度控制响应
/// </summary>
private static ParseResult ParseSetTempResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定6字节协议3.3
if (frame.Length != 6)
return new ParseResult(false, "温度控制响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位0=成功)
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "温度控制指令接收成功" : $"接收失败(错误码:{status}" }
};
return new ParseResult(true, "温度控制响应解析成功", data);
}
/// <summary>
/// 解析电机控制响应
/// </summary>
private static ParseResult ParseMotorResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定6字节协议3.4
if (frame.Length != 6)
return new ParseResult(false, "电机控制响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位0=成功)
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "电机控制指令接收成功" : $"接收失败(错误码:{status}" }
};
return new ParseResult(true, "电机控制响应解析成功", data);
}
/// <summary>
/// 解析读取温度响应
/// </summary>
private static ParseResult ParseReadTempResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定0x0D13字节协议3.5
if (frame.Length != 0x0D)
return new ParseResult(false, $"读取温度响应帧长错误需13字节实际{frame.Length}字节)", null);
// 提取数据设置温度5-6字节、实际温度7-8字节、预留9-10、标志位11、状态位12
ushort setTempParam = (ushort)((frame[4] << 8) | frame[5]);
ushort actualTempParam = (ushort)((frame[6] << 8) | frame[7]);
byte flag = frame[10]; // 0=未到温1=到温
byte status = frame[11];
// 转换为实际温度除以100精度0.1
double setTemp = setTempParam / 100.0;
double actualTemp = actualTempParam / 100.0;
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "读取温度成功" : $"读取失败(错误码:{status}" },
{ "设置温度(℃)", setTemp },
{ "实际温度(℃)", actualTemp },
{ "到温标志", flag == 0 ? "未到温" : "已到温" },
{ "原始设置温度参数", $"0x{setTempParam:X4}" },
{ "原始实际温度参数", $"0x{actualTempParam:X4}" }
};
return new ParseResult(true, "读取温度响应解析成功", data);
}
/// <summary>
/// 解析读取AD响应
/// </summary>
private static ParseResult ParseReadAdcResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定0x0D13字节协议3.6
if (frame.Length != 0x0D)
return new ParseResult(false, $"读取AD响应帧长错误需13字节实际{frame.Length}字节)", null);
// 提取数据AD值5-6字节、预留7-8、状态位9
ushort adcValue = (ushort)((frame[4] << 8) | frame[5]);
byte status = frame[8];
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "读取AD成功" : $"读取失败(错误码:{status}" },
{ "AD值", adcValue },
{ "原始AD参数", $"0x{adcValue:X4}" }
};
return new ParseResult(true, "读取AD响应解析成功", data);
}
/// <summary>
/// 解析读取EEPROM响应
/// </summary>
private static ParseResult ParseReadEepromResponse(byte[] frame, byte deviceId)
{
// 响应帧长N≥6协议3.7:返回长度-6为数据字节数
if (frame.Length < 6)
return new ParseResult(false, "读取EEPROM响应帧长不足需≥6字节", null);
// 提取数据参数1~参数n5到N-2字节、状态位N-2字节
int dataLength = frame.Length - 6; // 数据字节数N-6
byte[] eepromData = new byte[dataLength];
Array.Copy(frame, 4, eepromData, 0, dataLength); // 从第5字节索引4开始复制
byte status = frame[frame.Length - 2]; // 状态位N-2字节
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "读取EEPROM成功" : $"读取失败(错误码:{status}" },
{ "读取数据长度(字节)", dataLength },
{ "读取数据(十六进制)", BitConverter.ToString(eepromData).Replace("-", " ") },
{ "原始数据数组", eepromData }
};
return new ParseResult(true, "读取EEPROM响应解析成功", data);
}
/// <summary>
/// 解析写入EEPROM响应
/// </summary>
private static ParseResult ParseWriteEepromResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定6字节协议3.8
if (frame.Length != 6)
return new ParseResult(false, "写入EEPROM响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位0=成功)
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "写入EEPROM成功" : $"写入失败(错误码:{status}" }
};
return new ParseResult(true, "写入EEPROM响应解析成功", data);
}
/// <summary>
/// 解析获取实际转速响应
/// </summary>
private static ParseResult ParseReadMotorSpeedResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定8字节协议3.9
if (frame.Length != 8)
return new ParseResult(false, "获取转速响应帧长错误需8字节", null);
// 提取数据转速参数5-6字节、状态位7字节
ushort speedParam = (ushort)((frame[4] << 8) | frame[5]);
byte status = frame[6];
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "获取转速成功" : $"获取失败(错误码:{status}" },
{ "实际转速", speedParam },
{ "原始转速参数", $"0x{speedParam:X4}" }
};
return new ParseResult(true, "获取转速响应解析成功", data);
}
/// <summary>
/// 解析获取开关盖状态响应
/// </summary>
private static ParseResult ParseReadIoStatusResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定7字节协议3.10
if (frame.Length != 7)
return new ParseResult(false, "获取开关盖状态响应帧长错误需7字节", null);
// 提取数据开关盖参数5字节、状态位6字节
byte ioParam = frame[4]; // 0=关盖1=开盖
byte status = frame[5];
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "获取开关盖状态成功" : $"获取失败(错误码:{status}" },
{ "开关盖状态", ioParam == 0 ? "关盖" : "开盖" },
{ "原始状态参数", $"0x{ioParam:X2}" }
};
return new ParseResult(true, "获取开关盖状态响应解析成功", data);
}
/// <summary>
/// 解析校准温度响应
/// </summary>
private static ParseResult ParseCalibrateTempResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定6字节协议3.11
if (frame.Length != 6)
return new ParseResult(false, "校准温度响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位0=成功)
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "温度校准指令接收成功" : $"接收失败(错误码:{status}" }
};
return new ParseResult(true, "校准温度响应解析成功", data);
}
/// <summary>
/// 加热温度控制响应解析
/// </summary>
private static ParseResult ParseSetHotTempResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 6)
return new ParseResult(false, "加热温度响应帧长错误需6字节", null);
byte status = frame[4];
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "加热温度指令接收成功" : $"接收失败(错误码:{status}" }
};
return new ParseResult(true, "加热温度响应解析成功", data);
}
/// <summary>
/// 电磁阀控制响应解析
/// </summary>
private static ParseResult ParseRelayControlResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 6)
return new ParseResult(false, "电磁阀控制响应帧长错误需6字节", null);
byte status = frame[4];
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "电磁阀指令接收成功" : $"接收失败(错误码:{status}" }
};
return new ParseResult(true, "电磁阀控制响应解析成功", data);
}
#endregion
#endregion
/// <summary>
/// 帧间隔时间(协议要求>2ms留3ms余量
/// </summary>
private static void AddFrameInterval()
{
Task.Delay(3);
}
/// <summary>
/// CRC16校验用户原类中定义协议未使用保留以备扩展
/// </summary>
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 };
}
}
}