工控机 modbus RTU和TCP 通信类

发布于:2024-04-17 ⋅ 阅读:(23) ⋅ 点赞:(0)
 public class ModBusTCP
 {
     public  int ushorts2int(ushort[] res)
     {
         int high = res[0];
         int low = res[1];
         int value = (high << 16) + low;
         return value;
     }

     public  ushort[] int2ushorts(int res)
     {
         ushort ust1 = (ushort)(res >> 16);
         ushort ust2 = (ushort)res;          
         return new ushort[] { ust1, ust2 };
     }
     //声明一个Socket对象
     private Socket tcpClient;
     //单元标识符
     public byte SlaveId { get; set; } = 0x01;
     /// <summary>
     /// 建立连接
     /// </summary>
     /// <param name="ip">IP地址  </param>
     /// <param name="port">端口号</param>
     /// <returns></returns>
     public bool Connect(string ip, int port)
     { 
         //实例化Socket对象
         tcpClient =new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
         try
         {
             //建立连接   三次握手
             tcpClient.Connect(IPAddress.Parse(ip), port);
             return true;
         }
         catch (Exception)
         {

            return false;
         }
       
     }
     /// <summary>
     /// 断开连接
     /// </summary>
     public void DisConnect() 
     { 
         //  4次握手
         tcpClient?.Close();
     }
     /// <summary>
     /// 读取多个数据
     /// </summary>
     /// <param name="startAdr"></param>
     /// <param name="length"></param>
     /// <returns></returns>
     public byte[] ReadOutputRegisters(ushort startAdr, ushort length) 
     {
         #region 1 拼接报文
         
         List<byte> SendCommand = new List<byte>();
         //ModBusTCP 报文 格式     MBAP  + 功能码+  数据部分
         // MBAP  事务标识符(2字节) 协议标识符(2字节) 长度(2字节) 单元标识符(1字节)
         // 功能码 (1字节)
         // 数据部分  起始寄存器地址(2字节)   寄存器长度(2字节) 
         //=======================报文拼接===============================
         //事务标识符  设置固定  0x00 0x00
         // 等价 SendCommand.Add(0x00); SendCommand.Add(0x00);
         SendCommand.AddRange(new byte[] { 0x00, 0x00 });

         // 协议标识符  设置固定  0x00 0x00
         SendCommand.AddRange(new byte[] { 0x00, 0x00 });

         // 长度  
         SendCommand.AddRange(new byte[] {0x00,0x06});

         // 单元标识符 1字节
         SendCommand.Add(SlaveId);

         //功能码  固定 0x03
         SendCommand.Add(0x03);
         //数据部分 起始寄存器地址(2字节)
         #region ushort 转换字节数组
         //  %  /
         //byte Hbyte = (byte)(startAdr / 255);
         //byte Lbyte = (byte)(startAdr % 255);
         //SendCommand.AddRange(new byte[] { Hbyte,Lbyte});
         #endregion
         //BitConverter 大小端是颠倒
         byte[] temp = BitConverter.GetBytes(startAdr);
         SendCommand.AddRange(new byte[] { temp[1], temp[0] });
         //数据部分 寄存器长度(2字节)
         temp = BitConverter.GetBytes(length);
         SendCommand.AddRange(new byte[] { temp[1], temp[0] });
         #endregion
         #region 2 发送报文
         tcpClient.Send(SendCommand.ToArray());
         #endregion
         #region 3 接收报文
         Thread.Sleep(50);
         int count = tcpClient.Available;
         byte[] buffer= new byte[count];
         tcpClient.Receive(buffer,buffer.Length,SocketFlags.None);
         #endregion
         #region 4 验证报文
         //Tx  发送报文: 00 C9 00 00 00 06 01 03 00 00 00 02
         //Rx  接收报文: 00 C9 00 00 00 07 01 03 04 01 4D 00 01
         // 校验 00 07 01 03 04  这几位
         // 00 C9 00 00 00 07 01 03 04   这9位是长度固定的,  +   获取寄存器长度length*2   每个寄存器占2字节
         if (buffer.Length == 9+length*2) 
         {
             //两位字节转换整数  result_h   高位字节 result_1  地位字节
             //result = result_h * 256 + result_1
             //   00 07     
             //   3+length*2   3来源于01 03 04  固定的  
             if (buffer[4] * 256 + buffer[5]==3+length*2) 
             {
                 //   单元标识符  和   功能码
                 if (buffer[6] == SlaveId && buffer[7] == 0x03)
                 {
                     #region 5 解析报文
                     //  返回的数据字节数,就是 要读取寄存器*2
                     byte[] result = new byte[length*2];
                     Array.Copy(buffer,9,result,0,length*2);
                     return result;
                     #endregion
                 }
             }

         }
         return null;
         #endregion
        
       
     }

     /// <summary>
     /// 写入多数据
     /// </summary>
     /// <param name="startAdr">开始寄存器地址</param>
     /// <param name="length">寄存器长度</param>
     /// <param name="dates">具体数据</param>
     /// <returns></returns>
     public void WriteResisters(ushort startAdr, ushort length,ushort datecount, ushort[] dates)
     {
         #region 1 拼接报文
         List<byte> SendCommand = new List<byte>();
         // 事务标识符 2字节  固定  0x00 0x00
         SendCommand.AddRange(new byte[] { 0x00, 0x00 });
         // 协议标识符 2字节  固定  0x00 0x00
         SendCommand.AddRange(new byte[] { 0x00, 0x00 });
         // 长度  2字节       7:  单元标识符1字节+功能码1字节+ 开始寄存器地址2字节 寄存器长度2字节 + 字节长度1字节
         ushort[] lens=int2ushorts(2*length+7);
         //byte[] start1 = BitConverter.GetBytes(lens[0]);
         byte[] len = BitConverter.GetBytes(lens[1]);
         if (BitConverter.IsLittleEndian)
         {
             Array.Reverse(len);
         }
         SendCommand.AddRange(len);
         // 单元标识符  1字节  固定 SlaveId
         SendCommand.Add(SlaveId);
         // 功能码  写入 多个保持寄存器
         SendCommand.Add(0x10);
         // 数据部分     开始寄存器地址 寄存器长度  字节长度   具体数据
         // 开始寄存器地址
         byte[] start = BitConverter.GetBytes(startAdr);
         //数据值
         List<byte> valueBytes = new List<byte>();
         byte[] value;
         foreach (ushort b in dates)
         {
             value= BitConverter.GetBytes(b);
             if (BitConverter.IsLittleEndian)
             {
                 Array.Reverse(value);
             }
             valueBytes.AddRange(value);
         }            
         //根据计算机大小端存储方式进行高低字节转换
         if (BitConverter.IsLittleEndian)
         {
             Array.Reverse(start);              
         }
         SendCommand.AddRange(start);
         //寄存器长度  2字节
         byte[] registLen = BitConverter.GetBytes(length);         
         if (BitConverter.IsLittleEndian)
         {
             Array.Reverse(registLen);
           
         }
         SendCommand.AddRange(registLen);
         //字节长度 1字节
         SendCommand.Add((byte)(datecount%255));
         
         //SendCommand.AddRange(new byte[] { 0,1});
        
         SendCommand.AddRange(valueBytes);
       
         #endregion
         #region 2 发送报文
         Thread.Sleep(50);
         //tcpClient.Send(SendCommand.ToArray(),0,SendCommand.Count(),SocketFlags.Partial);
         //tcpClient.Send(SendCommand.ToArray());
         //tcpClient.Send(new byte[] { 00, 01, 00, 00, 00, 08, 0xFF, 0x05, 00, 05 , 00 ,01, 01, 01 });
         //Rx: 000002 - 00 01 00 00 00 09 01 0A 00 05 00 01 01 00 0F
         //事务标识符2字节           1F 97
         //协议标识符2字节           00 00
         //长度 2字节                00 11
         //单元标识符                01
         //功能码                    10
         //起始地址 2字节            00 04
         //寄存器长度 2字节          00 02            读2个寄存器
         //字节数    寄存器长度*2    04
         //字节数组N*2字节           00 06 00 09
         //1F 97 00 00 00 11 01 10 00 04 00 02 04 00 06 00 09
         // 00 01 00 00 00 11 01 10 00 03 00 02 04 00 02 01 09
         //tcpClient.Send(new byte[] { 00, 01, 00, 00, 00, 11, 01, 0x10, 00, 00 ,00 ,02 ,04,01,02, 30 ,39 });
         //tcpClient.Send(new byte[] { 00, 01, 00, 00, 00, 11, 01, 0x10, 00, 00 ,00 ,02 ,04,01,02, 30 ,39 });
         tcpClient.Send(SendCommand.ToArray());
         #endregion

     }
 }

在这里插入图片描述

ModBusTCP

在这里插入图片描述

在这里插入图片描述