using System; using System.Collections.Generic; using System.IO.Ports; using System.Net.Sockets; using System.Linq; using System.Net; namespace ModbusLibrary { public class ModbusClient { private TcpClient tcpClient; private NetworkStream tcpStream; private SerialPort serialPort; private bool isTcp; private BitMode bitMode= BitMode.大端; /// /// BitConverter 小端序 /// IPAddress.HostToNetworkOrder 方法将本地字节序转换为网络字节序(大端序)。 /// /// /// public ModbusClient(string host, int port) { tcpClient = new TcpClient(host, port); tcpStream = tcpClient.GetStream(); isTcp = true; } public ModbusClient(string portName, int baudRate, int dataBits, StopBits stopBits, Parity parity) { serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits); serialPort.Open(); isTcp = false; } public void Close() { if ( isTcp ) { tcpClient.Close(); } else { serialPort.Close(); } } private ushort CalculateCRC(byte[] data) { ushort crc = 0xFFFF; foreach ( byte b in data ) { crc ^= b; for ( int i = 0; i < 8; i++ ) { if ( ( crc & 0x0001 ) == 1 ) { crc >>= 1; crc ^= 0xA001; } else { crc >>= 1; } } } return crc; } private byte[] BuildRequestToNetwork(byte unitId, byte functionCode, ushort startAddress, ushort quantity, byte[] data = null) { List request = new List(); if ( isTcp ) { // Modbus TCP request.AddRange(BitConverter.GetBytes(( ushort ) IPAddress.HostToNetworkOrder(( short ) 0))); // 事务标识符 request.AddRange(BitConverter.GetBytes(( ushort ) IPAddress.HostToNetworkOrder(( short ) 0))); // 协议标识符 int dataLength = 6+((quantity-1)*2); request.AddRange(BitConverter.GetBytes(( ushort ) IPAddress.HostToNetworkOrder(( short ) dataLength))); // 长度 request.Add(unitId); // 单元标识符 request.Add(functionCode); // 功能码 request.AddRange(BitConverter.GetBytes(( ushort ) IPAddress.HostToNetworkOrder(( short ) startAddress))); // 起始地址 if ( data != null ) { request.AddRange(data); // 数据 } if ( functionCode == 0x03 || functionCode == 0x04 ) { request.AddRange(BitConverter.GetBytes(( ushort ) IPAddress.HostToNetworkOrder(( short ) quantity))); // 寄存器数量 } } else { // Modbus RTU request.Add(unitId); // 地址 request.Add(functionCode); // 功能码 request.AddRange(BitConverter.GetBytes(startAddress)); // 起始地址 if ( functionCode == 0x03 || functionCode == 0x04 ) { request.AddRange(BitConverter.GetBytes(quantity)); // 寄存器数量 } else if ( data != null ) { request.AddRange(data); // 数据 } ushort crc = CalculateCRC(request.ToArray()); request.AddRange(BitConverter.GetBytes(crc)); // CRC 校验 } return request.ToArray(); } private byte[] BuildRequestTobit(byte unitId, byte functionCode, ushort startAddress, ushort quantity, byte[] data = null) { List request = new List(); if ( isTcp ) { // Modbus TCP request.AddRange(BitConverter.GetBytes(( ushort) 0)); // 事务标识符 request.AddRange(BitConverter.GetBytes(( ushort )0)); // 协议标识符 int dataLength = 6+((quantity-1)*2); request.AddRange(BitConverter.GetBytes(( ushort ) dataLength)); // 长度 request.Add(unitId); // 单元标识符 request.Add(functionCode); // 功能码 request.AddRange(BitConverter.GetBytes(startAddress)); // 起始地址 if ( data != null ) { request.AddRange(data); // 数据 } if ( functionCode == 0x03 || functionCode == 0x04 ) { request.AddRange(BitConverter.GetBytes(quantity)); // 寄存器数量 } } else { // Modbus RTU request.Add(unitId); // 地址 request.Add(functionCode); // 功能码 request.AddRange(BitConverter.GetBytes(startAddress)); // 起始地址 if ( functionCode == 0x03 || functionCode == 0x04 ) { request.AddRange(BitConverter.GetBytes(quantity)); // 寄存器数量 } else if ( data != null ) { request.AddRange(data); // 数据 } ushort crc = CalculateCRC(request.ToArray()); request.AddRange(BitConverter.GetBytes(crc)); // CRC 校验 } return request.ToArray(); } private byte[] ReadResponse() { byte[] buffer = new byte[256]; int bytesRead; if ( isTcp ) { // 读取事务标识符 bytesRead = tcpStream.Read(buffer, 0, 2); ushort transactionId = (ushort)(buffer[0] << 8 | buffer[1]); // 读取协议标识符 bytesRead += tcpStream.Read(buffer, 2, 2); ushort protocolId = (ushort)(buffer[2] << 8 | buffer[3]); // 读取长度 bytesRead += tcpStream.Read(buffer, 4, 2); ushort length = (ushort)(buffer[4] << 8 | buffer[5]); // 读取完整帧 bytesRead += tcpStream.Read(buffer, 6, length); byte[] response = new byte[bytesRead]; Array.Copy(buffer, response, bytesRead); return response; } else { bytesRead = serialPort.Read(buffer, 0, buffer.Length); byte[] response = new byte[bytesRead]; Array.Copy(buffer, response, bytesRead); return response; } } private void WriteRequest(byte[] request) { if ( isTcp ) { tcpStream.Write(request, 0, request.Length); } else { serialPort.Write(request, 0, request.Length); } } private bool CheckCRC(byte[] response) { ushort crcReceived = (ushort)(response[response.Length - 2] << 8 | response[response.Length - 1]); byte[] data = new byte[response.Length - 2]; Array.Copy(response, 0, data, 0, response.Length - 2); ushort crcCalculated = CalculateCRC(data); return crcReceived == crcCalculated; } public bool[] ReadCoils(byte unitId, ushort startAddress, ushort quantity) { byte[] request = BuildRequestToNetwork(unitId, 0x01, startAddress, quantity); WriteRequest(request); byte[] response = ReadResponse(); if ( isTcp ) { // 更严格的长度检查 if ( response.Length < 9 ) { throw new Exception("Invalid response length for ReadCoils (TCP)"); } if ( response[ 7 ] == 0x01 ) { int byteCount = response[8]; if ( response.Length != 9 + byteCount ) { throw new Exception("Invalid response length for ReadCoils (TCP)"); } bool[] coils = new bool[quantity]; int index = 0; for ( int i = 0; i < byteCount; i++ ) { byte b = response[9 + i]; for ( int j = 0; j < 8; j++ ) { coils[ index++ ] = ( b & ( 1 << j ) ) != 0; if ( index >= quantity ) break; } } return coils; } else { throw new Exception("Error reading coils"); } } else { if ( !CheckCRC(response) || response[ 1 ] != 0x01 ) { throw new Exception("Error reading coils"); } int byteCount = response[2]; if ( response.Length != 3 + byteCount + 2 ) { throw new Exception("Invalid response length for ReadCoils (RTU)"); } bool[] coils = new bool[quantity]; int index = 0; for ( int i = 0; i < byteCount; i++ ) { byte b = response[3 + i]; for ( int j = 0; j < 8; j++ ) { coils[ index++ ] = ( b & ( 1 << j ) ) != 0; if ( index >= quantity ) break; } } return coils; } } public bool[] ReadDiscreteInputs(byte unitId, ushort startAddress, ushort quantity) { byte[] request = BuildRequestToNetwork(unitId, 0x02, startAddress, quantity); WriteRequest(request); byte[] response = ReadResponse(); if ( isTcp ) { if ( response.Length < 9 ) { throw new Exception("Invalid response length for ReadDiscreteInputs (TCP)"); } if ( response[ 7 ] == 0x02 ) { int byteCount = response[8]; if ( response.Length != 9 + byteCount ) { throw new Exception("Invalid response length for ReadDiscreteInputs (TCP)"); } bool[] inputs = new bool[quantity]; int index = 0; for ( int i = 0; i < byteCount; i++ ) { byte b = response[9 + i]; for ( int j = 0; j < 8; j++ ) { inputs[ index++ ] = ( b & ( 1 << j ) ) != 0; if ( index >= quantity ) break; } } return inputs; } else { throw new Exception("Error reading discrete inputs"); } } else { if ( !CheckCRC(response) || response[ 1 ] != 0x02 ) { throw new Exception("Error reading discrete inputs"); } int byteCount = response[2]; if ( response.Length != 3 + byteCount + 2 ) { throw new Exception("Invalid response length for ReadDiscreteInputs (RTU)"); } bool[] inputs = new bool[quantity]; int index = 0; for ( int i = 0; i < byteCount; i++ ) { byte b = response[3 + i]; for ( int j = 0; j < 8; j++ ) { inputs[ index++ ] = ( b & ( 1 << j ) ) != 0; if ( index >= quantity ) break; } } return inputs; } } public ushort[] ReadHoldingRegisters(byte unitId, ushort startAddress, ushort quantity) { byte[] request = BuildRequestToNetwork(unitId, 0x03, startAddress, quantity); WriteRequest(request); byte[] response = ReadResponse(); if ( isTcp ) { if ( response.Length < 9 ) { throw new Exception("Invalid response length for ReadHoldingRegisters (TCP)"); } if ( response[ 7 ] == 0x03 ) { int byteCount = response[8]; if ( response.Length != 9 + byteCount ) { throw new Exception("Invalid response length for ReadHoldingRegisters (TCP)"); } ushort[] registers = new ushort[byteCount / 2]; for ( int i = 0; i < byteCount / 2; i++ ) { // 转换为本地字节序 registers[ i ] = ( ushort ) IPAddress.NetworkToHostOrder(( short ) ( response[ 9 + i * 2 ] << 8 | response[ 10 + i * 2 ] )); } return registers; } else { throw new Exception("Error reading holding registers"); } } else { if ( !CheckCRC(response) || response[ 1 ] != 0x03 ) { throw new Exception("Error reading holding registers"); } int byteCount = response[2]; if ( response.Length != 3 + byteCount + 2 ) { throw new Exception("Invalid response length for ReadHoldingRegisters (RTU)"); } ushort[] registers = new ushort[byteCount / 2]; for ( int i = 0; i < byteCount / 2; i++ ) { registers[ i ] = ( ushort ) ( response[ 3 + i * 2 ] << 8 | response[ 4 + i * 2 ] ); } return registers; } } public ushort[] ReadInputRegisters(byte unitId, ushort startAddress, ushort quantity) { byte[] request = BuildRequestToNetwork(unitId, 0x04, startAddress, quantity); WriteRequest(request); byte[] response = ReadResponse(); if ( isTcp ) { if ( response.Length < 9 ) { throw new Exception("Invalid response length for ReadInputRegisters (TCP)"); } if ( response[ 7 ] == 0x04 ) { int byteCount = response[8]; if ( response.Length != 9 + byteCount ) { throw new Exception("Invalid response length for ReadInputRegisters (TCP)"); } ushort[] registers = new ushort[byteCount / 2]; for ( int i = 0; i < byteCount / 2; i++ ) { registers[ i ] = ( ushort ) ( response[ 9 + i * 2 ] << 8 | response[ 10 + i * 2 ] ); } return registers; } else { throw new Exception("Error reading input registers"); } } else { if ( !CheckCRC(response) || response[ 1 ] != 0x04 ) { throw new Exception("Error reading input registers"); } int byteCount = response[2]; if ( response.Length != 3 + byteCount + 2 ) { throw new Exception("Invalid response length for ReadInputRegisters (RTU)"); } ushort[] registers = new ushort[byteCount / 2]; for ( int i = 0; i < byteCount / 2; i++ ) { registers[ i ] = ( ushort ) ( response[ 3 + i * 2 ] << 8 | response[ 4 + i * 2 ] ); } return registers; } } public void WriteSingleCoil(byte unitId, ushort address, bool value) { byte[] data = BitConverter.GetBytes((ushort)(value? 0xFF00 : 0x0000)); byte[] request = BuildRequestToNetwork(unitId, 0x05, address, 0, data); WriteRequest(request); byte[] response = ReadResponse(); if ( isTcp ) { if ( response.Length != 12 ) { throw new Exception("Invalid response length for WriteSingleCoil (TCP)"); } if ( response[ 7 ] != 0x05 ) { throw new Exception("Error writing single coil"); } } else { if ( !CheckCRC(response) || response[ 1 ] != 0x05 ) { throw new Exception("Error writing single coil"); } } } public void WriteSingleRegister(byte unitId, ushort address, ushort value) { byte[] data = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)value)); byte[] request = BuildRequestToNetwork(unitId, 0x06, address, 0, data); WriteRequest(request); byte[] response = ReadResponse(); if ( isTcp ) { if ( response.Length != 12 ) { throw new Exception("Invalid response length for WriteSingleRegister (TCP)"); } if ( response[ 7 ] != 0x06 ) { throw new Exception("Error writing single register"); } } else { if ( !CheckCRC(response) || response[ 1 ] != 0x06 ) { throw new Exception("Error writing single register"); } } } public void WriteMultipleCoils(byte unitId, ushort startAddress, bool[] values) { byte[] data = new byte[(values.Length / 8) + ((values.Length % 8 == 0)? 0 : 1)]; for ( int i = 0; i < values.Length; i++ ) { int byteIndex = i / 8; int bitIndex = i % 8; if ( values[ i ] ) { data[ byteIndex ] |= ( byte ) ( 1 << bitIndex ); } } byte[] request = BuildRequestToNetwork(unitId, 0x0F, startAddress, (ushort)values.Length, data); WriteRequest(request); byte[] response = ReadResponse(); if ( isTcp ) { if ( response.Length < 12 ) { throw new Exception("Invalid response length for WriteMultipleCoils (TCP)"); } if ( response[ 7 ] != 0x0F ) { throw new Exception("Error writing multiple coils"); } } else { if ( !CheckCRC(response) || response[ 1 ] != 0x0F ) { throw new Exception("Error writing multiple coils"); } } } public void WriteMultipleRegisters(byte unitId, ushort startAddress, ushort[] values) { byte[] data = new byte[values.Length * 2]; for ( int i = 0; i < values.Length; i++ ) { BitConverter.GetBytes(( ushort ) IPAddress.HostToNetworkOrder(( short ) values[i])); data[ i * 2 ] = ( byte ) ( values[ i ] >> 8 ); data[ i * 2 + 1 ] = ( byte ) ( values[ i ] & 0xFF ); } byte[] request = BuildRequestToNetwork(unitId, 0x10, startAddress, (ushort)values.Length, data); WriteRequest(request); byte[] response = ReadResponse(); if ( isTcp ) { if ( response.Length < 12 ) { throw new Exception("Invalid response length for WriteMultipleRegisters (TCP)"); } if ( response[ 7 ] != 0x10 ) { throw new Exception("Error writing multiple registers"); } } else { if ( !CheckCRC(response) || response[ 1 ] != 0x10 ) { throw new Exception("Error writing multiple registers"); } } } } public enum BitMode { 大端, 小端 } }