C#实现ModbusTCP主站通讯(二)


前言

     今天要说的是如何在上一篇文章《C#实现ModbusTCP通讯》案例的基础上添加新功能。

     废话不多说,主要添加了int、float数据的字节序读取功能。

     什么是字节序?就是字节排序,使用字面ABCD表示。

     每个字母代表一个字节(A=最高位字节,D=最低位字节)。

     如:大端序( 0x12345678 存储为 12 34 56 78),小端序()0x12345678 存储为 78 56 34 12)。


    排序方式有如下几种:

   ABCD [A, B, C, D] 大端序

   DCBA [D, C, B, A] 小端序

   BADC [B, A, D, C]

   CDAB [C, D, A, B]


  • 基本的ModbusTCP读取线圈和寄存器数据一般使用bool和ushort类型,只占用一个寄存器。
  • 如果要返回int、float类型的数据需要占用 2 个寄存器,其实就是读写两个寄存器,再根据不同的字节序和数据类型转换即可。

运行环境

   操作系统:Windows11

   编程软件:Visual Studio 2022

   Net版本:.Net Framework 4.8.0

一、预览

  (一)界面、运行效果

     初始化字节序是ABCD。

     数据读取可以通过点击读取int、float按钮读取,需设置起始地址和长度读取。

     数据的写入根据起始地址和发送内容写入。

     如输入:12 点击写入按钮 (int或float)实现单个写入。输入[12,34,0,0,0]点击按钮实现写入多个数。

 

二、代码

(一)MainForm代码

     添加按钮了读取写入int、float按钮,用于触发读取写入功能。增加字节序变更功能。

#region 写入按钮事件
 private void btn_WriteInt_Click(object sender, EventArgs e)
 {
     try
     {
         int[] register = ParseArray<int>(rtbx_SendData.Text);
         if (register != null &&register.Length == 1)
         {
             modbusTcp.WriteInt(modbusTcp.DataModel.ReadStartAddress, register[0]);
             MessageUpdate($"[{rtbx_SendData.Text}]", Color.Green, $"# 写入 Int >");
         }
         else
         {
             modbusTcp.WriteIntArray(modbusTcp.DataModel.ReadStartAddress, register);
             StringBuilder appendInt = new StringBuilder();
             for (int i = 0; i < register.Length; i++)
             {
                 appendInt.Append(register[i] + " ");
             }
             MessageUpdate($"{appendInt.ToString()}", Color.Green, $"# 写入 Int >");
         }
     }
     catch (Exception ex)
     {
         MessageUpdate($"{ex.Message}", Color.Red, $"# 写入数据 >");
     }
 }
 private void btn_WriteFloat_Click(object sender, EventArgs e)
 {

     try
     {
         float[] register = ParseArray<float>(rtbx_SendData.Text);
         if (register != null && register.Length == 1)
         {
             modbusTcp.WriteFloat(modbusTcp.DataModel.ReadStartAddress, register[0]);
             MessageUpdate($"[{rtbx_SendData.Text}]", Color.Green, $"# 写入 Float >");
         }
         else
         {
             modbusTcp.WriteFloatArray(modbusTcp.DataModel.ReadStartAddress, register);
             StringBuilder appendInt = new StringBuilder();
             for (int i = 0; i < register.Length; i++)
             {
                 appendInt.Append(register[i] + " ");
             }
             MessageUpdate($"{appendInt.ToString()}", Color.Green, $"# 写入 Float >");
         }
     }
     catch (Exception ex)
     {
         MessageUpdate($"{ex.Message}", Color.Red, $"# 写入数据 >");
     }
 }
 #endregion

 #region 读取按钮事件
 private void btn_ReadInt_Click(object sender, EventArgs e)
 {
     int[] intArray = modbusTcp.ReadInt(modbusTcp.DataModel.ReadStartAddress, modbusTcp.DataModel.ReadDataLength);
     if (intArray == null) return;
     string tempArray = string.Empty;
     for (int i = 0; i < intArray.Length; i++)
     {
         tempArray += intArray[i]+" ";
     }
     MessageUpdate($"[{tempArray}]", Color.Green, $"# 读取 Int >");
 }

 private void btn_ReadFloat_Click(object sender, EventArgs e)
 {
     float[] intArray = modbusTcp.ReadFloat(modbusTcp.DataModel.ReadStartAddress, modbusTcp.DataModel.ReadDataLength);
     if (intArray == null) return;
     string tempArray = string.Empty;
     for (int i = 0; i < intArray.Length; i++)
     {
         tempArray += intArray[i] + " ";
     }
     MessageUpdate($"[{tempArray}]", Color.Green, $"# 读取 float >");
 }
 #endregion

 

(二)读写功能

     创建读写单个、多个int、float数据的方法,以及一些数据转换方法。

#region 新增读写功能
#region 写入
/// <summary>
/// 写整数
/// </summary>
public byte[] WriteInt(ushort startAddress, int data, byte unitId = 1)
{
    if (_socket == null) return null;
    _socket.ReceiveTimeout = ReceiveTimeout;
    try
    {
        // 拆分int
        byte[] byteArray = ConvertIntToBytes(data);
        // 发送请求
        byte[] request = BuildMutiWriteRequest(0x10, startAddress, byteArray, 2, unitId);
        _socket.Send(request);
        // 请求报文
        OnRequestMessage(new ModbusMessageEvents(request, true));
        byte[] response = ResponseParse(1, 0x10);
        // 响应报文
        OnResponseMessage(new ModbusMessageEvents(response, true));
        return response;
    }
    catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut)
    {
        throw new TimeoutException($"读取操作超时({ReceiveTimeout}ms)", ex);
    }
}
/// <summary>
/// 写浮点
/// </summary>
public byte[] WriteFloat(ushort startAddress, float data, byte unitId = 1)
{
    if (_socket == null) return null;
    _socket.ReceiveTimeout = ReceiveTimeout;
    try
    {
        // 拆分float
        byte[] byteArray = ConvertFloatToBytes(data);
        // 发送请求
        byte[] request = BuildMutiWriteRequest(0x10, startAddress, byteArray, 2, unitId);
        _socket.Send(request);
        // 请求报文
        OnRequestMessage(new ModbusMessageEvents(request, true));
        byte[] response = ResponseParse(1, 0x10);
        // 响应报文
        OnResponseMessage(new ModbusMessageEvents(response, true));
        return response;
    }
    catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut)
    {
        throw new TimeoutException($"读取操作超时({ReceiveTimeout}ms)", ex);
    }
}
#endregion
#region 读取
/// <summary>
/// 读取保持寄存器(功能码0x03 )
/// </summary>
public int[] ReadInt(ushort startAddress, int numberOfPoints, byte unitId = 1)
{
    if (_socket == null) return null;
    _socket.ReceiveTimeout = ReceiveTimeout;
    try
    {
        //读取整数*2个数寄存器
        ushort number = (ushort)(2 * numberOfPoints);
        //发送请求
        byte[] request = BuildReadRequest(0x03, startAddress, number, unitId);
        _socket.Send(request);
        //请求报文
        OnRequestMessage(new ModbusMessageEvents(request));
        //响应接收
        byte[] response = ResponseParse(number, 0x03);
        //响应报文
        OnResponseMessage(new ModbusMessageEvents(response));
        //验证响应
        ValidateResponse(0x03, response, request);
        //获取读取数据
        int length = 4 * numberOfPoints;
        byte[] byteData = new byte[length];
        byte[] data = new byte[4];
        Array.Copy(response,9,byteData, 0, length);
        int[] intArray = new int[numberOfPoints];
        for (int i = 0; i < numberOfPoints; i++)
        {
            Array.Copy(byteData,i*4, data,0,4);
            intArray[i] = BitConverter.ToInt32(FormatOrder(data), 0);
        }
        return intArray;
    }
    catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut)
    {
        throw new TimeoutException($"读取操作超时({ReceiveTimeout}ms)", ex);
    }
}
/// <summary>
/// 读取保持寄存器(功能码0x03 )
/// </summary>
public float[] ReadFloat(ushort startAddress, int numberOfPoints, byte unitId = 1)
{
    if (_socket == null) return null;
    _socket.ReceiveTimeout = ReceiveTimeout;
    try
    {
        //读取整数*2个数寄存器
        ushort number = (ushort)(2 * numberOfPoints);
        //1、发送请求
        byte[] request = BuildReadRequest(0x03, startAddress, number, unitId);
        _socket.Send(request);
        //2、请求报文
        OnRequestMessage(new ModbusMessageEvents(request));
        //3、响应接收
        byte[] response = ResponseParse(number, 0x03);
        //4、响应报文
        OnResponseMessage(new ModbusMessageEvents(response));
        //5、验证响应
        ValidateResponse(0x03, response, request);
        //6、计算数据长度、获取读取数据
        int length = 4 * numberOfPoints;
        byte[] byteData = new byte[length];
        Array.Copy(response, 9, byteData, 0, length);
        //7、遍历转换结果
        byte[] data = new byte[4];
        float[] intArray = new float[numberOfPoints];
        for (int i = 0; i < numberOfPoints; i++)
        {
            Array.Copy(byteData, i * 4, data, 0, 4);
            intArray[i] = BitConverter.ToSingle(FormatOrder(data), 0);
        }
        //8、返回结果
        return intArray;
    }
    catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut)
    {
        throw new TimeoutException($"读取操作超时({ReceiveTimeout}ms)", ex);
    }
}
#endregion

 


结语

  • 本文例展示了使用Scoket实现ModbusTCP主站通讯的一些说明及部分代码,实现通讯中常用的两种数据类型int、floa。
  • 完整案例文末连接下载。
  • 如果感兴趣的话可下载案例体验、测试程序bug,性能优化,也可以自行修改封装实现更多功能,案例代码仅供参考…

在这里插入图片描述

  • 项目地址:gitee.com/incodenotes/csharp-modbus
  • 如果你觉得这篇文章对你有帮助,不妨点个赞再走呗!
  • 如有其他疑问,欢迎评论区留言讨论!
  • 也可以加入微信公众号 【编程笔记in】 ,一起交流学习!
❖ 感 谢 您 的 关 注 ❖
posted @ 2025-05-08 14:43  编程笔记in  阅读(94)  评论(0)    收藏  举报