【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;// 创建TcpClientprivate TcpClient tcpClient = null;// 创建modbusprivate 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.mzph.cn/diannao/74377.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【leetcode题解】贪心算法

目录 贪心算法 柠檬水找零 将数组和减半的最少操作次数 最大数 摆动序列 最长递增子序列 递增的三元子序列 最长连续递增序列 买卖股票的最佳时机 买卖股票的最佳时机 II K 次取反后最大化的数组和 按身高排序 优势洗牌 最长回文串 增减字符串匹配 分发饼干 最…

Langchain4J框架相关面试题

以下是关于Langchain4J框架的面试题目及答案 ### Langchain4J基础概念类 1. **Langchain4J框架是什么&#xff1f;它的核心功能有哪些&#xff1f;** Langchain4J是一个用于构建语言模型应用的Java框架&#xff0c;它为开发者提供了一套简洁高效的API&#xff0c;使得在Jav…

Apache Doris

Apache Doris介绍 Apache Doris 是一个基于 MPP 架构的高性能、实时的分析型数据库&#xff0c;以极速易用的特点被人们所熟知&#xff0c;仅需亚秒级响应时间即可返回海量数据下的查询结果&#xff0c;不仅可以支持高并发的点查询场景&#xff0c;也能支持高吞吐的复杂分析场…

VLAN间通信

目录 第一步&#xff1a;配vlan 第二步&#xff1a;配置核心vlanif,MAC地址信息。 第三步&#xff1a;ospf协议 三层交换机&#xff08;汇聚层&#xff09;: 对于交换机、路由器、防火墙等网络设备而言&#xff0c;接口类型一般存在两种&#xff1a;二层接口&#xff0c;三…

LeetCode热题100精讲——Top2:字母异位词分组【哈希】

你好&#xff0c;我是安然无虞。 文章目录 题目背景字母异位词分组C解法Python解法 题目背景 如果大家对于 哈希 类型的概念并不熟悉, 可以先看我之前为此专门写的算法详解: 蓝桥杯算法竞赛系列第九章巧解哈希题&#xff0c;用这3种数据类型足矣 字母异位词分组 题目链接&am…

基于python+django的图书借阅网站-图书借阅管理系统源码+运行步骤

该系统是基于pythondjango开发的在线图书借阅管理系统。系统适合场景&#xff1a;大学生、课程作业、系统设计、毕业设计。 演示地址 前台地址&#xff1a; http://book.gitapp.cn 后台地址&#xff1a;http://book.gitapp.cn/#/admin 后台管理帐号&#xff1a; 用户名&…

uni-app集成保利威直播、点播SDK经验FQ(二)|小程序直播/APP直播开发适用

通过uniapp集成保利威直播、点播SDK来开发小程序/APP的视频直播能力&#xff0c;在实际开发中可能会遇到的疑问和解决方案&#xff0c;下篇。更多疑问请咨询19924784795。 1.ios不能后台挂起uniapp插件 ios端使用后台音频播放和画中画功能&#xff0c;没有在 manifest.json 进…

数据库三级填空+应用题(1)

填空 35【答案】TOP 3 WITH TIES 【解析】希望选出商品数量最多的前3类商品&#xff0c;并获得相应的商品类别和数量。with ties一般是和Top 、 order by相结合使用,表示包括与最后一行order by后面的参数取值并列的结果。 36在SQL Server 2008中&#xff0c;每个数据页可存储8…

前端(vue)学习笔记(CLASS 5):自定义指令插槽路由

1、自定义指令 内置指令&#xff1a;内部提供的&#xff0c;每个指令都有自己各自独立的功能 自定义指令&#xff1a;自己定义的指令&#xff0c;可以封装一些dom操作&#xff0c;扩展额外功能 全局注册-语法 例如&#xff0c;当页面加载时&#xff0c;让元素获得焦点 Vue.…

【redis】事务详解,相关命令multi、exec、discard 与 watch 的原理

文章目录 什么是事务原子性一致性持久性隔离性 优势与 MySQL 对比用处 事务相关命令开启事务——MULTI执行事务——EXEC放弃当前事务——DISCARD监控某个 key——WATCH作用场景使用方法实现原理 事务总结 什么是事务 MySQL 事务&#xff1a; 原子性&#xff1a;把多个操作&am…

【Java SE】单例设计模式

参考笔记&#xff1a;深入理解Java设计模式&#xff1a;单例模式及其饿汉式与懒汉式的对比,-CSDN博客 目录 1.什么是设计模式 2.经典设计模式 3.单例设计模式&#xff08;static属性/方法经典使用场景 &#xff09; 3.1 饿汉式单例模式 3.2 懒汉式单例模式 4.补充 1.什么…

【day2】数据结构刷题 栈

一 有效的括号 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的…

蓝桥杯 劲舞团

问题描述 小蓝最近迷上了一款名为 “劲舞团” 的游戏。 在游戏中&#xff0c;只要按照给出的键位提示依次按出对应的键位&#xff0c;游戏人物便可以跟随节奏跳舞。 对于连续的 K 次正确敲击&#xff0c;如果任意连续两次敲击之间的时间间隔都小于等于 1 秒&#xff08;即 1…

数据库数值函数详解

各类资料学习下载合集 ​​https://pan.quark.cn/s/8c91ccb5a474​​ 数值函数是数据库中用于处理数值数据的函数,可以用于执行各种数学运算、统计计算等。数值函数在数据分析及处理时非常重要,能够帮助我们进行数据的聚合、计算和转换。在本篇博客中,我们将详细介绍常用的…

关于金融开发领域的一些专业知识总结

目录 1. 交易生命周期 1.1 证券交易所 1.1.1 交易前 1) 订单生成&#xff08;Order Generation&#xff09; 2) 订单管理&#xff08;Order Management&#xff09; 1.1.2 交易执行 3) 交易匹配&#xff08;Trade Matching&#xff09; 1.1.3 交易后 4) 交易确认&…

Leetcode 3495. Minimum Operations to Make Array Elements Zero

Leetcode 3495. Minimum Operations to Make Array Elements Zero 1. 解题思路2. 代码实现 题目链接&#xff1a;3495. Minimum Operations to Make Array Elements Zero 1. 解题思路 这一题的话核心就是统计对任意自然数 n n n&#xff0c;从 1 1 1到 n n n当中所有的数字对…

Vue 3 + TypeScript 实现视频播放与字幕功能:集成西瓜播放器 XGPlayer

文章目录 1. 前言&#xff1a;视频播放器的重要性2. 准备工作2.1 安装 Vue 3 项目2.2 安装 XGPlayer 和相关依赖 3. 实现视频播放3.1 初始化 XGPlayer 4. 添加字幕功能4.1 配置字幕 4.2 字幕文件格式5. 增加交互性完整的代码&#xff0c;仅供参考6. 总结 在现代 Web 开发中&…

MacOS安装 nextcloud 的 Virtual File System

需求 在Mac上安装next cloud实现类似 OneDrive 那样&#xff0c;文件直接保存在服务器&#xff0c;需要再下载到本地。 方法 在 官网下载Download for desktop&#xff0c;注意要下对版本&#xff0c;千万别下 Mac OS默认的那个。 安装了登录在配置过程中千万不要设置任何同…

.NET 9 彻底改变了 API 文档:从 Swashbuckle(Swagger) 到 Scalar

示例代码下载&#xff1a;https://download.csdn.net/download/hefeng_aspnet/90404652 摘要 API 文档是现代软件开发的支柱。随着 .NET 9 从 Swashbuckle 转向 Microsoft.AspNetCore.OpenApi&#xff0c;开发人员需要新的策略来保持高效。本文探讨了这些变化&#xff0c;并介…

深入剖析Java虚拟机(JVM):从零开始掌握Java核心引擎

&#x1f4cc; 引言&#xff1a;为什么每个Java开发者都要懂JVM&#xff1f; 想象你是一名赛车手&#xff0c;Java是你的赛车&#xff0c;而JVM就是赛车的引擎。 虽然你可以不关心引擎内部构造就能开车&#xff0c;但要想在比赛中获胜&#xff0c;必须了解引擎如何工作&#…