【C#】Winform调用NModbus实现Modbus TCP 主站通讯

  • 一、前言

    • Modbus是一种串行通信协议,是工业领域全球最流行的协议之一。

  • 运行环境

    • 操作系统:Windows11
    • 编程软件:Visual Studio 2022
    • .Net 版本:.Net Framework4.6.0
    • 项目依赖:NModbus 3.0.81

  • 协议类型

    • Modbus RTU:一种二进制协议,采用紧凑的数据帧格式,通信效率较高。
    • 通常用于串行通信链路,如RS - 485或RS - 232 ,在工业自动化领域应用广泛。
    • Modbus ASCII:采用ASCII码进行数据传输,数据帧可读性强,但传输效率相对较低,同样基于串行通信。
    • Modbus TCP/IP:基于以太网和TCP/IP协议栈,将Modbus协议封装在TCP/IP协议中,适用于通过网络进行远程通信的场合,是目前工业以太网中常用的通信协议之一。
  • 通信模式

    • ​主从模式:在Modbus网络中,有一个主设备(通常是控制器或上位机)和多个从设备(如传感器、执行器等)。
    • 主设备发起通信请求,从设备根据请求进行响应,从设备不能主动向主设备发送数据。
  • 程序功能

    • 1、连接从站服务。
    • 2、写入数值到指定寄存器。
    • 3、定时读取寄存器值。
    • 4、定时心跳检测通讯状态。

  • 预览

    • 运行效果

在这里插入图片描述


  • 三、代码

    public partial class ModbusTCP : Form
    {
        #region 字段
        // Modbus服务器的IP地址和端口
        private string ipAddress = "127.0.0.1";
        // 端口号
        private int port = 502;
        // 从站地址
        private byte slaveId = 1;
        // 读取保持寄存器的起始地址和数量
        ushort startAddress = 0;
        ushort numRegisters = 10;
        // 写入寄存器的地址和值
        ushort writeAddress = 0;
        ushort writeValue = 0;
        // 连接状态
        private bool isConnected = false;
        // 创建TcpClient
        private TcpClient tcpClient = null;
        // 创建modbus
        private ModbusFactory factory = null;
        // Modbus主站
        private IModbusMaster master = null;
        // 任务定时器
        Timer taskTimer = null;
        // 心跳定时器
        private Timer heartbeatTimer = null;
    
        #endregion
    
        #region 初始化加载
        public ModbusTCP()
        {
            InitializeComponent();
            CenterToParent();
            CenterToScreen();
        }
        private void MainForm_Load(object sender, EventArgs e)
        {
            Initialize();
        }
        private void ModbusTCP_FormClosing(object sender, FormClosingEventArgs e)
        {
            isConnected = false;
            taskTimer?.Stop();
            tcpClient?.Close();
            heartbeatTimer?.Stop();
        }
        #endregion
    
        /// <summary>
        /// 初始化
        /// </summary>
        public void Initialize()
        {
            InitializeControlsState();
            UpdataControlsState();
            dataGridView.Columns[0].Width = 100;
            dataGridView.Columns[1].Width = 100;
            dataGridView.Columns[0].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
            dataGridView.Columns[1].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
            dataGridView.RowHeadersVisible = false;
    
            //数据表格
            dataGridView.Rows.Add(new object[] { 0, 0 });
            dataGridView.Rows.Add(new object[] { 1, 0 });
            dataGridView.Rows.Add(new object[] { 2, 0 });
            dataGridView.Rows.Add(new object[] { 3, 0 });
            dataGridView.Rows.Add(new object[] { 4, 0 });
            dataGridView.Rows.Add(new object[] { 5, 0 });
            dataGridView.Rows.Add(new object[] { 6, 0 });
            dataGridView.Rows.Add(new object[] { 7, 0 });
            dataGridView.Rows.Add(new object[] { 8, 0 });
            dataGridView.Rows.Add(new object[] { 9, 0 });
    
            //定时读取值
            taskTimer = new Timer();
            taskTimer.Interval = 100;
            taskTimer.Tick += Timer_Tick;
    
            // 心跳任务
            heartbeatTimer = new Timer();
            heartbeatTimer.Interval = 1000;
            heartbeatTimer.Tick += HeartbeatTimer_Tick;
        }
    
        private void HeartbeatTimer_Tick(object sender, EventArgs e)
        {
            try
            {
                // 发送心跳请求(这里假设发送一个简单的读取请求作为心跳)
                ushort[] dummyArray = master.ReadHoldingRegisters(slaveId, 0, 1);
                // 检查心跳响应是否有效(可以根据返回值来判断)
                if (dummyArray == null || dummyArray.Length != 1 || dummyArray[0] != 0)
                {
                    UpdataMessage("心跳失败,断开连接...");
                    isConnected = false;
                    taskTimer.Stop();
                    tcpClient.Close();
                    UpdataControlsState();
                }
            }
            catch (Exception ex)
            {
                UpdataMessage("心跳失败...");
                isConnected = false;
                taskTimer.Stop();
                tcpClient.Close();
                UpdataControlsState();
            }
        }
    
        /// <summary>
        /// 初始化控件状态
        /// </summary>
        public void InitializeControlsState()
        {
            tbx_SlaveID.Text = slaveId.ToString();
            tbx_IPAddress.Text = ipAddress;
            tbx_TargetPort.Text = port.ToString();
            tbx_StartAddress.Text = startAddress.ToString();
            tbx_ReadLength.Enabled = false;
            tbx_ReadLength.Text = numRegisters.ToString();
            tbx_WriteAddress.Text = writeAddress.ToString();
            tbx_WriteValue.Text = writeValue.ToString();
        }
    
        private void UpdataControlsState()
        {
            if (isConnected)
            {
                btn_Connect.Text = "断开";
                btn_WriteData.Enabled = true;
                tbx_IPAddress.Enabled = false;
                tbx_TargetPort.Enabled = false;
                tbx_SlaveID.Enabled = false;
                tbx_ReadLength.Enabled=false;
            }
            else
            {
                btn_Connect.Text = "连接";
                btn_WriteData.Enabled = false;
                tbx_IPAddress.Enabled = true;
                tbx_TargetPort.Enabled = true;
                tbx_SlaveID.Enabled = true;
                tbx_ReadLength.Enabled = false;
            }
        }
    
        /// <summary>
        /// 定时器方法
        /// </summary>
        private void Timer_Tick(object sender, EventArgs e)
        {
            try
            {
                if (isConnected)
                {
                    // 读取保持寄存器
                    ushort[] array = master.ReadHoldingRegisters(slaveId, startAddress, numRegisters);
                    // 输出读取到的寄存器值
                    for (int i = 0; i < array.Length; i++)
                    {
                        dataGridView.Rows[i].Cells[0].Value = (startAddress + i);
                        dataGridView.Rows[i].Cells[1].Value = array[i];
                    }
                }
            }
            catch (Exception ex)
            {
                UpdataMessage("");
            }
        }
        /// <summary>
        ///  连接
        /// </summary>
        private void btn_Connect_Click(object sender, EventArgs e)
        {
            try
            {
                if (!isConnected)
                {
                    tcpClient = new TcpClient(ipAddress, port);
                    factory = new ModbusFactory();
                    master = factory.CreateMaster(tcpClient);
                    taskTimer.Start();
                    heartbeatTimer?.Start();
                    isConnected = true;
                    UpdataControlsState();
                    UpdataMessage("连接成功...");
                }
                else
                {
                    isConnected = false;
                    master = null;
                    taskTimer.Stop();
                    tcpClient.Close();
                    UpdataControlsState();
                    UpdataMessage("断开连接...");
                    heartbeatTimer?.Stop();
                }
            }
            catch (Exception ex)
            {
                isConnected = false;
                taskTimer?.Stop();
                heartbeatTimer?.Stop();
                tcpClient?.Close();
                UpdataControlsState();
                UpdataMessage("连接失败...");
                UpdataMessage($"{ex.Message}");
            }
        }
        
        /// <summary>
        ///  写入数据
        /// </summary>
        private void btn_WriteData_Click(object sender, EventArgs e)
        {
            master.WriteSingleRegister(slaveId, writeAddress, writeValue);
            UpdataMessage($"从站ID:{slaveId},写入数据:地址:{writeAddress} ,值:{writeValue}");
        }
    
        /// <summary>
        /// 更新操作消息
        /// </summary>
        private void UpdataMessage(string message)
        {
            tbx_Output.BeginInvoke(new Action(() =>
            {
                tbx_Output.AppendText($"{DateTime.Now.ToString()}{message}\r\n");
            }));
        }
    
        #region 文本变更
        /// <summary>
        /// 起始地址
        /// </summary>
        private void tbx_StartAddress_TextChanged(object sender, EventArgs e)
        {
            if (ushort.TryParse(tbx_StartAddress.Text, out ushort address))
            {
                startAddress = address;
            }
        }
        /// <summary>
        /// 读取长度
        /// </summary>
        private void tbx_ReadLength_TextChanged(object sender, EventArgs e)
        {
            if (ushort.TryParse(tbx_ReadLength.Text, out ushort length))
            {
                numRegisters = length;
            }
        }
        /// <summary>
        /// 写入地址
        /// </summary>
        private void tbx_WriteAddress_TextChanged(object sender, EventArgs e)
        {
            if (ushort.TryParse(tbx_WriteAddress.Text, out ushort address))
            {
                writeAddress = address;
            }
        }
        /// <summary>
        /// 写入值
        /// </summary>
        private void tbx_WriteValue_TextChanged(object sender, EventArgs e)
        {
            if (ushort.TryParse(tbx_WriteValue.Text, out ushort address))
            {
                writeValue = address;
            }
        }
        /// <summary>
        /// 从站ID
        /// </summary>
        private void tbx_SlaveID_TextChanged(object sender, EventArgs e)
        {
            if (byte.TryParse(tbx_SlaveID.Text, out byte address))
            {
                slaveId = address;
            }
        }
        #endregion
    }
    

  • 结语

    • 既是分享,也是备份。

  • 最后

    • 如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!
    • 如果有疑问或需要进一步的帮助,欢迎评论区留言。
    • 也可以关注微信公众号 [编程笔记in] 社区,共同学习交流!

### 如何在C# Winform 应用程序中使用Modbus TCP协议将数据发布到Web服务器 为了实现C# Winform应用程序中通过Modbus TCP协议获取数据并将其发送至Web端的功能,开发者需构建两个主要模块:一是负责与支持Modbus TCP的设备进行交互的数据采集模块;二是用于向Web服务提交所收集信息的服务调用模块。 #### 构建Modbus TCP 客户端以连接PLC或其他兼容硬件 建立稳定的Modbus TCP会话对于成功读取远程设备上的寄存器至关重要。这通常涉及到初始化一个新的`TcpClient`实例来创建网络连接,并利用专门设计用来处理Modbus请求/响应消息序列化的库(比如NModbus4)。下面是一个简单的例子展示怎样设置这样的链接: ```csharp using System; using NModbus; // 创建TCP客户端对象 var client = new TcpClient("your_plc_ip_address", 502); // 默认端口为502 INetwork network = new Network.TcpIp.NetworkDevice(client); IModbusFactory factory = new ModbusFactory(); IAdapter adapter = (factory.CreateMaster(network)); // 执行具体操作,例如读取保持寄存器 ushort[] registers = master.ReadHoldingRegisters(slaveId, startAddress, numberOfPoints); client.Close(); // 关闭连接 ``` 此段代码展示了如何建立一个基本的Modbus TCP客户端[^1],它能够执行诸如读写寄存器之类的简单任务。然而,在实际应用场景下可能还需要考虑错误处理机制以及更复杂的消息结构。 #### 将获得的信息传递给Web API 或者 RESTful 接口 一旦获得了来自工业控制器的有效负载之后,则可以通过HTTP POST方法把它们转发出去。这里假设目标API接受JSON格式作为输入参数之一。因此,先要安装Newtonsoft.Json NuGet包以便轻松转换.NET对象成为字符串表示形式。接着按照如下模式编写函数: ```csharp using Newtonsoft.Json.Linq; using RestSharp; public void PostDataToWebService(string url, JObject data) { var restClient = new RestClient(url); var request = new RestRequest(Method.POST); request.AddHeader("Content-Type","application/json"); request.AddParameter("undefined",data.ToString(),ParameterType.RequestBody); IRestResponse response = restClient.Execute(request); } ``` 上述片段说明了当准备就绪时应采取哪些措施才能有效地推送更新后的测量值至上层信息系统。请注意替换掉示例中的URL地址和JObject内容以匹配特定环境下的需求[^2]。 #### 综合运用两者完成整个流程 最后一步就是整合以上提到的技术要点形成完整的解决方案框架。这意味着每当接收到新的传感器数值或者状态变化报告的时候都会触发一次对外部资源的通知过程。考虑到实时性和可靠性因素的影响,建议定期轮询远端装置的状态而不是等待事件驱动型通知到来再做反应。 综上所述,通过合理规划架构设计思路加上适当选用第三方工具集的支持,完全可以实现从本地桌面界面无缝对接云端服务平台的效果。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程笔记in

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值