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长连接和短链接及优缺点...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
