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  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/49945.shtml

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

相关文章

IntelliJ IDEA maven配置,设置pom.xml的配置文件

IntelliJ IDEA项目&#xff0c;选择 文件 设置&#xff0c;弹窗 构建、执行、部署 构建工具 Maven就可以 maven配置好以后&#xff0c;在pom.xml的配置文件中就可以设置对应的jar包了&#xff0c;这样构建的时候自动需要的jar&#xff0c;在项目中导入即 需要的jar包设置在po…

大数据Flink(六十六):Flink的重要概念和小结

文章目录 Flink的重要概念和小结 一、​​​​​​​​​​​​​​数据流图(Dataflow Graph)

解锁ChatGLM-6B的潜力:优化大语言模型训练,突破任务困难与答案解析难题

解锁ChatGLM-6B的潜力&#xff1a;优化大语言模型训练&#xff0c;突破任务困难与答案解析难题 LLM&#xff08;Large Language Model&#xff09;通常拥有大量的先验知识&#xff0c;使得其在许多自然语言处理任务上都有着不错的性能。 但&#xff0c;想要直接利用 LLM 完成…

【rust/egui】(四)看看template的app.rs:update以及组件TopBottomPanelButton

说在前面 rust新手&#xff0c;egui没啥找到啥教程&#xff0c;这里自己记录下学习过程环境&#xff1a;windows11 22H2rust版本&#xff1a;rustc 1.71.1egui版本&#xff1a;0.22.0eframe版本&#xff1a;0.22.0上一篇&#xff1a;这里 update update实际上还是eframe::App的…

自研分布式IM-HubuIM RFC草案

HubuIM RFC草案 消息协议设计 基本协议 评估标准 【性能】协议传输效率&#xff0c;尽可能降低端到端的延迟&#xff0c;延迟高于200ms用户侧就会有所感知 【兼容】既要向前兼容也要向后兼容 【存储】减少消息包的大小&#xff0c;降低空间占用率&#xff0c;一个字节在亿…

最新AI系统ChatGPT程序源码/微信公众号/H5端+搭建部署教程+完整知识库

一、前言 SparkAi系统是基于国外很火的ChatGPT进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。 那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧&#xff01…

二级MySQL(五)——完整性约束练习

在MYSQL中&#xff0c;通常用来指定一个已有数据库作为当前数据库的语句是【USE】 下列选项中不是MYSQL中常用数据类型的是【VAR】 在MYSQL中&#xff0c;常用【NULL】表示一个字段没有值或缺值 在CREATE TABLE语句中&#xff0c;通常使用【PRIMARY KEY】关键字来指定主键 …

IoT DC3 是一个基于 Spring Cloud 的开源的、分布式的物联网(IoT)平台本地部署步骤

dc3 windows 本地搭建步骤&#xff1a; ​​ 必要软件环境 进入原网页# 务必保证至少需要给 docker 分配&#xff1a;1 核 CPU 以及 4G 以上的运行内存&#xff01; JDK : 推荐使用 Oracle JDK 1.8 或者 OpenJDK8&#xff0c;理论来说其他版本也行&#xff1b; Maven : 推荐…

【视觉SLAM入门】5.2. 2D-3D PNP 3D-3D ICP BA非线性优化方法 数学方法SVD DLT

"养气之学&#xff0c;戒之躁急" 1. 3D-2D PNP1.1 代数法1.1.1 DLT(直接线性变换法)1.1.2. P3P 1.2 优化法BA (Bundle Adjustment)法 2. 3D-3D ICP2.1 代数法2.1.1 SVD方法 2.2 优化(BA)法2.2.2 非线性优化方法 前置事项&#xff1a; 1. 3D-2D PNP 该问题描述为&am…

记录《现有docker中安装spark3.4.1》

基础docker环境中存储hadoop3--方便后续查看 参考&#xff1a; 实践&#xff1a; export JAVA_HOME/opt/apache/jdk1.8.0_333 export SPARK_MASTER_IP192.168.0.220 export SPARK_WORKER_MEMORY4g export SPARK_WORKER_CORES2 export SPARK_EXECUTOR_MEMORY4g export HADOOP_H…

Mongodb两种启动方法

一、命令行启动 1.修改存放数据库的位置 说明&#xff1a;E:\data\mongodb&#xff1b;我在E盘创建的文件夹mongodb mongod --dbpathE:\data\mongodb 2.成功启动 说明&#xff1a;默认端口27017&#xff0c;代表已经启动成功 &#xff0c;并在mongodb自动创建文件 二、配置项…

华为AR路由器 典型配置案例——以太网交换

目录 Eth-Trunk 例&#xff1a;配置三层链路聚合 组网需求 操作步骤 检查配置结果 配置脚本 VLAN 举例&#xff1a;配置基于接口划分VLAN&#xff0c;实现同一VLAN内的互通&#xff08;同设备&#xff09; 组网需求 操作步骤 检查配置结果 配置脚本 举例&#xff…

TCP半连接队列和全连接队列

目录 什么是 TCP 半连接队列和全连接队列&#xff1f; TCP 全连接队列溢出 如何知道应用程序的 TCP 全连接队列大小&#xff1f; 如何模拟 TCP 全连接队列溢出的场景&#xff1f; 全连接队列溢出会发生什么 ? 如何增大全连接队列呢 ? TCP 半连接队列溢出 如何查看 TC…

22款美规奔驰S500升级原厂香氛负离子系统,清香宜人,久闻不腻

奔驰原厂香氛合理性可通过车内空气调节组件营造芳香四溢的怡人氛围。通过更换手套箱内香氛喷雾发生器所用的香水瓶&#xff0c;可轻松选择其他香氛。香氛的浓度和持续时间可调。淡雅的香氛缓缓喷出&#xff0c;并且在关闭后能够立刻散去。车内气味不会永久改变&#xff0c;香氛…

Linux 虚拟机安装 hadoop

目录 1 hadoop下载 2 解压hadoop 3 为 hadoop 文件夹改名 4 给 hadoop 文件夹赋权 5 修改环境变量 6 刷新环境变量 7 在hadoop313目录下创建文件夹data 8 检查文件 9 编辑 ./core-site.xml文件 10 编辑./hadoop-env.sh文件 11 编辑./hdfs-site.xml文件 12 编辑./mapr…

【C++入门到精通】C++入门 —— 模版(template)

阅读导航 前言一、模版的概念二、函数模版1. 函数模板概念2. 函数模板定义格式3. 函数模板的原理4. 函数模版的实例化&#x1f6a9;隐式实例化&#x1f6a9;显式实例化 5. 函数模板的匹配原则 三、类模板1. 类模板的定义格式2. 类模板的实例化 四、非类型模板参数1. 概念2. 定义…

动态规划之路径问题

路径问题 1. 不同路径&#xff08;medium&#xff09;2. 不同路径II&#xff08;medium&#xff09;3. 礼物最大值&#xff08;medium&#xff09;4. 下降路径最小和&#xff08;medium&#xff09;5. 最⼩路径和&#xff08;medium&#xff09;6. 地下城游戏&#xff08;hard&…

图床项目进度(一)——UI首页

1. 前言 前面我不是说了要做一个图床吗&#xff0c;现在在做ui。 我vue水平不够高&#xff0c;大部分参考b站项目照猫画虎。 vue实战后台 我使用ts&#xff0c;vite&#xff0c;vue3进行了重构。 当然&#xff0c;我对这些理解并不深刻&#xff0c;许多代码都是游离于表面&am…

基于决策树(Decision Tree)的乳腺癌诊断

决策树(DecisionTree)学习是以实例为基础的归纳学习算法。算法从--组无序、无规则的事例中推理出决策树表示形式的分类规则,决策树也能表示为多个If-Then规则。一般在决策树中采用“自顶向下、分而治之”的递归方式,将搜索空间分为若千个互不相交的子集,在决策树的内部节点(非叶…

Spring事务和事务传播机制

目录 一. Spring 中事务的实现 1. 编程式事务 2. 声明式事务 3. Transaction 参数的设置 4. Transaction 的隔离级别 5. Transaction 的工作原理 二. Spring 事务传播机制 七种事务传播机制 支持当前事务 不支持当前事务 嵌套事务 一. Spring 中事务的实现 1. 编程式事…