一、实现了读取线圈状态和写入多个线圈的功能。代码中包含了详细的注释说明,可以清晰地了解每个方法的功能和使用方式。
对于读取线圈状态的方法,使用时需要传入从站地址、起始地址和线圈数量,最后会返回一个 bool 数组,其中每个元素表示一个线圈的状态。
对于写入多个线圈的方法,使用时需要传入从站地址、起始地址和要写入的 bool 数组,表示每个线圈的状态。该方法内部会根据数量计算出需要传输的字节数,并将 bool 数组转换为字节数组,最后将整个请求报文发送出去。如果写入成功,该方法会返回 true。
二、实现了读取保持寄存器和写入多个保持寄存器的功能。代码中包含了详细的注释说明,可以清晰地了解每个方法的功能和使用方式。
对于读取保持寄存器的方法,使用时需要传入从站地址、起始地址和寄存器数量,最后会返回一个 ushort 数组,其中每个元素表示一个寄存器的值。
对于写入多个保持寄存器的方法,使用时需要传入从站地址、起始地址和要写入的 ushort 数组,表示每个寄存器的值。该方法内部会根据数量计算出需要传输的字节数,并将 ushort 数组转换为字节数组,最后将整个请求报文发送出去。如果写入成功,该方法会返回 true。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Net.Sockets;
namespace Authorization.WebApi
{
///
///
///
public static class ModbusTcpClient
{
private static byte[] buffer = new byte[1024];
///
/// 读线圈 00 01 00 00 00 06 01 01 00 00 00 08
/// 读取线圈状态的请求报文,从站地址是 0x01,起始地址是 0x0000,线圈数量是 0x0008
/// 使用 client.ReadCoil(0x01, 0x0000, 0x0008, out bool[] values);
///
///
///
///
///
///
///
public static bool ReadCoil(this Socket socket, int slaveAddress, int startAddress, int quantity, out bool[] values)
{
bool success = false;
values = null;
// 组装 Modbus TCP 请求报文
byte[] request = new byte[12];
byte[] addressBytes = BitConverter.GetBytes((ushort)startAddress);
byte[] quantityBytes = BitConverter.GetBytes((ushort)quantity);
request[0] = (byte)slaveAddress; // 从站地址
request[1] = 0x01; // 功能码
request[2] = addressBytes[1]; // 起始地址高字节
request[3] = addressBytes[0]; // 起始地址低字节
request[4] = quantityBytes[1]; // 线圈数量高字节
request[5] = quantityBytes[0]; // 线圈数量低字节
byte[] crcBytes = CalculateCrc(request, request.Length - 2); // 计算 CRC 校验码
request[6] = crcBytes[0]; // CRC 校验码低字节
request[7] = crcBytes[1]; // CRC 校验码高字节
// 发送 Modbus TCP 请求报文
socket.Send(request, 0, 8, SocketFlags.None);
// 接收 Modbus TCP 响应报文
int count = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
if (count >= 5 && buffer[0] == slaveAddress && buffer[1] == 0x01 && buffer[2] == quantity / 8 + (quantity % 8 == 0 ? 0 : 1))
{
byte[] data = new byte[quantity / 8 + (quantity % 8 == 0 ? 0 : 1)];
Array.Copy(buffer, 3, data, 0, data.Length);
values = new bool[quantity];
for (int i = 0; i < quantity; i++)
{
int byteIndex = i / 8;
int bitIndex = i % 8;
values[i] = ((data[byteIndex] >> bitIndex) & 0x01) == 0x01;
}
success = true;
}
return success;
}
public static bool WriteCoil(this Socket socket, int slaveAddress, int address, bool value)
{
bool success = false;
// 组装 Modbus TCP 请求报文
byte[] request = new byte[8];
byte[] addressBytes = BitConverter.GetBytes((ushort)address);
request[0] = (byte)slaveAddress; // 从站地址
request[1] = 0x05; // 功能码
request[2] = addressBytes[1]; // 输出地址高字节
request[3] = addressBytes[0]; // 输出地址低字节
request[4] = (byte)(value ? 0xFF : 0x00); // 输出值
request[5] = 0x00;
byte[] crcBytes = CalculateCrc(request, request.Length - 2); // 计算 CRC 校验码
request[6] = crcBytes[1]; // CRC 校验码低字节
request[7] = crcBytes[0]; // CRC 校验码高字节
// 发送 Modbus TCP 请求报文
socket.Send(request, 0, 8, SocketFlags.None);
// 接收 Modbus TCP 响应报文
int count = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
if (count == 8 && buffer[0] == slaveAddress && buffer[1] == 0x05 && BitConverter.ToUInt16(buffer, 2) == address && BitConverter.ToUInt16(buffer, 4) == (value ? 0xFF00 : 0x0000))
{
success = true;
}
return success;
}
///
/// 写多个线圈 00 01 00 00 00 08 01 0f 00 00 00 08 01 ff
/// bool[] coils = new bool[] { true, true, true, true, true, true, true, true };
/// client.WriteMultipleCoils(0x01, 0x0000, coils);
///
///
///
///
///
///
public static bool WriteMultipleCoils(this Socket socket, int slaveAddress, int address, bool[] values)
{
bool success = false;
// 组装 Modbus TCP 请求报文
int quantity = values.Length;
int byteCount = (quantity % 8 == 0) ? quantity / 8 : quantity / 8 + 1;
byte[] request = new byte[7 + byteCount];
byte[] addressBytes = BitConverter.GetBytes((ushort)address);
byte[] quantityBytes = BitConverter.GetBytes((ushort)quantity);
request[0] = (byte)slaveAddress; // 从站地址
request[1] = 0x0F; // 功能码
request[2] = addressBytes[1]; // 起始地址高字节
request[3] = addressBytes[0]; // 起始地址低字节
request[4] = quantityBytes[1]; // 线圈数量高字节
request[5] = quantityBytes[0]; // 线圈数量低字节
request[6] = (byte)byteCount; // 字节数
for (int i = 0; i < byteCount; i++)
{
byte coilByte = 0;
for (int j = 0; j < 8 && i * 8 + j < quantity; j++)
{
if (values[i * 8 + j])
{
coilByte |= (byte)(1 << j);
}
}
request[7 + i] = coilByte; // 线圈值
}
byte[] crcBytes = CalculateCrc(request, request.Length - 2); // 计算 CRC 校验码
request[request.Length - 2] = crcBytes[0]; // CRC 校验码低字节
request[request.Length - 1] = crcBytes[1]; // CRC 校验码高字节
// 发送 Modbus TCP 请求报文
socket.Send(request, 0, request.Length, SocketFlags.None);
// 接收 Modbus TCP 响应报文
int count = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
if (count == 8 && buffer[0] == slaveAddress && buffer[1] == 0x0F && BitConverter.ToUInt16(buffer, 2) == address && BitConverter.ToUInt16(buffer, 4) == quantity)
{
success = true;
}
return success;
}
///
/// 读保持寄存器 00 01 00 00 00 06 01 03 00 00 00 02
/// 读取多个寄存器的请求报文,从站地址是 0x01,起始地址是 0x0000,寄存器数量是 0x0002
/// client.ReadHoldingRegisters(0x01, 0x0000, 2);
///
///
///
///
///
///
///
public static bool ReadHoldingRegister(this Socket socket, int slaveAddress, int startAddress, int quantity, out ushort[] values)
{
bool success = false;
values = null;
// 组装 Modbus TCP 请求报文
byte[] request = new byte[12];
byte[] addressBytes = BitConverter.GetBytes((ushort)startAddress);
byte[] quantityBytes = BitConverter.GetBytes((ushort)quantity);
request[0] = (byte)slaveAddress; // 从站地址
request[1] = 0x03; // 功能码
request[2] = addressBytes[1]; // 起始地址高字节
request[3] = addressBytes[0]; // 起始地址低字节
request[4] = quantityBytes[1]; // 寄存器数量高字节
request[5] = quantityBytes[0]; // 寄存器数量低字节
byte[] crcBytes = CalculateCrc(request, 6); // 计算 CRC 校验码
request[6] = crcBytes[0]; // CRC 校验码低字节
request[7] = crcBytes[1]; // CRC 校验码高字节
// 发送 Modbus TCP 请求报文
socket.Send(request, 0, 8, SocketFlags.None);
// 接收 Modbus TCP 响应报文
int count = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
if (count == 3 + quantity * 2 && buffer[0] == slaveAddress && buffer[1] == 0x03)
{
values = new ushort[quantity];
for (int i = 0; i < quantity; i++)
{
values[i] = BitConverter.ToUInt16(buffer, 3 + i * 2);
}
success = true;
}
return success;
}
///
/// 写多个保持寄存器
/// 00 01 00 00 00 06 01 10 00 00 00 02 04 00 64 00 32
/// 表示写入第一个保持寄存器的值为00 64,转换成+进制为100
/// 表示写入第二个保持寄存器的值为00 32,转换成十进制为50
/// socket.WriteMultipleRegisters(1, 0, new ushort[] { 100, 50 });
///
///
///
///
///
///
public static bool WriteMultipleRegisters(this Socket socket, int slaveAddress, int startAddress, ushort[] values)
{
bool success = false;
// 组装 Modbus TCP 请求报文
byte[] request = new byte[13 + values.Length * 2];
byte[] addressBytes = BitConverter.GetBytes((ushort)startAddress);
byte[] quantityBytes = BitConverter.GetBytes((ushort)values.Length);
request[0] = (byte)slaveAddress; // 从站地址
request[1] = 0x10; // 功能码
request[2] = addressBytes[1]; // 起始地址高字节
request[3] = addressBytes[0]; // 起始地址低字节
request[4] = quantityBytes[1]; // 寄存器数量高字节
request[5] = quantityBytes[0]; // 寄存器数量低字节
request[6] = (byte)(values.Length * 2); // 字节数
for (int i = 0; i < values.Length; i++)
{
byte[] valueBytes = BitConverter.GetBytes(values[i]);
request[7 + i * 2] = valueBytes[1]; // 寄存器值高字节
request[8 + i * 2] = valueBytes[0]; // 寄存器值低字节
}
byte[] crcBytes = CalculateCrc(request, request.Length - 2); // 计算 CRC 校验码
request[request.Length - 2] = crcBytes[0]; // CRC 校验码低字节
request[request.Length - 1] = crcBytes[1]; // CRC 校验码高字节
// 发送 Modbus TCP 请求报文
socket.Send(request, 0, request.Length, SocketFlags.None);
// 接收 Modbus TCP 响应报文
int count = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
if (count == 8 && buffer[0] == slaveAddress && buffer[1] == 0x10 && BitConverter.ToUInt16(buffer, 2) == startAddress && BitConverter.ToUInt16(buffer, 4) == values.Length)
{
success = true;
}
return success;
}
public static bool WriteHoldingRegister(this Socket socket, int slaveAddress, int address, ushort value)
{
bool success = false;
// 组装 Modbus TCP 请求报文
byte[] request = new byte[12];
byte[] addressBytes = BitConverter.GetBytes((ushort)address);
byte[] valueBytes = BitConverter.GetBytes(value);
request[0] = (byte)slaveAddress; // 从站地址
request[1] = 0x06; // 功能码
request[2] = addressBytes[1]; // 寄存器地址高字节
request[3] = addressBytes[0]; // 寄存器地址低字节
request[4] = valueBytes[1]; // 寄存器值高字节
request[5] = valueBytes[0]; // 寄存器值低字节
byte[] crcBytes = CalculateCrc(request, 6); // 计算 CRC 校验码
request[6] = crcBytes[0]; // CRC 校验码低字节
request[7] = crcBytes[1]; // CRC 校验码高字节
// 发送 Modbus TCP 请求报文
socket.Send(request, 0, 8, SocketFlags.None);
// 接收 Modbus TCP 响应报文
int count = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
if (count == 8 && buffer[0] == slaveAddress && buffer[1] == 0x06 && BitConverter.ToUInt16(buffer, 2) == address && BitConverter.ToUInt16(buffer, 4) == value)
{
success = true;
}
return success;
}
private static byte[] 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) == 0x0001)
{
crc >>= 1;
crc ^= 0xA001;
}
else
{
crc >>= 1;
}
}
}
byte[] crcBytes = BitConverter.GetBytes(crc);
Array.Reverse(crcBytes);
return crcBytes;
}
}
}