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

Posted 厦门德仔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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, truemodbus网关 串口采集传输

求助一个MODBUS TCP/IP报文解析。

西门子200MODBUS通信电度表读取地址怎么设置

工业通信网关的实现

怎样读取西门子1500plc modbus tcp 发布的信息

ModbusKepServer西门子PLC通信