一、实现了读取线圈状态和写入多个线圈的功能。代码中包含了详细的注释说明,可以清晰地了解每个方法的功能和使用方式。
对于读取线圈状态的方法,使用时需要传入从站地址、起始地址和线圈数量,最后会返回一个 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
{/// <summary>/// /// </summary>public static class ModbusTcpClient{private static byte[] buffer = new byte[1024];/// <summary>/// 读线圈 00 01 00 00 00 06 01 01 00 00 00 08/// 读取线圈状态的请求报文,从站地址是 0x01,起始地址是 0x0000,线圈数量是 0x0008/// 使用 client.ReadCoil(0x01, 0x0000, 0x0008, out bool[] values);/// </summary>/// <param name="socket"></param>/// <param name="slaveAddress"></param>/// <param name="startAddress"></param>/// <param name="quantity"></param>/// <param name="values"></param>/// <returns></returns>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, 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 >= 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[12];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); // 输出值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] == 0x05 && BitConverter.ToUInt16(buffer, 2) == address && BitConverter.ToUInt16(buffer, 4) == (value ? 0xFF00 : 0x0000)){success = true;}return success;}/// <summary>/// 写多个线圈 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);/// </summary>/// <param name="socket"></param>/// <param name="slaveAddress"></param>/// <param name="address"></param>/// <param name="values"></param>/// <returns></returns>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;}/// <summary>/// 读保持寄存器 00 01 00 00 00 06 01 03 00 00 00 02 /// 读取多个寄存器的请求报文,从站地址是 0x01,起始地址是 0x0000,寄存器数量是 0x0002/// client.ReadHoldingRegisters(0x01, 0x0000, 2);/// </summary>/// <param name="socket"></param>/// <param name="slaveAddress"></param>/// <param name="startAddress"></param>/// <param name="quantity"></param>/// <param name="values"></param>/// <returns></returns>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;}/// <summary>/// 写多个保持寄存器/// 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 });/// </summary>/// <param name="socket"></param>/// <param name="slaveAddress"></param>/// <param name="startAddress"></param>/// <param name="values"></param>/// <returns></returns>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;}}
}