C#与西门子PLC1500的ModbusTcp服务器通信4--搭建ModbusTcp客户端
1、客户端选择
客户端可以是一个程序或一个设备,这里我以C#WINFORM程序来实现客户机与PLC的Modbustcp服务器通信,开发环境是VS2019,.NET Framework版本是4.7.2
2、创建winform程序
3、引入Nmodbus4协议
找到项目,找到引用,右键“管理nuget程序”,在下面对话框操作
4、界面布局如下:
布局中用到的是下拉框combobox,文本框textbox,按钮button,标签label
这个IP地址和端口号是与这里对应
5、窗体定义两个变量,并引入对应的命令空间
ModbusIpMaster master = null;//modbus对象
TcpClient tcpClient = null;//tcp客户端对象
6、连接按钮代码
private void btnOpen_Click(object sender, EventArgs e){string ip = txtIPAddress.Text.Trim();bool t = IsIP(ip);if (t){try{int port = int.Parse(txtPort.Text.Trim());tcpClient = new TcpClient();tcpClient.Connect(ip, port);//连接到主机master = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站master.Transport.ReadTimeout = 1000;//读超时master.Transport.WriteTimeout = 1000;//写超时master.Transport.Retries = 3;//尝试重复连接次数master.Transport.WaitToRetryMilliseconds = 200;//尝试重复连接间隔lblMessage.Text = "连接成功!";btnOpen.Enabled = false;}catch (Exception ex){MessageBox.Show("连接失败," + ex.Message);}}else{MessageBox.Show("无效的ip地址!");}}
7、读取的代码--ushort类型
本例子中只用到了读取保存寄存器这个功能码,即ReadHoldingRegisters(从站地址,开始地址,寄存器数量)
/// <summary>/// 读取/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void myread_Click(object sender, EventArgs e){//由于NModbus4读取到寄存器的数据都是ushort类型//功能码string readType = cboReadTypes.Text.Trim();//从站地址byte slaveAddr = byte.Parse(txtRSlaveId.Text.Trim());//开始地址ushort startAddr = ushort.Parse(txtRStartAddress.Text.Trim());//读取数量ushort readCount = ushort.Parse(txtRCount.Text.Trim());switch (readType){case "读线圈":bool[] blVals = master.ReadCoils(slaveAddr, startAddr, readCount);txtReadDatas1.Text = string.Join(",", blVals.Select(b => b ? "1" : "0"));break;case "读输入线圈":bool[] blInputVals = master.ReadInputs(slaveAddr, startAddr, readCount);txtReadDatas1.Text = string.Join(",", blInputVals.Select(b => b ? "1" : "0"));break;case "读保持寄存器"://情况1:ushort到ushort类型:即读取无符号的整数,如23,89,处理方法是:原封不动//ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, startAddr, readCount);//txtReadDatas.Text = string.Join(",", uDatas);//功能码string dataType = cmddatatype.Text.Trim();switch (dataType){case "ushort"://利用token循环读取ushortctsRead = new CancellationTokenSource();Task.Run(new Action(() =>{ReadUshortFromPLC(slaveAddr, startAddr, readCount);}), ushortctsRead.Token);break;case "short"://利用token循环读取shortctsRead = new CancellationTokenSource();Task.Run(new Action(() =>{ReadShortFromPLC(slaveAddr, startAddr, readCount);}), shortctsRead.Token);break;case "float"://利用token循环读取floatctsRead = new CancellationTokenSource();Task.Run(new Action(() =>{ReadFloatFromPLC(slaveAddr, startAddr, readCount);}), floatctsRead.Token);break;} break;case "读输入寄存器":ushort[] uDatas1 = master.ReadInputRegisters(slaveAddr, startAddr, readCount);txtReadDatas1.Text = string.Join(",", uDatas1);break;}}
这里要注意,
NModbus4读取到寄存器的数据都是ushort类型
NModbus4读取到寄存器的数据都是ushort类型
代码中用到ReadUshortFromPLC方法,ReadShortFromPLC方法,ReadFloatFromPLC方法在本文最后链接都会提供
运行程序,连接成功,读取数据
注意这里,从站地址一般都是1,除非你改了,开始地址是0,表示寄存器的起始地址,数量是3,表示读取3个寄存器数量,也就是前面3个变量,m1-speed,m1-duaror,m1-level
这里为什么数量不能是4,因为第4个变量是real,它占2个寄存器,即占4个字节,它不是ushort类型,这里地址也不能是%DB3.DBW4这种写法,这不是S7协议读取变量,是MODBUS读取寄存器,两者不一样的,别糊涂了,各位长老。
8、读取的代码--float类型
很多人搞不清楚这个开始地址和数量,这个开始地址是Modbus的地址,Modbus地址编号从0开始,因此8个变量的地址就是0,1,2,3,4,5,6,7,数量是指要读取的寄存器个数,word占一个,real占2个,这里很难理解,比较绕比较晕,一个是PLC地址,一个是MODBUS地址
我们要读的温度是第3个寄存器,它是real类型,占2个寄存器数量
9、写入的代码--ushort类型
/// <summary>/// 写入/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnWrite_Click(object sender, EventArgs e){//功能码string writeType = cboWriteTypes.Text.Trim();//从站地址byte slaveAddr = byte.Parse(txtWSlaveId.Text.Trim());//开始地址ushort startAddr = ushort.Parse(txtWStartAddress.Text.Trim());//数量//实际数量string objWriteVals = "";string dataType = cmddatatype2.Text.Trim();switch (dataType){case "ushort":objWriteVals = txtWriteDatas1.Text.Trim();break;case "short":objWriteVals = txtWriteDatas2.Text.Trim();break;case "float":objWriteVals = txtWriteDatas3.Text.Trim();break;}ushort writeCount = ushort.Parse(txtWCount.Text.Trim()); ushort objWCount = (ushort)objWriteVals.Split(',').Length;//实际数量与要求数量不一致,不允许操作if (writeCount != objWCount){MessageBox.Show("写入值的数量不正确!");return;}string vals = objWriteVals;switch (writeType){case "写单线圈":bool blVal = vals == "1" ? true : false;try{master.WriteSingleCoil(slaveAddr, startAddr, blVal);MessageBox.Show("【单线圈】写入成功!");}catch (Exception ex){MessageBox.Show(ex.Message);}break;case "写单保持寄存器":ushort uVal01 = ushort.Parse(vals);try{master.WriteSingleRegister(slaveAddr, startAddr, uVal01);MessageBox.Show("【单保持寄存器】写入成功!");}catch (Exception ex){MessageBox.Show(ex.Message);}break;case "写多线圈":bool[] blVals = vals.Split(',').Select(s => s == "1" ? true : false).ToArray();//bool数组try{master.WriteMultipleCoils(slaveAddr, startAddr, blVals);MessageBox.Show("【多线圈】写入成功!");}catch (Exception ex){MessageBox.Show(ex.Message);}break;case "写多保持寄存器":try{//功能码//string dataType = cmddatatype2.Text.Trim();switch (dataType){case "ushort":情况1:写入无符号的整数,即写入ushort数据,如写入33,44ushort[] uVals01 = vals.Split(',').Select(s => ushort.Parse(s)).ToArray();master.WriteMultipleRegisters(startAddr, uVals01);break;case "short"://情况2:写入有符号的整数,即写入short数据,如写入-133,-65,98等,处理方法是:short[]=>byte[]=>ushort[],情况2包括了情况1 short[] uVals02 = vals.Split(',').Select(s => short.Parse(s)).ToArray();byte[] y2 = ByteArrayLib.GetByteArrayFromShortArray(uVals02);ushort[] ushorts2 = UShortLib.GetUShortArrayFromByteArray(y2);master.WriteMultipleRegisters(startAddr, ushorts2);MessageBox.Show("【short类型数据】写入成功!");break;case "float"://情况3:写入有符号的小数,即写入float数据,如写入-6.3,-2.65,56.893,51,-465等,处理方法是:float[]=>byte[]=>ushort[],情况3包括了情况2和情况1 float[] uVals03 = vals.Split(',').Select(s => float.Parse(s)).ToArray();byte[] y3 = ByteArrayLib.GetByteArrayFromFloatArray(uVals03);ushort[] ushorts3 = UShortLib.GetUShortArrayFromByteArray(y3);master.WriteMultipleRegisters(startAddr, ushorts3);MessageBox.Show("【float类型数据】写入成功!");break;}情况2:写入有符号的整数,即写入short数据,如写入-133,-65,98等,处理方法是:short[]=>byte[]=>ushort[],情况2包括了情况1 //short[] uVals02 = vals.Split(',').Select(s => short.Parse(s)).ToArray();//byte[] y = ByteArrayLib.GetByteArrayFromShortArray(uVals02);//ushort[] ushorts = UShortLib.GetUShortArrayFromByteArray(y);//master.WriteMultipleRegisters(slaveAddr, startAddr, ushorts);情况3:写入有符号的小数,即写入float数据,如写入-6.3,-2.65,56.893,51,-465等,处理方法是:float[]=>byte[]=>ushort[],情况3包括了情况2和情况1 //float[] uVals02 = vals.Split(',').Select(s => float.Parse(s)).ToArray();//byte[] y = ByteArrayLib.GetByteArrayFromFloatArray(uVals02);//ushort[] ushorts = UShortLib.GetUShortArrayFromByteArray(y);//master.WriteMultipleRegisters(slaveAddr, startAddr, ushorts);MessageBox.Show("【多保持寄存器】写入成功!");}catch (Exception ex){MessageBox.Show(ex.Message);}break;}}
写入成功,同时读取的也是刚才写的值,在博途的监控表中看到
10、写入的代码--float类型
写入负数
11、小结
客户端创建tcp client对象,然后modbus利用tcp对象创建modbus通信,然后通过不同数据类型读写PLC数据,成功了
代码链接:
链接:https://pan.baidu.com/s/1aCqv3eSX-7SXAdGtrGNpTw
提取码:kyqo
相关文章:

C#与西门子PLC1500的ModbusTcp服务器通信4--搭建ModbusTcp客户端
1、客户端选择 客户端可以是一个程序或一个设备,这里我以C#WINFORM程序来实现客户机与PLC的Modbustcp服务器通信,开发环境是VS2019,.NET Framework版本是4.7.2 2、创建winform程序 3、引入Nmodbus4协议 找到项目,找到引用&…...

性能调优篇 二、Jvm监控及诊断工具-命令行篇
目录 一、概述1、简单命令行工具 二、jps:查看正在运行的Java程序(掌握)1、是什么?2、测试3、基本语法 三、jstat:查看jvm统计信息(掌握)1、是什么?2、基本语法3、补充 四、jinfo&am…...

Fooocus启动时modules报错的解决方法
原理:是由于其他程序的安装导致modules的版本不对,先卸载现有版本,再运行run.bat让其自动安装响应的modules版本。 1、cmd运行windows dos终端。 2、将Fooocus_win64_1-1-1035文件夹备份,rename为Fooocus_win64_1-1-1035backup文…...

RSA私钥解密操作
RSA私钥解密操作 一、背景二、操作三、常见问题3.1 invalid key format3.2 解密的数据太长3.3 Decryption error 一、背景 项目数据库中存放的敏感字段已使用rsa加密的方式,将内容加密成密文存放, 现在需要在使用的时候,使用私钥进行解密。 二、操作 …...
数据库基本知识
基本概念 数据 描述事物的符号记录称为数据,数字,文字,图形,图像,声音,档案记录等都是数据 数据是以“记录”的形式按照统一的格式进行存储的,而不是杂乱无章的 相同格式和类型的数据统一存…...
使用Redis统计网站的UV/DAU
HyperLogLog/BitMap 统计UV、DAU需要用到Redis的高级数据类型 M public class RedisKeyUtil {private static final String PREFIX_UV "uv";private static final String PREFIX_DAU "dau";// a single days UVpublic static String getUVKey(String …...
【python】报错:ImportError: DLL load failed: 找不到指定的模块 的详细解决办法
原因:安装的包与python版本不一致 解决方法: 查看python版本: #python / #python -V Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 18:58:18) [MSC v.1900 64 bit (AMD64)] on win32只查看python第三方模块(库、包&…...

SemrushBot蜘蛛爬虫屏蔽方式
查看访问日志时候发现有SemrushBot爬虫 屏蔽方法: 使用robots.txt文件是一种标准的协议,用于告诉搜索引擎哪些页面可以和不能被爬取,如想禁止Googlebot爬取整个网站的话,可以在该文件中添加以下内容: User-agent: Googlebot Disallow: / 对于遵循robots协议的蜘蛛…...
6 ssh面密登录
1. 首先进入自己的家目录,执行命令 [atguiguhadoop102 .ssh]$ ssh-keygen -t rsa然后敲(三个回车),就会生成两个文件id_rsa(私钥)、id_rsa.pub(公钥) 2. 将公钥拷贝到要免密登录的…...

基于微信小程序的汽车租赁系统的设计与实现ljx7y
汽车租赁系统,主要包括管理员、用户二个权限角色,对于用户角色不同,所使用的功能模块相应不同。本文从管理员、用户的功能要求出发,汽车租赁系统系统中的功能模块主要是实现管理员后端;首页、个人中心、汽车品牌管理、…...

优化学习体验的在线考试系统
随着互联网的发展,在线教育逐渐成为学习的主要方式之一。在线考试系统作为在线教育的重要组成部分,对于学习者提供了更为便捷和灵活的学习方式。但是,如何优化学习体验,提高学习效果,仍然是在线考试系统需要解决的问题…...

1267. 统计参与通信的服务器
题目描述: 这里有一幅服务器分布图,服务器的位置标识在 m * n 的整数矩阵网格 grid 中,1 表示单元格上有服务器,0 表示没有。 如果两台服务器位于同一行或者同一列,我们就认为它们之间可以进行通信。 请你统计并返回能…...

【考研数学】矩阵、向量与线性方程组解的关系梳理与讨论
文章目录 引言一、回顾二、梳理齐次线性方程组非齐次线性方程组 写在最后 引言 两个原因让我想写这篇文章,一是做矩阵题目的时候就发现这三货经常绑在一起,让人想去探寻其中奥秘;另一就是今天学了向量组的秩,让我想起来了之前遗留…...

打造个人的NAS云存储-通过Nextcloud搭建私有云盘实现公网远程访问
文章目录 摘要1. 环境搭建2. 测试局域网访问3. 内网穿透3.1 ubuntu本地安装cpolar3.2 创建隧道3.3 测试公网访问 4 配置固定http公网地址4.1 保留一个二级子域名4.1 配置固定二级子域名4.3 测试访问公网固定二级子域名 摘要 Nextcloud,它是ownCloud的一个分支,是一个文件共享服…...

FFI绕过disable_functions
文章目录 FFI绕过disable_functions[RCTF 2019]NextphpPHP7.4 FFI参考 FFI绕过disable_functions [RCTF 2019]Nextphp 首先来看这道题目 index.php <?php if (isset($_GET[a])) {eval($_GET[a]); } else {show_source(__FILE__); }查看一下phpinfo 发现过滤了很多函数&…...

53 个 CSS 特效 2
53 个 CSS 特效 2 这里是第 17 到 32 个,跟上一部分比起来多了两个稍微大一点的首页布局,上篇:53 个 CSS 特效 1,依旧,预览地址在 http://www.goldenaarcher.com/html-css-js-proj/,git 地址: …...

ubuntu学习(六)----文件编程实现cp指令
1 思路 Linux要想复制一份文件通常指令为: cp src.c des.c 其中src.c为源文件,des.c为目标文件。 要想通过文件编程实现cp效果,思路如下 1 首先打开源文件 src.c 2 读src到buf 3 创建des.c 4 将buf写入到des.c 5 close两个文件 2 实现 vi …...

wireshark过滤器的使用
目录 wiresharkwireshark的基本使用wireshark过滤器的区别 抓包案例 wireshark wireshark的基本使用 抓包采用 wireshark,提取特征时,要对 session 进行过滤,找到关键的stream,这里总结了 wireshark 过滤的基本语法,…...
Zookeeper 脑裂问题
什么是脑裂? 脑裂(split-brain)就是“大脑分裂”,也就是本来一个“大脑”被拆分了两个或多个“大脑”,如果一个人有多个大脑,并且相互独立的话,那么会导致人体“手舞足蹈”,“不听使唤”。 脑裂通常会出现…...

计算机网络高频面试题解(一)
1. OSI七层模型 2. TCP/IP五层模型 3. TCP、UDP区别 4. TCP三次握手 5. TCP四次挥手 6. TCP状态转换图 7.TCP状态中TIME_WAIT作用 8. TCP连接建立为什么不是两次握手 9. TCP第三次握手失败会出现什么 10. TCP长连接和短链接及优缺点...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...

Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...