106 lines
3.3 KiB
C#
106 lines
3.3 KiB
C#
public static class ModbusHelper
|
||
{
|
||
// 生成写单个线圈(功能码05)的Modbus指令
|
||
public static string GenerateWriteSingleCoilCommand(byte deviceAddress, ushort coilAddress, bool value)
|
||
{
|
||
byte[] command = new byte[6];
|
||
command[0] = deviceAddress;
|
||
command[1] = 0x05; // 写单个线圈功能码
|
||
command[2] = (byte)(coilAddress >> 8);
|
||
command[3] = (byte)coilAddress;
|
||
command[4] = value ? (byte)0xFF : (byte)0x00;
|
||
command[5] = 0x00;
|
||
|
||
// 计算CRC校验
|
||
ushort crc = CalculateCRC(command, 6);
|
||
byte[] crcBytes = BitConverter.GetBytes(crc);
|
||
|
||
// 组合完整命令(包含CRC校验)
|
||
byte[] fullCommand = new byte[8];
|
||
Array.Copy(command, fullCommand, 6);
|
||
fullCommand[6] = crcBytes[0]; // CRC低字节在前
|
||
fullCommand[7] = crcBytes[1]; // CRC高字节在后
|
||
|
||
// 转换为十六进制字符串
|
||
return BitConverter.ToString(fullCommand).Replace("-", "");
|
||
}
|
||
|
||
// 验证Modbus响应是否有效
|
||
public static bool ValidateResponse(string command, string response)
|
||
{
|
||
if (string.IsNullOrEmpty(response) || response.Length < 8)
|
||
return false;
|
||
|
||
// 解析响应字符串为字节数组
|
||
byte[] responseBytes = StringToByteArray(response);
|
||
|
||
// 从原始命令中提取设备地址
|
||
byte deviceAddress = StringToByteArray(command.Substring(0, 2))[0];
|
||
|
||
// 验证设备地址
|
||
if (responseBytes[0] != deviceAddress)
|
||
return false;
|
||
|
||
// 验证功能码(响应中的功能码应与请求相同,若有异常则功能码最高位为1)
|
||
byte requestFunctionCode = StringToByteArray(command.Substring(2, 2))[0];
|
||
byte responseFunctionCode = responseBytes[1];
|
||
|
||
// 检查是否为异常响应
|
||
if ((responseFunctionCode & 0x80) != 0)
|
||
{
|
||
byte errorCode = responseBytes[2];
|
||
Console.WriteLine($"Modbus异常响应 - 功能码: {requestFunctionCode}, 错误码: {errorCode}");
|
||
return false;
|
||
}
|
||
|
||
// 验证功能码是否匹配
|
||
if (responseFunctionCode != requestFunctionCode)
|
||
return false;
|
||
|
||
// 验证CRC校验
|
||
ushort receivedCrc = (ushort)((responseBytes[responseBytes.Length - 2] << 8) | responseBytes[responseBytes.Length - 1]);
|
||
ushort calculatedCrc = CalculateCRC(responseBytes, responseBytes.Length - 2);
|
||
|
||
return receivedCrc == calculatedCrc;
|
||
}
|
||
|
||
// 计算Modbus RTU CRC校验
|
||
private static ushort CalculateCRC(byte[] data, int length)
|
||
{
|
||
ushort crc = 0xFFFF;
|
||
|
||
for (int i = 0; i < length; i++)
|
||
{
|
||
crc ^= data[i];
|
||
|
||
for (int j = 0; j < 8; j++)
|
||
{
|
||
if ((crc & 0x0001) != 0)
|
||
{
|
||
crc >>= 1;
|
||
crc ^= 0xA001;
|
||
}
|
||
else
|
||
{
|
||
crc >>= 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
return crc;
|
||
}
|
||
|
||
// 将十六进制字符串转换为字节数组
|
||
private static byte[] StringToByteArray(string hex)
|
||
{
|
||
int length = hex.Length;
|
||
byte[] bytes = new byte[length / 2];
|
||
|
||
for (int i = 0; i < length; i += 2)
|
||
{
|
||
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
|
||
}
|
||
|
||
return bytes;
|
||
}
|
||
} |