Modbus读写Csharp兼容西门子、施耐德

以下为C#读写Modbus,兼容西门子和施耐德。有详细的Modbus协议报文说明,以及代码注释
一、新建类:ModbusTcpUtil.cs 代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;

/*
 * Modbus报文协议格式【只考虑读写保持寄存器 0x03:读多个保持寄存器 0x06:写单个保持寄存器 0x10:写多个保持寄存器】
 * PLC中,每个寄存器将数据分成两个字节,即一个寄存器【一个字Word】地址占用两个字节Byte,寄存器地址范围:【0x0000~0xFFFF】
 * 注意:PLC存储数据是高位在前,如十进制的1000,在PLC中存储字节顺序是:0x03 0xE8。而在C#中存储字节顺序是低位在前:如 BitConverter.GetBytes((short)1000),是 0xE8 0x03 
      ①发送的请求【读多个保持寄存器 0x03】Modbus字节流说明:
            * byte[0] byte[1] 随便指定,PLC返回的前两个字节完全一致
            * byte[2]=0 byte[3]=0 固定为0 代表Modbus标识
            * byte[4] byte[5] 排在byte[5]后面所有字节的个数,也就是总长度6
            * byte[6] 站号,随便指定,00--FF都可以,PLC返回的保持一致
            * byte[7] 功能码,读保持寄存器 0x03:
            * byte[8] byte[9] 起始地址,如起始地址为整数20 则为 0x00 0x14,再如起始地址为整数1000,则为 0x03 0xE8
            * byte[10] byte[11] 寄存器个数【Word】,读取的数据长度【以字Word为单位】,读取Int32或Float就是两个字,读取byte或short就是一个字 范围【1~125 即 0x0001~0x007D】
            * 
            * 接收的内容解析:【读多个保持寄存器 0x03】【Modbus响应】
            * byte[0] byte[1] 与发送的一致
            * byte[2]=0 byte[3]=0 固定为0 代表Modbus标识
            * byte[4] byte[5] 排在byte[5]后面所有字节的个数
            * byte[6] 站号,与发送的一致
            * byte[7] 功能码,与发送的一致
            * byte[8] 表示byte[8]后面跟随的字节数【发送的寄存器个数 * 2】
            * byte[9] byte[10] byte[11] byte[12] byte[...] 真实数据的字节流,字节流的总个数就是byte[8]
  
      ②发送的请求【写单个保持寄存器 0x06】Modbus字节流说明: 【0x06只能写入一个16位【short】数据,无法写入Int32或Float】
            * byte[0] byte[1] 随便指定,PLC返回的前两个字节完全一致
            * byte[2]=0 byte[3]=0 固定为0 代表Modbus标识
            * byte[4] byte[5] 排在byte[5]后面所有字节的个数
            * byte[6] 站号,随便指定,00--FF都可以,PLC返回的保持一致
            * byte[7] 功能码,0x06:写单个保持寄存器
            * byte[8] byte[9] 起始地址,如起始地址为整数20 则为 0x00 0x14,再如起始地址为整数1000,则为 0x03 0xE8
            * byte[10] byte[11] 写入的值【只能写入一个字Word】
            * 
            * 接收的内容【写单个保持寄存器的响应内容 0x06】与发送的内容完全一致           
  
      ③发送的请求【写多个保持寄存器 0x10】Modbus字节流说明:【写Int32,UInt32,Float需要两个寄存器 写Int64,UInt64,Double需要四个寄存器】
            * byte[0] byte[1] 随便指定,PLC返回的前两个字节完全一致
            * byte[2]=0 byte[3]=0 固定为0 代表Modbus标识 随便指定也可以
            * byte[4] byte[5] 排在byte[5]后面所有字节的个数
            * byte[6] 站号,随便指定,00--FF都可以,PLC返回的保持一致
            * byte[7] 功能码,0x10:写多个保持寄存器
            * byte[8] byte[9] 起始地址,如起始地址为整数20 则为 0x00 0x14,再如起始地址为整数1000,则为 0x03 0xE8
            * byte[10] byte[11] 寄存器数量【设置长度】,范围1~120【0x78】,因此byte[10]=0, byte[11]为寄存器数量
            * byte[12] 字节个数 也就是【寄存器数量*2】,范围【2~240】
            * byte[13] byte[14] byte[15] byte[16] byte[...]  具体的数据内容 对应 数据一高位 数据一低位 数据二高位 数据二低位
            * 
            * 【写多个保持寄存器 0x10】Modbus响应结果
            * byte[0] byte[1] 随便指定,PLC返回的前两个字节完全一致
            * byte[2]=0 byte[3]=0 固定为0 代表Modbus标识
            * byte[4] byte[5] 排在byte[5]后面所有字节的个数
            * byte[6] 站号,随便指定,00--FF都可以,PLC返回的保持一致
            * byte[7] 功能码,0x10:写多个保持寄存器
            * byte[8] byte[9] 起始地址
            * byte[10] byte[11] 寄存器数量【设置长度】            
*/

namespace TestProj
{
    /// <summary>
    /// 通过Modbus协议与PLC进行通信,读写指定起始地址的PLC寄存器数据
    /// 兼容西门子PLC、施耐德PLC
    /// </summary>
    public class ModbusTcpUtil
    {
        /// <summary>
        /// 与PLC通信的socket客户端
        /// </summary>
        public Socket socket;
        /// <summary>
        /// 是否已连接上PLC,true:已连接上PLC false:未连接
        /// </summary>
        public bool isConnectPLC = false;
        /// <summary>
        /// 连接PLC
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public bool ConnectPLC(string ip, int port)
        {
            isConnectPLC = false;
            try
            {
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IAsyncResult asyncResult = socket.BeginConnect(ip, port, CallbackConnect, socket);
                asyncResult.AsyncWaitHandle.WaitOne(2000);
                socket.ReceiveTimeout = 2000;//2000ms无数据接收则超时
                return isConnectPLC;
            }
            catch (Exception ex)
            {
                //MessageBox.Show("连接Modbus_TCP失败:" + ex.Message);
                System.Diagnostics.Debug.WriteLine("连接Modbus_TCP失败:" + ex.Message);
                isConnectPLC = false;
            }
            return isConnectPLC;
        }

        /// <summary>
        /// 异步连接PLC
        /// </summary>
        /// <param name="ar"></param>
        private void CallbackConnect(IAsyncResult ar)
        {
            isConnectPLC = false;
            try
            {
                Socket skt = ar.AsyncState as Socket;
                skt.EndConnect(ar);
                isConnectPLC = true;
            }
            catch (Exception ex)
            {
                //MessageBox.Show("连接Modbus_TCP失败:" + ex.Message);
                System.Diagnostics.Debug.WriteLine("连接Modbus_TCP失败:" + ex.Message);
            }
        }

        /// <summary>
        /// 关闭套接字连接
        /// </summary>
        public void CloseConnect() 
        {
            if (this.socket != null)
            {
                try
                {
                    this.socket.Close(1000);
                }
                catch { }
            }
        }

        /// <summary>
        /// 写一个指定起始地址的Modbus值。写入【sbyte,byte,short,ushort】使用【0x06指令码:写单个寄存器】。
        /// 写入【int,uint,long,ulong,float,double】使用【0x10指令码:写多个寄存器】
        /// </summary>
        /// <typeparam name="T">基本的数据类型,如short,int,double等</typeparam>
        /// <param name="startAddress">寄存器起始地址,范围:【0x0000~0xFFFF】</param>
        /// <param name="value">写入的值</param>
        /// <returns>true:写入成功 false:写入失败</returns>
        public bool WriteValue<T>(int startAddress, T value)
        {
            if (socket == null || !socket.Connected)
            {
                System.Diagnostics.Debug.WriteLine("socket为空或者尚未建立与PLC_Modbus的连接...");
                return false;
            }
            if (startAddress < 0 || startAddress > 65535)
            {
                System.Diagnostics.Debug.WriteLine("Modbus的起始地址必须在0~65535之间");
                return false;
            }
            byte[] addrArray = BitConverter.GetBytes((ushort)startAddress);
            //sbyte,byte,short,ushort 占用一个寄存器(Word)范围的可以使用功能码0x06:写单个寄存器
            //int,long,float,double 需要使用两个或两个以上寄存器,因此只能使用功能码0x10:写多个寄存器 其中int,uint,float占用两个寄存器 long,ulong,double占用四个寄存器
            byte[] buffer = new byte[12] { 0x02, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x06, addrArray[1], addrArray[0], 0x00, 0x00 };
            if (typeof(T) == typeof(sbyte))
            {
                sbyte sb = Convert.ToSByte(value);
                byte b = (byte)sb;
                buffer[11] = b;
            }
            else if (typeof(T) == typeof(byte))
            {
                byte b = Convert.ToByte(value);
                buffer[11] = b;
            }
            else if (typeof(T) == typeof(short))
            {
                short s = Convert.ToInt16(value);
                byte[] writeValueArray = BitConverter.GetBytes(s);
                buffer[10] = writeValueArray[1];
                buffer[11] = writeValueArray[0];
            }
            else if (typeof(T) == typeof(ushort))
            {
                ushort us = Convert.ToUInt16(value);
                byte[] writeValueArray = BitConverter.GetBytes(us);
                buffer[10] = writeValueArray[1];
                buffer[11] = writeValueArray[0];
            }
            else if (typeof(T) == typeof(int))
            {
                int i = Convert.ToInt32(value);
                byte[] writeValueArray = BitConverter.GetBytes(i);
                buffer = new byte[17] { 0x02, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x01, 0x10, addrArray[1], addrArray[0], 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00 };
                buffer[13] = writeValueArray[3];
                buffer[14] = writeValueArray[2];
                buffer[15] = writeValueArray[1];
                buffer[16] = writeValueArray[0];
            }
            else if (typeof(T) == typeof(uint))
            {
                uint ui = Convert.ToUInt32(value);
                byte[] writeValueArray = BitConverter.GetBytes(ui);
                buffer = new byte[17] { 0x02, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x01, 0x10, addrArray[1], addrArray[0], 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00 };
                buffer[13] = writeValueArray[3];
                buffer[14] = writeValueArray[2];
                buffer[15] = writeValueArray[1];
                buffer[16] = writeValueArray[0];
            }
            else if (typeof(T) == typeof(long))
            {
                long l = Convert.ToInt64(value);
                byte[] writeValueArray = BitConverter.GetBytes(l);
                buffer = new byte[21] { 0x02, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x10, addrArray[1], addrArray[0], 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
                buffer[13] = writeValueArray[7];
                buffer[14] = writeValueArray[6];
                buffer[15] = writeValueArray[5];
                buffer[16] = writeValueArray[4];
                buffer[17] = writeValueArray[3];
                buffer[18] = writeValueArray[2];
                buffer[19] = writeValueArray[1];
                buffer[20] = writeValueArray[0];
            }
            else if (typeof(T) == typeof(ulong))
            {
                ulong ul = Convert.ToUInt64(value);
                byte[] writeValueArray = BitConverter.GetBytes(ul);
                buffer = new byte[21] { 0x02, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x10, addrArray[1], addrArray[0], 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
                buffer[13] = writeValueArray[7];
                buffer[14] = writeValueArray[6];
                buffer[15] = writeValueArray[5];
                buffer[16] = writeValueArray[4];
                buffer[17] = writeValueArray[3];
                buffer[18] = writeValueArray[2];
                buffer[19] = writeValueArray[1];
                buffer[20] = writeValueArray[0];
            }
            else if (typeof(T) == typeof(float))
            {
                float f = Convert.ToSingle(value);
                byte[] writeValueArray = BitConverter.GetBytes(f);
                buffer = new byte[17] { 0x02, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x01, 0x10, addrArray[1], addrArray[0], 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00 };
                buffer[13] = writeValueArray[3];
                buffer[14] = writeValueArray[2];
                buffer[15] = writeValueArray[1];
                buffer[16] = writeValueArray[0];
            }
            else if (typeof(T) == typeof(double))
            {
                double d = Convert.ToDouble(value);
                byte[] writeValueArray = BitConverter.GetBytes(d);
                buffer = new byte[21] { 0x02, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x10, addrArray[1], addrArray[0], 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
                buffer[13] = writeValueArray[7];
                buffer[14] = writeValueArray[6];
                buffer[15] = writeValueArray[5];
                buffer[16] = writeValueArray[4];
                buffer[17] = writeValueArray[3];
                buffer[18] = writeValueArray[2];
                buffer[19] = writeValueArray[1];
                buffer[20] = writeValueArray[0];
            }
            else
            {
                //暂不考虑 char(就是ushort,两个字节),decimal(十六个字节)等类型
                System.Diagnostics.Debug.WriteLine("写Modbus数据暂不支持其他类型:" + value.GetType());
                return false;
            }
            try
            {
                socket.Send(buffer);
                DisplayBuffer(buffer, buffer.Length, true);
                Thread.Sleep(50);//等待50ms
                byte[] receiveBuffer = new byte[1024];
                int receiveCount = socket.Receive(receiveBuffer);
                DisplayBuffer(receiveBuffer, receiveCount, false);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("接收Modbus的响应数据异常,请查看发送的报文格式是否有误:" + ex.Message);
                return false;
            }
            return true;
        }

        /// <summary>
        /// 从一个指定的起始寄存器地址开始,顺次连续写入字节流【写任意多个寄存器】
        /// </summary>
        /// <param name="startAddress">起始地址</param>
        /// <param name="buffer">要写入的字节数组,buffer数组长度范围:【1~240(0x01~0xF0)】</param>
        /// <returns>true:写入成功 false:写入失败</returns>
        public bool WriteValue(int startAddress, byte[] buffer)
        {
            //分奇数个字节、偶数个字节
            if (socket == null || !socket.Connected)
            {
                System.Diagnostics.Debug.WriteLine("socket为空或者尚未建立与PLC_Modbus的连接...");
                return false;
            }
            if (startAddress < 0 || startAddress > 65535)
            {
                System.Diagnostics.Debug.WriteLine("Modbus的起始地址必须在0~65535之间");
                return false;
            }
            if (buffer == null || buffer.Length < 1 || buffer.Length > 240)
            {
                System.Diagnostics.Debug.WriteLine("写连续寄存器块范围:(1 至120 个寄存器)");//每个寄存器将数据分成两字节
                return false;
            }
            byte[] addrArray = BitConverter.GetBytes((ushort)startAddress);
            //需要写入的寄存器个数
            byte registerCount = (byte)((buffer.Length + 1) / 2);
            //实际写入的字节个数:注意buffer数组的长度为奇数时 需要将最后一个寄存器的高位设置为0
            byte writeCount = (byte)(registerCount * 2);
            byte[] sendBuffer = new byte[13 + writeCount];
            sendBuffer[0] = 0x02;
            sendBuffer[1] = 0x01;
            sendBuffer[5] = (byte)(7 + writeCount);
            sendBuffer[6] = 0x01;
            sendBuffer[7] = 0x10;
            sendBuffer[8] = addrArray[1];
            sendBuffer[9] = addrArray[0];
            sendBuffer[11] = registerCount;
            sendBuffer[12] = writeCount;
            for (int i = 0; i < writeCount - 2; i++)
            {
                sendBuffer[13 + i] = buffer[i];
            }

            //最后两个元素[最后的一个寄存器]的处理
            if (buffer.Length % 2 == 1)
            {
                //如果是奇数个,需要将最后一个寄存器的高位设置为0
                sendBuffer[13 + writeCount - 2] = 0;
                sendBuffer[13 + writeCount - 1] = buffer[buffer.Length - 1];
            }
            else
            {
                //如果是偶数个,则一一对应
                sendBuffer[13 + writeCount - 2] = buffer[buffer.Length - 2];
                sendBuffer[13 + writeCount - 1] = buffer[buffer.Length - 1];
            }

            try
            {
                socket.Send(sendBuffer);
                DisplayBuffer(sendBuffer, sendBuffer.Length, true);
                Thread.Sleep(50);//等待50ms
                byte[] receiveBuffer = new byte[1024];
                int receiveCount = socket.Receive(receiveBuffer);
                DisplayBuffer(receiveBuffer, receiveCount, false);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("接收Modbus的响应数据异常,请查看发送的报文格式是否有误:" + ex.Message);
                return false;
            }
            return true;
        }

        /// <summary>
        /// 读取指定起始地址的值
        /// </summary>
        /// <typeparam name="T">基本的数据类型,如short,int,double等</typeparam>
        /// <param name="startAddress">起始地址</param>
        /// <param name="value">返回的具体指</param>
        /// <returns>true:读取成功 false:读取失败</returns>
        public bool ReadValue<T>(int startAddress, out T value)
        {
            value = default(T);
            if (socket == null || !socket.Connected)
            {
                System.Diagnostics.Debug.WriteLine("socket为空或者尚未建立与PLC_Modbus的连接...");
                return false;
            }
            if (startAddress < 0 || startAddress > 65535)
            {
                System.Diagnostics.Debug.WriteLine("Modbus的起始地址必须在0~65535之间");
                return false;
            }
            byte[] addrArray = BitConverter.GetBytes((ushort)startAddress);
            byte wordLength = 0;//读取的地址个数【多少个字Word】 int,float需要两个字 long,double需要四个字
            if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(byte) || typeof(T) == typeof(short) || typeof(T) == typeof(ushort))
            {
                wordLength = 1;
            }
            else if (typeof(T) == typeof(int) || typeof(T) == typeof(uint) || typeof(T) == typeof(float))
            {
                wordLength = 2;
            }
            else if (typeof(T) == typeof(long) || typeof(T) == typeof(ulong) || typeof(T) == typeof(double))
            {
                wordLength = 4;
            }
            else
            {
                //暂不考虑 char(就是ushort,两个字节),decimal(十六个字节)等类型
                System.Diagnostics.Debug.WriteLine("读Modbus数据暂不支持其他类型:" + value.GetType());
                return false;
            }
            byte[] sendBuffer = new byte[12] { 0x02, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, addrArray[1], addrArray[0], 0x00, wordLength };
            socket.Send(sendBuffer);

            DisplayBuffer(sendBuffer, sendBuffer.Length, true);
            Thread.Sleep(50);//等待50ms

            byte[] receiveBuffer = new byte[1024];
            try
            {
                //协议错误时 Receive函数将发生异常
                int receiveCount = socket.Receive(receiveBuffer);
                DisplayBuffer(receiveBuffer, receiveCount, false);
                //receiveBuffer[8] : 真实数据的字节流总个数
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("接收Modbus的响应数据异常,请查看发送的报文格式是否有误:" + ex.Message);
                return false;
            }

            if (typeof(T) == typeof(sbyte))
            {
                byte b = receiveBuffer[10];
                sbyte sb = (sbyte)b;
                value = (T)(object)sb;
            }
            else if (typeof(T) == typeof(byte))
            {
                byte b = receiveBuffer[10];
                value = (T)(object)b;
            }
            else if (typeof(T) == typeof(short))
            {
                short s = BitConverter.ToInt16(new byte[] { receiveBuffer[10], receiveBuffer[9] }, 0);
                value = (T)(object)s;
            }
            else if (typeof(T) == typeof(ushort))
            {
                ushort us = BitConverter.ToUInt16(new byte[] { receiveBuffer[10], receiveBuffer[9] }, 0);
                value = (T)(object)us;
            }
            else if (typeof(T) == typeof(int))
            {
                int i = BitConverter.ToInt32(new byte[] { receiveBuffer[12], receiveBuffer[11], receiveBuffer[10], receiveBuffer[9] }, 0);
                value = (T)(object)i;
            }
            else if (typeof(T) == typeof(uint))
            {
                uint ui = BitConverter.ToUInt32(new byte[] { receiveBuffer[12], receiveBuffer[11], receiveBuffer[10], receiveBuffer[9] }, 0);
                value = (T)(object)ui;
            }
            else if (typeof(T) == typeof(long))
            {
                long l = BitConverter.ToInt64(new byte[] { receiveBuffer[16], receiveBuffer[15], receiveBuffer[14], receiveBuffer[13], receiveBuffer[12], receiveBuffer[11], receiveBuffer[10], receiveBuffer[9] }, 0);
                value = (T)(object)l;
            }
            else if (typeof(T) == typeof(ulong))
            {
                ulong ul = BitConverter.ToUInt64(new byte[] { receiveBuffer[16], receiveBuffer[15], receiveBuffer[14], receiveBuffer[13], receiveBuffer[12], receiveBuffer[11], receiveBuffer[10], receiveBuffer[9] }, 0);
                value = (T)(object)ul;
            }
            else if (typeof(T) == typeof(float))
            {
                float f = BitConverter.ToSingle(new byte[] { receiveBuffer[12], receiveBuffer[11], receiveBuffer[10], receiveBuffer[9] }, 0);
                value = (T)(object)f;
            }
            else if (typeof(T) == typeof(double))
            {
                double d = BitConverter.ToDouble(new byte[] { receiveBuffer[16], receiveBuffer[15], receiveBuffer[14], receiveBuffer[13], receiveBuffer[12], receiveBuffer[11], receiveBuffer[10], receiveBuffer[9] }, 0);
                value = (T)(object)d;
            }
            return true;
        }

        /// <summary>
        /// 从起始地址开始,读取一段字节流
        /// </summary>
        /// <param name="startAddress">起始寄存器地址</param>
        /// <param name="length">读取的字节个数</param>
        /// <param name="value">返回的字节流数据</param>
        /// <returns>true:读取成功 false:读取失败</returns>
        public bool ReadValue(int startAddress, int length, out byte[] value)
        {
            value = new byte[length];
            if (socket == null || !socket.Connected)
            {
                System.Diagnostics.Debug.WriteLine("socket为空或者尚未建立与PLC_Modbus的连接...");
                return false;
            }
            //读保持寄存器0x03读取的寄存器数量的范围为 1~125。因一个寄存器【一个Word】存放两个字节,因此 字节数组的长度范围 为 1~250
            if (length < 1 || length > 250)
            {
                System.Diagnostics.Debug.WriteLine("返回的字节数组的长度范围为 1~250");
                return false;
            }
            if (startAddress < 0 || startAddress > 65535)
            {
                System.Diagnostics.Debug.WriteLine("Modbus的起始地址必须在0~65535之间");
                return false;
            }
            byte[] addrArray = BitConverter.GetBytes((ushort)startAddress);
            //读取的寄存器个数: 如果length为偶数 则为 length/2 如果length为奇数,则为(length+1)/2。因整数相除,结果不考虑余数,所以如下通用:
            byte registerCount = (byte)((length + 1) / 2);
            byte[] sendBuffer = new byte[12] { 0x02, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, addrArray[1], addrArray[0], 0x00, registerCount };
            socket.Send(sendBuffer);

            DisplayBuffer(sendBuffer, sendBuffer.Length, true);
            Thread.Sleep(50);//等待50ms

            byte[] receiveBuffer = new byte[1024];
            try
            {
                int receiveCount = socket.Receive(receiveBuffer);
                DisplayBuffer(receiveBuffer, receiveCount, false);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("接收Modbus的响应数据异常,请查看发送的报文格式是否有误:" + ex.Message);
                return false;
            }
            //接收到的实际数据字节个数
            byte receiveLength = receiveBuffer[8];
            if (receiveLength != registerCount * 2)
            {
                System.Diagnostics.Debug.WriteLine("解析接收数据非法,接收的实际数据长度【不是】读取寄存器数量的2倍");
                return false;
            }
            value = new byte[receiveLength];
            for (int i = 0; i < receiveLength; i++)
            {
                value[i] = receiveBuffer[9 + i];
            }
            return true;
        }

        /// <summary>
        /// 读取起始地址开始存储的条码,默认读取最大长度为100的条码字符串
        /// </summary>
        /// <param name="startAddress">起始地址</param>
        /// <param name="barcode">返回的条码字符串</param>
        /// <returns>true:读取成功 false:读取失败</returns>
        public bool ReadBarcode(int startAddress, out string barcode)
        {
            barcode = string.Empty;
            byte[] dataBuffer = new byte[100];
            bool result = ReadValue(startAddress, 100, out dataBuffer);
            if (!result)
            {
                return false;
            }
            List<byte> list = new List<byte>();
            for (int i = 0; i < dataBuffer.Length; i += 2)
            {
                //因一个寄存器存储的数据 是一个字Word,分成两个字节Byte【高位字节、低位字节】,存储的条码是低位在前,因此每隔两个需要交换顺序
                list.Add(dataBuffer[i + 1]);
                list.Add(dataBuffer[i]);
                //遇到'\0'后面的数据无效
                if (dataBuffer[i] == 0 || dataBuffer[i + 1] == 0)
                {
                    break;
                }
            }
            byte[] actualBuffer = list.ToArray();
            barcode = Encoding.ASCII.GetString(actualBuffer).Trim('\0').Trim();
            return result;
        }

        /// <summary>
        /// 打印Debug发送或接收字节数组信息
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="count"></param>
        /// <param name="isSend"></param>
        public void DisplayBuffer(byte[] buffer, int count, bool isSend)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append((isSend ? "发送" : "接收到") + "的字节流:\n");
            for (int i = 0; i < count; i++)
            {
                if (i > 0)
                {
                    sb.Append(" ");
                }
                sb.Append(buffer[i].ToString("X2"));
            }
            string content = sb.ToString();
            System.Diagnostics.Debug.WriteLine(content);
        }
    }
}

二、新建测试Winform窗体 FormDemo.cs。主要调用ModbusTcpUtil测试代码如下【设计器自动生成的代码忽略】:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TestProj
{
    public partial class FormDemo : Form
    {
        ModbusTcpUtil modbusUtil = new ModbusTcpUtil();
        public FormDemo()
        {
            InitializeComponent();
            cboDataType.SelectedIndex = 1;
        }


        private void btnConnect_Click(object sender, EventArgs e)
        {
            btnConnect.Enabled = false;
            string ip = txtIP.Text;
            int port = int.Parse(txtPort.Text);
            if (modbusUtil.ConnectPLC(ip, port))
            {
                DisplayRichTextboxContentAndScroll("连接Modbus_TCP成功");
            }
            else 
            {
                DisplayRichTextboxContentAndScroll("连接Modbus_TCP失败,请检查网络以及IP设置");
                btnConnect.Enabled = true;
            }
        }

        private void btnRead_Click(object sender, EventArgs e)
        {
            int startAddress = int.Parse(txtStartAddress.Text);
            string curType = cboDataType.Text;
            try
            {
                switch (curType)
                {
                    case "Byte(Byte)":
                        byte b;
                        modbusUtil.ReadValue<byte>(startAddress, out b);
                        txtValue.Text = b.ToString();
                        break;
                    case "Int16(Int)":
                        short s;
                        modbusUtil.ReadValue<short>(startAddress, out s);
                        txtValue.Text = s.ToString();
                        break;
                    case "UInt16(Word)":
                        ushort us;
                        modbusUtil.ReadValue<ushort>(startAddress, out us);
                        txtValue.Text = us.ToString();
                        break;
                    case "Int32(DInt)":
                        int i;
                        modbusUtil.ReadValue<int>(startAddress, out i);
                        txtValue.Text = i.ToString();
                        break;
                    case "UInt32(DWord)":
                        uint ui;
                        modbusUtil.ReadValue<uint>(startAddress, out ui);
                        txtValue.Text = ui.ToString();
                        break;
                    case "Float(Real)":
                        float f;
                        modbusUtil.ReadValue<float>(startAddress, out f);
                        txtValue.Text = f.ToString();
                        break;
                    case "Int64":
                        long l;
                        modbusUtil.ReadValue<long>(startAddress, out l);
                        txtValue.Text = l.ToString();
                        break;
                    case "UInt64":
                        ulong ul;
                        modbusUtil.ReadValue<ulong>(startAddress, out ul);
                        txtValue.Text = ul.ToString();
                        break;
                    case "Double":
                        double d;
                        modbusUtil.ReadValue<double>(startAddress, out d);
                        txtValue.Text = d.ToString();
                        break;
                }
            }
            catch (Exception ex)
            {
                DisplayRichTextboxContentAndScroll("写寄存器出现错误,请检查:\n" + ex.Message);
            }
        }

        private void btnWrite_Click(object sender, EventArgs e)
        {
            int startAddress = int.Parse(txtStartAddress.Text);
            string curType = cboDataType.Text;
            try
            {
                switch (curType)
                {
                    case "Byte(Byte)":
                        modbusUtil.WriteValue<byte>(startAddress, byte.Parse(txtValue.Text));
                        break;
                    case "Int16(Int)":
                        modbusUtil.WriteValue<short>(startAddress, short.Parse(txtValue.Text));
                        break;
                    case "UInt16(Word)":
                        modbusUtil.WriteValue<ushort>(startAddress, ushort.Parse(txtValue.Text));
                        break;
                    case "Int32(DInt)":
                        modbusUtil.WriteValue<int>(startAddress, int.Parse(txtValue.Text));
                        break;
                    case "UInt32(DWord)":
                        modbusUtil.WriteValue<uint>(startAddress, uint.Parse(txtValue.Text));
                        break;
                    case "Float(Real)":
                        modbusUtil.WriteValue<float>(startAddress, float.Parse(txtValue.Text));
                        break;
                    case "Int64":
                        modbusUtil.WriteValue<long>(startAddress, long.Parse(txtValue.Text));
                        break;
                    case "UInt64":
                        modbusUtil.WriteValue<ulong>(startAddress, ulong.Parse(txtValue.Text));
                        break;
                    case "Double":
                        modbusUtil.WriteValue<double>(startAddress, double.Parse(txtValue.Text));
                        break;
                }
            }
            catch (Exception ex) 
            {
                DisplayRichTextboxContentAndScroll("写寄存器出现错误,请检查:\n" + ex.Message);
            }
        }

        /// <summary>
        /// 显示序号 大于100时 就清空文本
        /// </summary>
        int displaySequence = 0;
        /// <summary>
        /// 显示内容并滚动到当前位置
        /// </summary>
        /// <param name="addContent"></param>
        /// <param name="wd">为null时不添加新行,非空时添加一行</param>
        /// <param name="isNG">是否是有异常的数据航</param>
        private void DisplayRichTextboxContentAndScroll(string addContent)
        {
            displaySequence++;
            this.Invoke(new MethodInvoker(() =>
            {
                if (displaySequence >= 100)
                {
                    rtxtDisplay.Clear();
                    displaySequence = 0;
                }
                rtxtDisplay.AppendText(addContent + "\n");
                rtxtDisplay.ScrollToCaret();
            }));
        }

        private void FormDemo_FormClosing(object sender, FormClosingEventArgs e)
        {
            modbusUtil.CloseConnect();
        }

        private void btnReadMultiple_Click(object sender, EventArgs e)
        {
            int startAddress = int.Parse(txtAddressForRead.Text);
            int length = int.Parse(txtByteCount.Text);
            byte[] buffer;
            modbusUtil.ReadValue(startAddress, length, out buffer);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < buffer.Length; i++)
            {
                if (i > 0)
                {
                    sb.Append(" ");
                }
                sb.Append(buffer[i].ToString("X2"));
            }
            string content = sb.ToString();
            rtxtReadData.Text = content;
        }

        private void btnWriteMultiple_Click(object sender, EventArgs e)
        {
            int startAddress = int.Parse(txtAddressForWrite.Text);
            string[] dataArray = rtxtWriteData.Text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            byte[] buffer = new byte[dataArray.Length];
            for (int i = 0; i < buffer.Length; i++)
            {
                buffer[i] = Convert.ToByte(dataArray[i], 16);
            }
            bool result = modbusUtil.WriteValue(startAddress, buffer);
            if (result)
            {
                DisplayRichTextboxContentAndScroll("写入连续字节流成功...");
            }
            else 
            {
                DisplayRichTextboxContentAndScroll("写入连续字节流失败...");
            }
        }
    }
}


 

猜你喜欢

转载自blog.csdn.net/ylq1045/article/details/84792855