Modbus TCP通信报文解析

一、实现了读取线圈状态和写入多个线圈的功能。代码中包含了详细的注释说明,可以清晰地了解每个方法的功能和使用方式。

对于读取线圈状态的方法,使用时需要传入从站地址、起始地址和线圈数量,最后会返回一个 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;
        }

    }
}

你可能感兴趣的:(开发语言,c#)