15181692257 发表于 2021-12-1 11:50

C#实现MODBUS TCP 通信 第二章 (程序内实现)

上一章,我们对MODBUS 的报文已经有初步了解,本章就在程序内来具体实现,我先建立了一个MODBUS 方法类,类中包含有常用的读写方法,以及数据处理方法,由于我是用在实际生产中,所以对寄存器的值有限制,只能是正数,所以还有数据转换方法,可以作为参考,类的具体代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace ModbusTCPDemo
{
    public class ModbusTCP
    {
      private Socket socket = null;
      public bool Connect(string ip, int port)
      {
            //实例化Socket
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                socket.Connect(IPAddress.Parse(ip), port);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
      }

      public void DisConnect()
      {
            if (socket != null)
            {
                socket.Close();
            }
      }
      //读取寄存器
      public ushort[] ReadKeeepReg(ushort start, ushort length)
      {
            //拼接
            List<byte> SendCommand = new List<byte>();
            //事物处理标识符
            SendCommand.AddRange(new byte[] { 0x00, 0x00 });
            //协议标识符
            SendCommand.AddRange(new byte[] { 0x00, 0x00 });
            //长度
            SendCommand.AddRange(new byte[] { 0x00, 0x06 });
            //单元标识符
            SendCommand.Add(0x01);
            //功能码
            SendCommand.Add(0x03);
            //起始地址
            SendCommand.Add((byte)(start / 256));
            SendCommand.Add((byte)(start % 256));
            //结束地址-长度
            SendCommand.Add((byte)(length / 256));
            SendCommand.Add((byte)(length % 256));
            //发送
            socket.Send(SendCommand.ToArray());
            //接收
            byte[] buffer = new byte;
            int count = socket.Receive(buffer, SocketFlags.None);
            //验证-截取
            byte[] result = new byte;
            Array.Copy(buffer, 0, result, 0, count);
            if (count == 9 + 2 * length)
            {
                if (result == 0x03)
                {
                  //解析
                  ushort[] data = new ushort;
                  for (int i = 0; i < data.Length; i++)
                  {
                        data = BitConverter.ToUInt16(new byte[] { result, result },0);

                  }
                  return data;
                }
                return null;
            }
            return null;
      }

      //写入单个寄存器
      public List<byte> WriteInputRge(short start, short writedata)
      {
            //拼接
            List<byte> SendCommand = new List<byte>();
            //事物处理标识符
            SendCommand.AddRange(new byte[] { 0x00, 0x00 });
            //协议标识符
            SendCommand.AddRange(new byte[] { 0x00, 0x00 });
            //长度
            SendCommand.AddRange(new byte[] { 0x00, 0x06 });
            //单元标识符
            SendCommand.Add(0x01);
            //功能码
            SendCommand.Add(0x06);
            //寄存器地址
            SendCommand.Add((byte)(start / 256));
            SendCommand.Add((byte)(start % 256));
            //具体数据
            SendCommand.Add((byte)(writedata / 256));
            SendCommand.Add((byte)(writedata % 256));
            //发送
            socket.Send(SendCommand.ToArray());
            //接收
            byte[] buffer = new byte;
            int count = socket.Receive(buffer, SocketFlags.None);
            //验证-截取
            byte[] result = new byte;
            Array.Copy(buffer, 0, result, 0, count);
            List<byte> ReceiveCommand = result.ToList();
            if (ReceiveCommand == SendCommand)
            {
                return ReceiveCommand;
            }
            return null;

      }

      //写入多个寄存器
      public List<byte> BatchWriteInputRge(short start, short length,short[] data)
      {
            //拼接
            List<byte> SendCommand = new List<byte>();
            //事物处理标识符
            SendCommand.AddRange(new byte[] { 0x00, 0x00 });
            //协议标识符
            SendCommand.AddRange(new byte[] { 0x00, 0x00 });
            //长度
            SendCommand.Add((byte)((9 + (length - 1) * 2) / 256));
            SendCommand.Add((byte)((9 + (length - 1) * 2) % 256));
            //单元标识符
            SendCommand.Add(0x01);
            //功能码
            SendCommand.Add(0x10);
            //寄存器地址
            SendCommand.Add((byte)(start / 256));
            SendCommand.Add((byte)(start % 256));
            //寄存器个数
            SendCommand.Add((byte)(length / 256));
            SendCommand.Add((byte)(length % 256));
            //写字节个数
            SendCommand.Add((byte)(data.Length*2));
            for (int i = 0; i < data.Length; i++)
            {
                SendCommand.Add((byte)(data / 256));
                SendCommand.Add((byte)(data % 256));
            }

            //发送
            socket.Send(SendCommand.ToArray());
            //接收
            byte[] buffer = new byte;
            int count = socket.Receive(buffer, SocketFlags.None);
            //验证-截取
            byte[] result = new byte;
            Array.Copy(buffer, 0, result, 0, count);
            List<byte> ReceiveCommand = result.ToList();
            if (ReceiveCommand == SendCommand)
            {
                return ReceiveCommand;
            }
            return null;

            //00 00   00 00   00 0B   01   10   00 0000 020400 9911 55
      }

      //数据拆分方法,将一个32位int 转换成4个8位int
      public int[] Int32BitTo8Bit(int i)
      {
            int[] ints = new int;
            if (i > 0)
            {
                int n1 = i & 0xffff;
                int n2 = i >> 16;
                ints = n1 & 0xff;
                ints = n1 >> 8;
                ints = n2 & 0xff;
                ints = n2 >> 8;

                int ii = (((ints << 8) + ints) << 16) + ((ints << 8) + ints);

                return ints;
            }
            return null;
      }

    }
}

实现步骤:
1.建立连接
2.调用方法
ModbusTCP modbusTCP = new ModbusTCP(); //实例化ModbusTCP 类
if (modbusTCP.Connect("127.0.0.1", 502))//调用连接方法
{
int[] data_int = modbusTCP.Int32BitTo8Bit(Convert.ToInt32(zcl));
                  short[] data_short = new short;
                  for (int i = 0; i < data_int.Length; i++)
                  {
                        data_short = (short)data_int;
                  }
                  
                  System.Collections.Generic.List<byte> val = modbusTCP.BatchWriteInputRge(0, (short)data_short.Length, data_short);
                  if (val != null)
                  {
                        string s="";
                        for (int i = 0; i < val.Count; i++)
                        {
                            s +=" " + val.ToString();
                        }
                        Console.WriteLine("写入成功返回:"+s);
                  }
                  else
                  {
                        Console.WriteLine("写入失败!");
                  }
}

ipc2008 发表于 2021-12-1 15:51

我也来贴一段,英文注释,三菱PLC

using System;
using System.Net;
using System.Net.Sockets;

namespace WithstandVoltage
{
    /// <summary>
    /// Modbus TCP common driver class. This class implements a modbus TCP master driver.
    /// It supports the following commands:
    ///
    /// Read coils
    /// Read discrete inputs
    /// Write single coil
    /// Write multiple cooils
    /// Read holding register
    /// Read input register
    /// Write single register
    /// Write multiple register
    ///
    /// All commands can be sent in synchronous or asynchronous mode. If a value is accessed
    /// in synchronous mode the program will stop and wait for slave to response. If the
    /// slave didn't answer within a specified time a timeout exception is called.
    /// The class uses multi threading for both synchronous and asynchronous access. For
    /// the communication two lines are created. This is necessary because the synchronous
    /// thread has to wait for a previous command to finish.
    /// The synchronous channel can be disabled during connection. This can be necessary when
    /// the slave only supports one connection.
    ///
    /// </summary>
    public class Master
    {
      // ------------------------------------------------------------------------
      // Constants for access
      private const byte fctReadCoil = 1;

      private const byte fctReadDiscreteInputs = 2;
      private const byte fctReadHoldingRegister = 3;
      private const byte fctReadInputRegister = 4;
      private const byte fctWriteSingleCoil = 5;
      private const byte fctWriteSingleRegister = 6;
      private const byte fctWriteMultipleCoils = 15;
      private const byte fctWriteMultipleRegister = 16;
      private const byte fctReadWriteMultipleRegister = 23;

      /// <summary>Constant for exception illegal function.</summary>
      public const byte excIllegalFunction = 1;

      /// <summary>Constant for exception illegal data address.</summary>
      public const byte excIllegalDataAdr = 2;

      /// <summary>Constant for exception illegal data value.</summary>
      public const byte excIllegalDataVal = 3;

      /// <summary>Constant for exception slave device failure.</summary>
      public const byte excSlaveDeviceFailure = 4;

      /// <summary>Constant for exception acknowledge.</summary>
      public const byte excAck = 5;

      /// <summary>Constant for exception slave is busy/booting up.</summary>
      public const byte excSlaveIsBusy = 6;

      /// <summary>Constant for exception gate path unavailable.</summary>
      public const byte excGatePathUnavailable = 10;

      /// <summary>Constant for exception not connected.</summary>
      public const byte excExceptionNotConnected = 253;

      /// <summary>Constant for exception connection lost.</summary>
      public const byte excExceptionConnectionLost = 254;

      /// <summary>Constant for exception response timeout.</summary>
      public const byte excExceptionTimeout = 255;

      /// <summary>Constant for exception wrong offset.</summary>
      private const byte excExceptionOffset = 128;

      /// <summary>Constant for exception send failt.</summary>
      private const byte excSendFailt = 100;

      // ------------------------------------------------------------------------
      // Private declarations
      private static ushort _timeout = 500;

      private static ushort _refresh = 10;
      private static bool _connected = false;
      private static bool _no_sync_connection = false;

      private Socket tcpAsyCl;
      private byte[] tcpAsyClBuffer = new byte;

      private Socket tcpSynCl;
      private byte[] tcpSynClBuffer = new byte;

      // ------------------------------------------------------------------------
      /// <summary>Response data event. This event is called when new data arrives</summary>
      public delegate void ResponseData(ushort id, byte unit, byte function, byte[] data);

      /// <summary>Response data event. This event is called when new data arrives</summary>
      public event ResponseData OnResponseData;

      /// <summary>Exception data event. This event is called when the data is incorrect</summary>
      public delegate void ExceptionData(ushort id, byte unit, byte function, byte exception);

      /// <summary>Exception data event. This event is called when the data is incorrect</summary>
      public event ExceptionData OnException;

      /// <summary>Response data event. This event is called when new data arrives</summary>
      public delegate void DataStreamPrinf(string Msg, byte[] data);

      /// <summary>Response data event. This event is called when new data arrives</summary>
      public event DataStreamPrinf onDataStreamPrinf;

      // ------------------------------------------------------------------------
      /// <summary>Response timeout. If the slave didn't answers within in this time an exception is called.</summary>
      /// <value>The default value is 500ms.</value>
      public ushort timeout
      {
            get { return _timeout; }
            set { _timeout = value; }
      }

      // ------------------------------------------------------------------------
      /// <summary>Refresh timer for slave answer. The class is polling for answer every X ms.</summary>
      /// <value>The default value is 10ms.</value>
      public ushort refresh
      {
            get { return _refresh; }
            set { _refresh = value; }
      }

      // ------------------------------------------------------------------------
      /// <summary>Displays the state of the synchronous channel</summary>
      /// <value>True if channel was diabled during connection.</value>
      public bool NoSyncConnection
      {
            get { return _no_sync_connection; }
      }

      // ------------------------------------------------------------------------
      /// <summary>Shows if a connection is active.</summary>
      public bool connected
      {
            get { return _connected; }
      }

      // ------------------------------------------------------------------------
      /// <summary>Create master instance without parameters.</summary>
      public Master()
      {
      }

      // ------------------------------------------------------------------------
      /// <summary>Create master instance with parameters.</summary>
      /// <param name="ip">IP adress of modbus slave.</param>
      /// <param name="port">Port number of modbus slave. Usually port 502 is used.</param>
      public Master(string ip, ushort port)
      {
            connect(ip, port, false);
      }

      // ------------------------------------------------------------------------
      /// <summary>Create master instance with parameters.</summary>
      /// <param name="ip">IP adress of modbus slave.</param>
      /// <param name="port">Port number of modbus slave. Usually port 502 is used.</param>
      /// <param name="no_sync_connection">Disable sencond connection for synchronous requests</param>
      public Master(string ip, ushort port, bool no_sync_connection)
      {
            connect(ip, port, no_sync_connection);
      }

      // ------------------------------------------------------------------------
      /// <summary>Start connection to slave.</summary>
      /// <param name="ip">IP adress of modbus slave.</param>
      /// <param name="port">Port number of modbus slave. Usually port 502 is used.</param>
      /// <param name="no_sync_connection">Disable sencond connection for synchronous requests</param>
      public void connect(string ip, ushort port, bool no_sync_connection)
      {
            try
            {
                IPAddress _ip;
                _no_sync_connection = no_sync_connection;
                if (IPAddress.TryParse(ip, out _ip) == false)
                {
                  IPHostEntry hst = Dns.GetHostEntry(ip);
                  ip = hst.AddressList.ToString();
                }
                // ----------------------------------------------------------------
                // Connect asynchronous client
                tcpAsyCl = new Socket(IPAddress.Parse(ip).AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                tcpAsyCl.Connect(new IPEndPoint(IPAddress.Parse(ip), port));
                tcpAsyCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, _timeout);
                tcpAsyCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, _timeout);
                tcpAsyCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, 1);
                // ----------------------------------------------------------------
                // Connect synchronous client
                if (!_no_sync_connection)
                {
                  tcpSynCl = new Socket(IPAddress.Parse(ip).AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                  tcpSynCl.Connect(new IPEndPoint(IPAddress.Parse(ip), port));
                  tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, _timeout);
                  tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, _timeout);
                  tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, 1);
                }
                _connected = true;
            }
            catch (System.IO.IOException error)
            {
                _connected = false;
                throw (error);
            }
      }

      // ------------------------------------------------------------------------
      /// <summary>Stop connection to slave.</summary>
      public void disconnect()
      {
            Dispose();
      }

      // ------------------------------------------------------------------------
      /// <summary>Destroy master instance.</summary>
      ~Master()
      {
            Dispose();
      }

      // ------------------------------------------------------------------------
      /// <summary>Destroy master instance</summary>
      public void Dispose()
      {
            if (tcpAsyCl != null)
            {
                if (tcpAsyCl.Connected)
                {
                  try { tcpAsyCl.Shutdown(SocketShutdown.Both); }
                  catch { }
                  tcpAsyCl.Close();
                }
                tcpAsyCl = null;
            }
            if (tcpSynCl != null)
            {
                if (tcpSynCl.Connected)
                {
                  try { tcpSynCl.Shutdown(SocketShutdown.Both); }
                  catch { }
                  tcpSynCl.Close();
                }
                tcpSynCl = null;
            }
      }

      internal void CallException(ushort id, byte unit, byte function, byte exception)
      {
            if ((tcpAsyCl == null) || (tcpSynCl == null && _no_sync_connection)) return;
            if (exception == excExceptionConnectionLost)
            {
                tcpSynCl = null;
                tcpAsyCl = null;
            }
            if (OnException != null) OnException(id, unit, function, exception);
      }

      internal static UInt16 SwapUInt16(UInt16 inValue)
      {
            return (UInt16)(((inValue & 0xff00) >> 8) |
                     ((inValue & 0x00ff) << 8));
      }

      // ------------------------------------------------------------------------
      /// <summary>Read coils from slave asynchronous. The result is given in the response function.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address from where the data read begins.</param>
      /// <param name="numInputs">Length of data.</param>
      public void ReadCoils(ushort id, byte unit, ushort startAddress, ushort numInputs)
      {
            if (numInputs > 2000)
            {
                CallException(id, unit, fctReadCoil, excIllegalDataVal);
                return;
            }
            WriteAsyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadCoil), id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Read coils from slave synchronous.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address from where the data read begins.</param>
      /// <param name="numInputs">Length of data.</param>
      /// <param name="values">Contains the result of function.</param>
      public void ReadCoils(ushort id, byte unit, ushort startAddress, ushort numInputs, ref byte[] values)
      {
            if (numInputs > 2000)
            {
                CallException(id, unit, fctReadCoil, excIllegalDataVal);
                return;
            }
            values = WriteSyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadCoil), id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Read discrete inputs from slave asynchronous. The result is given in the response function.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address from where the data read begins.</param>
      /// <param name="numInputs">Length of data.</param>
      public void ReadDiscreteInputs(ushort id, byte unit, ushort startAddress, ushort numInputs)
      {
            if (numInputs > 2000)
            {
                CallException(id, unit, fctReadDiscreteInputs, excIllegalDataVal);
                return;
            }
            WriteAsyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadDiscreteInputs), id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Read discrete inputs from slave synchronous.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address from where the data read begins.</param>
      /// <param name="numInputs">Length of data.</param>
      /// <param name="values">Contains the result of function.</param>
      public void ReadDiscreteInputs(ushort id, byte unit, ushort startAddress, ushort numInputs, ref byte[] values)
      {
            if (numInputs > 2000)
            {
                CallException(id, unit, fctReadDiscreteInputs, excIllegalDataVal);
                return;
            }
            values = WriteSyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadDiscreteInputs), id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Read holding registers from slave asynchronous. The result is given in the response function.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address from where the data read begins.</param>
      /// <param name="numInputs">Length of data.</param>
      public void ReadHoldingRegister(ushort id, byte unit, ushort startAddress, ushort numInputs)
      {
            if (numInputs > 125)
            {
                CallException(id, unit, fctReadHoldingRegister, excIllegalDataVal);
                return;
            }
            WriteAsyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadHoldingRegister), id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Read holding registers from slave synchronous.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address from where the data read begins.</param>
      /// <param name="numInputs">Length of data.</param>
      /// <param name="values">Contains the result of function.</param>
      public void ReadHoldingRegister(ushort id, byte unit, ushort startAddress, ushort numInputs, ref byte[] values)
      {
            if (numInputs > 125)
            {
                CallException(id, unit, fctReadHoldingRegister, excIllegalDataVal);
                return;
            }
            values = WriteSyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadHoldingRegister), id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Read input registers from slave asynchronous. The result is given in the response function.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address from where the data read begins.</param>
      /// <param name="numInputs">Length of data.</param>
      public void ReadInputRegister(ushort id, byte unit, ushort startAddress, ushort numInputs)
      {
            if (numInputs > 125)
            {
                CallException(id, unit, fctReadInputRegister, excIllegalDataVal);
                return;
            }
            WriteAsyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadInputRegister), id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Read input registers from slave synchronous.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address from where the data read begins.</param>
      /// <param name="numInputs">Length of data.</param>
      /// <param name="values">Contains the result of function.</param>
      public void ReadInputRegister(ushort id, byte unit, ushort startAddress, ushort numInputs, ref byte[] values)
      {
            if (numInputs > 125)
            {
                CallException(id, unit, fctReadInputRegister, excIllegalDataVal);
                return;
            }
            values = WriteSyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadInputRegister), id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Write single coil in slave asynchronous. The result is given in the response function.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address from where the data read begins.</param>
      /// <param name="OnOff">Specifys if the coil should be switched on or off.</param>
      public void WriteSingleCoils(ushort id, byte unit, ushort startAddress, bool OnOff)
      {
            byte[] data;
            data = CreateWriteHeader(id, unit, startAddress, 1, 1, fctWriteSingleCoil);
            if (OnOff == true) data = 255;
            else data = 0;
            WriteAsyncData(data, id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Write single coil in slave synchronous.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address from where the data read begins.</param>
      /// <param name="OnOff">Specifys if the coil should be switched on or off.</param>
      /// <param name="result">Contains the result of the synchronous write.</param>
      public void WriteSingleCoils(ushort id, byte unit, ushort startAddress, bool OnOff, ref byte[] result)
      {
            byte[] data;
            data = CreateWriteHeader(id, unit, startAddress, 1, 1, fctWriteSingleCoil);
            if (OnOff == true) data = 255;
            else data = 0;
            result = WriteSyncData(data, id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Write multiple coils in slave asynchronous. The result is given in the response function.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address from where the data read begins.</param>
      /// <param name="numBits">Specifys number of bits.</param>
      /// <param name="values">Contains the bit information in byte format.</param>
      public void WriteMultipleCoils(ushort id, byte unit, ushort startAddress, ushort numBits, byte[] values)
      {
            ushort numBytes = Convert.ToUInt16(values.Length);
            if (numBytes > 250 || numBits > 2000)
            {
                CallException(id, unit, fctWriteMultipleCoils, excIllegalDataVal);
                return;
            }

            byte[] data;
            data = CreateWriteHeader(id, unit, startAddress, numBits, (byte)(numBytes + 2), fctWriteMultipleCoils);
            Array.Copy(values, 0, data, 13, numBytes);
            WriteAsyncData(data, id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Write multiple coils in slave synchronous.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address from where the data read begins.</param>
      /// <param name="numBits">Specifys number of bits.</param>
      /// <param name="values">Contains the bit information in byte format.</param>
      /// <param name="result">Contains the result of the synchronous write.</param>
      public void WriteMultipleCoils(ushort id, byte unit, ushort startAddress, ushort numBits, byte[] values, ref byte[] result)
      {
            ushort numBytes = Convert.ToUInt16(values.Length);
            if (numBytes > 250 || numBits > 2000)
            {
                CallException(id, unit, fctWriteMultipleCoils, excIllegalDataVal);
                return;
            }

            byte[] data;
            data = CreateWriteHeader(id, unit, startAddress, numBits, (byte)(numBytes + 2), fctWriteMultipleCoils);
            Array.Copy(values, 0, data, 13, numBytes);
            result = WriteSyncData(data, id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Write single register in slave asynchronous. The result is given in the response function.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address to where the data is written.</param>
      /// <param name="values">Contains the register information.</param>
      public void WriteSingleRegister(ushort id, byte unit, ushort startAddress, byte[] values)
      {
            byte[] data;
            data = CreateWriteHeader(id, unit, startAddress, 1, 1, fctWriteSingleRegister);
            data = values;
            data = values;
            WriteAsyncData(data, id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Write single register in slave synchronous.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address to where the data is written.</param>
      /// <param name="values">Contains the register information.</param>
      /// <param name="result">Contains the result of the synchronous write.</param>
      public void WriteSingleRegister(ushort id, byte unit, ushort startAddress, byte[] values, ref byte[] result)
      {
            byte[] data;
            data = CreateWriteHeader(id, unit, startAddress, 1, 1, fctWriteSingleRegister);
            data = values;
            data = values;
            result = WriteSyncData(data, id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Write multiple registers in slave asynchronous. The result is given in the response function.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address to where the data is written.</param>
      /// <param name="values">Contains the register information.</param>
      public void WriteMultipleRegister(ushort id, byte unit, ushort startAddress, byte[] values)
      {
            ushort numBytes = Convert.ToUInt16(values.Length);
            if (numBytes > 250)
            {
                CallException(id, unit, fctWriteMultipleRegister, excIllegalDataVal);
                return;
            }

            if (numBytes % 2 > 0) numBytes++;
            byte[] data;

            data = CreateWriteHeader(id, unit, startAddress, Convert.ToUInt16(numBytes / 2), Convert.ToUInt16(numBytes + 2), fctWriteMultipleRegister);
            Array.Copy(values, 0, data, 13, values.Length);
            WriteAsyncData(data, id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Write multiple registers in slave synchronous.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startAddress">Address to where the data is written.</param>
      /// <param name="values">Contains the register information.</param>
      /// <param name="result">Contains the result of the synchronous write.</param>
      public void WriteMultipleRegister(ushort id, byte unit, ushort startAddress, byte[] values, ref byte[] result)
      {
            ushort numBytes = Convert.ToUInt16(values.Length);
            if (numBytes > 250)
            {
                CallException(id, unit, fctWriteMultipleRegister, excIllegalDataVal);
                return;
            }

            if (numBytes % 2 > 0) numBytes++;
            byte[] data;

            data = CreateWriteHeader(id, unit, startAddress, Convert.ToUInt16(numBytes / 2), Convert.ToUInt16(numBytes + 2), fctWriteMultipleRegister);
            Array.Copy(values, 0, data, 13, values.Length);
            result = WriteSyncData(data, id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Read/Write multiple registers in slave asynchronous. The result is given in the response function.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startReadAddress">Address from where the data read begins.</param>
      /// <param name="numInputs">Length of data.</param>
      /// <param name="startWriteAddress">Address to where the data is written.</param>
      /// <param name="values">Contains the register information.</param>
      public void ReadWriteMultipleRegister(ushort id, byte unit, ushort startReadAddress, ushort numInputs, ushort startWriteAddress, byte[] values)
      {
            ushort numBytes = Convert.ToUInt16(values.Length);
            if (numBytes > 250)
            {
                CallException(id, unit, fctReadWriteMultipleRegister, excIllegalDataVal);
                return;
            }

            if (numBytes % 2 > 0) numBytes++;
            byte[] data;

            data = CreateReadWriteHeader(id, unit, startReadAddress, numInputs, startWriteAddress, Convert.ToUInt16(numBytes / 2));
            Array.Copy(values, 0, data, 17, values.Length);
            WriteAsyncData(data, id);
      }

      // ------------------------------------------------------------------------
      /// <summary>Read/Write multiple registers in slave synchronous. The result is given in the response function.</summary>
      /// <param name="id">Unique id that marks the transaction. In asynchonous mode this id is given to the callback function.</param>
      /// <param name="unit">Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function.</param>
      /// <param name="startReadAddress">Address from where the data read begins.</param>
      /// <param name="numInputs">Length of data.</param>
      /// <param name="startWriteAddress">Address to where the data is written.</param>
      /// <param name="values">Contains the register information.</param>
      /// <param name="result">Contains the result of the synchronous command.</param>
      public void ReadWriteMultipleRegister(ushort id, byte unit, ushort startReadAddress, ushort numInputs, ushort startWriteAddress, byte[] values, ref byte[] result)
      {
            ushort numBytes = Convert.ToUInt16(values.Length);
            if (numBytes > 250)
            {
                CallException(id, unit, fctReadWriteMultipleRegister, excIllegalDataVal);
                return;
            }

            if (numBytes % 2 > 0) numBytes++;
            byte[] data;

            data = CreateReadWriteHeader(id, unit, startReadAddress, numInputs, startWriteAddress, Convert.ToUInt16(numBytes / 2));
            Array.Copy(values, 0, data, 17, values.Length);
            result = WriteSyncData(data, id);
      }

      // ------------------------------------------------------------------------
      // Create modbus header for read action
      private byte[] CreateReadHeader(ushort id, byte unit, ushort startAddress, ushort length, byte function)
      {
            byte[] data = new byte;

            byte[] _id = BitConverter.GetBytes((short)id);
            data = _id;                          // Slave id high byte
            data = _id;                                // Slave id low byte
            data = 6;                                        // Message size
            data = unit;                                        // Slave address
            data = function;                                // Function code
            byte[] _adr = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)startAddress));
            data = _adr;                                // Start address
            data = _adr;                                // Start address
            byte[] _length = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)length));
            data = _length;                        // Number of data to read
            data = _length;                        // Number of data to read
            return data;
      }

      // ------------------------------------------------------------------------
      // Create modbus header for write action
      private byte[] CreateWriteHeader(ushort id, byte unit, ushort startAddress, ushort numData, ushort numBytes, byte function)
      {
            byte[] data = new byte;

            byte[] _id = BitConverter.GetBytes((short)id);
            data = _id;                                // Slave id high byte
            data = _id;                                // Slave id low byte
            byte[] _size = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)(5 + numBytes)));
            data = _size;                                // Complete message size in bytes
            data = _size;                                // Complete message size in bytes
            data = unit;                                        // Slave address
            data = function;                                // Function code
            byte[] _adr = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)startAddress));
            data = _adr;                                // Start address
            data = _adr;                                // Start address
            if (function >= fctWriteMultipleCoils)
            {
                byte[] _cnt = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)numData));
                data = _cnt;                        // Number of bytes
                data = _cnt;                        // Number of bytes
                data = (byte)(numBytes - 2);
            }
            return data;
      }

      // ------------------------------------------------------------------------
      // Create modbus header for read/write action
      private byte[] CreateReadWriteHeader(ushort id, byte unit, ushort startReadAddress, ushort numRead, ushort startWriteAddress, ushort numWrite)
      {
            byte[] data = new byte;

            byte[] _id = BitConverter.GetBytes((short)id);
            data = _id;                                                // Slave id high byte
            data = _id;                                                // Slave id low byte
            byte[] _size = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)(11 + numWrite * 2)));
            data = _size;                                                // Complete message size in bytes
            data = _size;                                                // Complete message size in bytes
            data = unit;                                                        // Slave address
            data = fctReadWriteMultipleRegister;        // Function code
            byte[] _adr_read = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)startReadAddress));
            data = _adr_read;                                        // Start read address
            data = _adr_read;                                        // Start read address
            byte[] _cnt_read = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)numRead));
            data = _cnt_read;                                // Number of bytes to read
            data = _cnt_read;                                // Number of bytes to read
            byte[] _adr_write = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)startWriteAddress));
            data = _adr_write;                                // Start write address
            data = _adr_write;                                // Start write address
            byte[] _cnt_write = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)numWrite));
            data = _cnt_write;                                // Number of bytes to write
            data = _cnt_write;                                // Number of bytes to write
            data = (byte)(numWrite * 2);

            return data;
      }

      // ------------------------------------------------------------------------
      // Write asynchronous data
      private void WriteAsyncData(byte[] write_data, ushort id)
      {
            if ((tcpAsyCl != null) && (tcpAsyCl.Connected))
            {
                try
                {
                  tcpAsyCl.BeginSend(write_data, 0, write_data.Length, SocketFlags.None, new AsyncCallback(OnSend), null);
                  onDataStreamPrinf("Send:", write_data);
                }
                catch (SystemException)
                {
                  CallException(id, write_data, write_data, excExceptionConnectionLost);
                }
            }
            else CallException(id, write_data, write_data, excExceptionConnectionLost);
      }

      // ------------------------------------------------------------------------
      // Write asynchronous data acknowledge
      private void OnSend(System.IAsyncResult result)
      {
            Int32 size = tcpAsyCl.EndSend(result);
            if (result.IsCompleted == false) CallException(0xFFFF, 0xFF, 0xFF, excSendFailt);
            else tcpAsyCl.BeginReceive(tcpAsyClBuffer, 0, tcpAsyClBuffer.Length, SocketFlags.None, new AsyncCallback(OnReceive), tcpAsyCl);
      }

      // ------------------------------------------------------------------------
      // Write asynchronous data response
      private void OnReceive(System.IAsyncResult result)
      {
            tcpAsyCl.EndReceive(result);
            if (result.IsCompleted == false) CallException(0xFF, 0xFF, 0xFF, excExceptionConnectionLost);

            ushort id = SwapUInt16(BitConverter.ToUInt16(tcpAsyClBuffer, 0));
            byte unit = tcpAsyClBuffer;
            byte function = tcpAsyClBuffer;
            byte[] data;

            byte[] dataprinf = new byte + 6];
            Array.Copy(tcpAsyClBuffer, 0, dataprinf, 0, tcpAsyClBuffer + 6);
            onDataStreamPrinf("Recv:", dataprinf);

            // ------------------------------------------------------------
            // Write response data
            if ((function >= fctWriteSingleCoil) && (function != fctReadWriteMultipleRegister))
            {
                data = new byte;
                Array.Copy(tcpAsyClBuffer, 10, data, 0, 2);
            }
            // ------------------------------------------------------------
            // Read response data
            else
            {
                data = new byte];
                Array.Copy(tcpAsyClBuffer, 9, data, 0, tcpAsyClBuffer);
            }
            // ------------------------------------------------------------
            // Response data is slave exception
            if (function > excExceptionOffset)
            {
                function -= excExceptionOffset;
                CallException(id, unit, function, tcpAsyClBuffer);
            }
            // ------------------------------------------------------------
            // Response data is regular data
            else if (OnResponseData != null) OnResponseData(id, unit, function, data);
      }

      // ------------------------------------------------------------------------
      // Write data and and wait for response
      private byte[] WriteSyncData(byte[] write_data, ushort id)
      {
            if (tcpSynCl.Connected)
            {
                try
                {
                  tcpSynCl.Send(write_data, 0, write_data.Length, SocketFlags.None);
                  int result = tcpSynCl.Receive(tcpSynClBuffer, 0, tcpSynClBuffer.Length, SocketFlags.None);

                  byte unit = tcpSynClBuffer;
                  byte function = tcpSynClBuffer;
                  byte[] data;

                  if (result == 0) CallException(id, unit, write_data, excExceptionConnectionLost);

                  // ------------------------------------------------------------
                  // Response data is slave exception
                  if (function > excExceptionOffset)
                  {
                        function -= excExceptionOffset;
                        CallException(id, unit, function, tcpSynClBuffer);
                        return null;
                  }
                  // ------------------------------------------------------------
                  // Write response data
                  else if ((function >= fctWriteSingleCoil) && (function != fctReadWriteMultipleRegister))
                  {
                        data = new byte;
                        Array.Copy(tcpSynClBuffer, 10, data, 0, 2);
                  }
                  // ------------------------------------------------------------
                  // Read response data
                  else
                  {
                        data = new byte];
                        Array.Copy(tcpSynClBuffer, 9, data, 0, tcpSynClBuffer);
                  }
                  return data;
                }
                catch (SystemException)
                {
                  CallException(id, write_data, write_data, excExceptionConnectionLost);
                }
            }
            else CallException(id, write_data, write_data, excExceptionConnectionLost);
            return null;
      }
    }
}

使用
                // Create new modbus master and add event functions
                MBmaster = new Master(TextBox_DeviceIP1.Text, 502, true);
                MBmaster.OnResponseData += new Master.ResponseData(MBmaster_OnResponseData);
                MBmaster.OnException += new Master.ExceptionData(MBmaster_OnException);
                MBmaster.onDataStreamPrinf += new Master.DataStreamPrinf(MBmaster_onDataStreamPrinf);
                //// Show additional fields, enable watchdog
                button_Send1.Enabled = true;
                panelCom.BackColor = Color.Lime;
                labelCom.Text = "Opened";
                this.BeginInvoke(updateTxt, "连接成功!" + "\r\n");
                groupBox1.Enabled = true;

goldli 发表于 2021-12-1 13:14

实际上,只需要如下
      private void SendCommand(byte[] command)
      {
            var localSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                var waitHandle = localSocket.BeginConnect(_server, null, null);
                var isConnected = waitHandle.AsyncWaitHandle.WaitOne(1000, true); //等待 1 秒

                _logger.Info($"康耐德IO 是否已连接 :: {isConnected}");

                localSocket.EndConnect(waitHandle);
                localSocket.Send(command);
            }
            catch (Exception e)
            {
                _logger.Error($"康耐德发送命令出错 : {e.Message}");
            }
            finally
            {
                localSocket.Disconnect(false);
                localSocket.Dispose();
            }
      }

15181692257 发表于 2021-12-1 11:59

其他的读写线圈等方法我这没有用到,可以按照上面几个方法来进行编写,也不是很难

goldli 发表于 2021-12-1 13:12

你这个有什么用嘛 ?

huanye63 发表于 2021-12-1 15:40

LinearGradientBrush.GradientStopsLinearGradientBrush.GradientStopsLinearGradientBrush.GradientStopsLinearGradientBrush.GradientStopsLinearGradientBrush.GradientStopsLinearGradientBrush.GradientStopsLinearGradientBrush.GradientStops

ipc2008 发表于 2021-12-1 15:55

    byte unit = 1;
                ushort StartAddress;
                StartAddress = 0x0664;
                MBmaster.WriteSingleCoils(ID, unit, StartAddress, true);
                ID++;
                this.BeginInvoke(updateTxt, "写入m636=1!");
                Thread.Sleep(1000);

15181692257 发表于 2021-12-3 10:36

goldli 发表于 2021-12-1 13:12
你这个有什么用嘛 ?

我自己的学习日记{:1_929:}

geekchina 发表于 2021-12-12 10:32

写的很详细,谢谢

15181692257 发表于 2021-12-13 10:33

geekchina 发表于 2021-12-12 10:32
写的很详细,谢谢

共同学习
页: [1] 2
查看完整版本: C#实现MODBUS TCP 通信 第二章 (程序内实现)