当前位置: 首页 > article >正文

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

一、前言

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

1.1 环境

系统:Win11
工具:Visual Studio 2022
.Net 版本:.Net Framework4.6.0
依赖库:NModbus 3.0.81

1.2 协议类型

Modbus RTU:一种二进制协议,采用紧凑的数据帧格式,通信效率较高。通常用于串行通信链路,如RS - 485或RS - 232 ,在工业自动化领域应用广泛。

Modbus ASCII:采用ASCII码进行数据传输,数据帧可读性强,但传输效率相对较低,同样基于串行通信。

​Modbus TCP/IP:基于以太网和TCP/IP协议栈,将Modbus协议封装在TCP/IP协议中,适用于通过网络进行远程通信的场合,是目前工业以太网中常用的通信协议之一。

1.3 通信模式

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

1.4 程序功能

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
}

http://www.kler.cn/a/592893.html

相关文章:

  • LeetCode--2181. 链表--合并零之间的节点
  • 【AI测试必学】DeepSeek API 快速入门:获取 API Key 与调用 API 步骤详解
  • 麒麟服务器操作系统Go环境部署手册
  • 织梦DedeCMS后台发布文章增加“顶”和“踩”默认随机值,并可后台修改
  • Spring Boot 整合 Elasticsearch 实践:从入门到上手
  • 有效防止SSL证书被劫持的策略
  • 路灯安全用电解决方案
  • 力士乐XM42系统Profinet与安川机器人Ethernet/IP通讯转换配置
  • 从零实现B站视频下载器:Python自动化实战教程
  • [力扣]1631. 最小体力消耗路径(bool类型dfs+二分答案/记忆化剪枝/并查集Kruskal思想)
  • 如何用Python和Selenium实现表单的自动填充与提交?
  • [代码规范]1_良好的命名规范能减轻工作负担
  • RabbitMQ相关的面试题
  • STM32 —— MCU、MPU、ARM、FPGA、DSP
  • O(1) 时间插入、删除和获取随机元素——面试经典150题(力扣)
  • OpenHarmony 框架层电池相关重要类解析
  • 【论文阅读】MMedPO: 用临床感知多模态偏好优化调整医学视觉语言模型
  • Vitis 2024.1 无法正常编译custom ip的bug(因为Makefile里的wildcard)
  • 【计算机网络】TCP粘包、拆包的机制
  • ChatGPT与深度学习:探索人工智能的核心技术