C#与西门子PLC1500的ModbusTcp服务器通信4--搭建ModbusTcp客户端

news2025/1/13 7:35:51

 1、客户端选择

客户端可以是一个程序或一个设备,这里我以C#WINFORM程序来实现客户机与PLC的Modbustcp服务器通信,开发环境是VS2019,.NET Framework版本是4.7.2

2、创建winform程序

 

 3、引入Nmodbus协议

 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,44
                                ushort[] 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.coloradmin.cn/o/917298.html

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

相关文章

无涯教程-PHP - Filtered反序列化

PHP 7引入了Filtered unserialize()函数&#xff0c;以在对不受信任的数据上的对象进行反序列化时提供更好的安全性。 <?phpclass MyClass1 { public $obj1prop; }class MyClass2 {public $obj2prop;}$obj1new MyClass1();$obj1->obj1prop1;$obj2new MyClass2();$obj…

java 版本企业招标投标管理系统源码+功能描述+tbms+及时准确+全程电子化 tbms

​ 功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查…

Java串口开发

网上搜索了关于java串口开发的资料,发现都不是特别的全,故写下一些心得以帮助其他人能快速上手java串口开发,如有错漏之处&#xff0c;敬请指正 串口开发会用到一个javax.comm和RXTXcomm库,&#xff0c;javax.comm库不支持64位操作系统。该库仅适用于32位操作系统,所以接下来主…

SAP S/4 BP(Business Partner)之一次性客商设置

目录 前言 一、一次性客商是什么&#xff1f; 二、BP创建步骤 1.开始步骤 总结 前言 在ECC里的一次性客商跟在S/4有一些区别&#xff0c;本文主要介绍在S/4里创建一次性BP的步骤。 一、一次性客商是什么&#xff1f; 一次性供应商是指我们通常不经常从其采购材料的供应商…

课程实录 | Ingress Controller 的工作原理(下)

原文作者&#xff1a;陶辉 - 杭州智链达数据有限公司&#xff0c;联合创始人兼 CTO 原文链接&#xff1a;课程实录 | Ingress Controller 的工作原理&#xff08;下&#xff09; 转载来源&#xff1a;NGINX 开源社区 NGINX 唯一中文官方社区 &#xff0c;尽在 nginx.org.cn 编者…

Android Lottie加载gson文件动画

一&#xff1a;Lottie的使用 在你工程的build.gradle文件里添加如下配置 implementation com.airbnb.android:lottie:3.4.0二&#xff1a;布局文件直接引入LottieAnimationView <com.airbnb.lottie.LottieAnimationViewandroid:id"id/lottie_view"android:layout…

axios 介绍

axios 介绍 axios 是一款基于 javascript xhr 进行封装的插件&#xff0c;自己通过 xhr 进行编写 ajax 请求&#xff0c;实现起来逻辑比较复杂&#xff0c;axios 封装后将复杂的逻辑写在插件的内部&#xff0c;我们用户只需要关心如何调用即可。对我们的开发带来了很大的便捷。…

在App备案的规则下,开发者的技术选型更加重要了

网站 ICP 备案已施行了很久&#xff0c;我们也非常清楚必须在进行 ICP 备案后&#xff0c;网站才能在大陆范围合法运营&#xff0c;并且用户可以通过域名正常访问网站。 但是月初出了新规&#xff0c;明年起&#xff0c;国内的 App 也要像网站一样进行备案了。想必大家也是早已…

HTTPS证书如何申请?只需这五个步骤

在如今的互联网时代&#xff0c;网络安全变得愈发重要。为了保障网站内数据的安全&#xff0c;许多网站都开始使用HTTPS协议来进行数据传输。而谷歌、百度等搜索巨擘启用全站HTTPS加密服务&#xff0c;更是掀起了网站HTTPS加密的浪潮。那么申请HTTPS证书都需要什么&#xff1f;…

Typora mac版本安装

提示&#xff1a;文章介绍&#xff0c;Typora在Mac系统中免费安装使用 文章目录 一、官网下载二、安装 一、官网下载 官网地址&#xff1a;https://www.typoraio.cn/ 二、安装 安装好后按 command 空格键&#xff0c;找到 Typora的安装路径 /Applications/Typora.app/Con…

信创、工业软件国产化:全面解析三大实时操作系统

信创与国产工业操作系统可以擦出什么火花。 信创技术的快速发展&#xff0c;为国产工业操作系统的研发和应用提供了广阔的空间。 工业操作系统作为工业制造的大脑和神经&#xff0c;工业软件已渗透和应用到工业领域几乎所有核心环节。工业操作系统是智能制造的核心&#xff0c;…

Vue3.2中使用swiper实现层叠式轮播图

介绍 在 vue3 中使用 swiper 实现缩略图的轮播图效果&#xff0c;具体如下图所示&#xff1a; 代码 <template><div classindex><div class"banner-box"><swiper:autoplay"state.autoplay":loop"state.loop":speed&q…

2023 Android 折叠屏适配详解,是时候点亮新技能了

自 2019 年三星发布了第一台&#xff08;柔宇不算&#xff09; Galaxy Z Fold 之后&#xff0c;Android 厂商们都陆续跟进了各自的可折叠方案&#xff0c;之后折叠屏手机市场一直保持快速增长&#xff0c;例如 2023 年上半年整体销量 227 万台&#xff0c;同比增长 102.0%。 虽…

某支付巨头:提升安全内生“数智”能力,筑牢应用安全长城

某企业是支付领域巨头&#xff0c;处于行业核心地位&#xff0c;推动了中国支付产业的智能化和数字化转型。该企业提供相关专业化服务和金融科技服务&#xff0c;共建开放生态&#xff0c;打造数字网络&#xff0c;服务全球支付。 构建金融操作系统 推动中国金融数字化发展 …

爬虫:绕过5秒盾Cloudflare和DDoS-GUARD

本文章仅供技术研究参考&#xff0c;勿做它用&#xff01; 5秒盾的特点 <title>Just a moment...</title> 返回的页面中不是目标数据&#xff0c;而是包含上面的代码&#xff1a;Just a moment... 或者第一次打开网页的时候&#xff1a; 这几个特征就是被Cloud…

2023年7月天猫糕点市场数据分析(天猫数据怎么看)

烘焙食品行业是近几年食品领域比较火热的赛道之一&#xff0c;随着居民饮食结构的变化&#xff0c;人均消费水平的上升&#xff0c;蛋糕、面包等烘焙糕点越发成为消费者饮食的重要组成部分。同时&#xff0c;在烘焙糕点市场中&#xff0c;老品牌不断推新迭变&#xff0c;新品牌…

线性代数的学习和整理11: 子式与余子式

目录 1 原始矩阵A 2 子式&#xff08;都是行列式&#xff09; 2.1 k阶子式 2.2 k阶主子式 2.3 k阶顺序主子式 3 余子式 3.1 余子式 3.2 代数余子式 3.3 余子式作用是&#xff1f; 1 原始矩阵A 下面设计一个原始矩阵A&#xff0c;故意设计为A34, 行数≠列数 $$ \lef…

【车载开发系列】UDS当中的时间参数

【车载开发系列】UDS当中的时间参数 UDS当中的时间参数 【车载开发系列】UDS当中的时间参数一. 术语定义二. 网络层时间调整参数三. ECU诊断层与会话层参数 一. 术语定义 缩写全称中文说明BSBlock Size块大小STminSeparation time min时间间隙SIService Identifier服务标识符S…

如何在App里拉起小程序?

什么是小程序运行时框架&#xff1f; FinClip 的小程序编程模型是分为多个页面&#xff0c;每个页面有自己的 template、CSS 和 JS&#xff0c;实际在运行的时候&#xff0c;业务逻辑的 JS 代码是运行在独立的 JavaScript 引擎中&#xff0c;每个页面的 template 和 CSS 是运行…

使用Jetpack Compose构建可折叠Card

使用Jetpack Compose构建可折叠Card 为何在Android应用开发中使用扩展卡片 扩展卡片在Android应用开发中广受欢迎&#xff0c;它们可以让开发者打造干净紧凑的用户界面&#xff0c;同时可以轻松展开&#xff0c;显示额外的内容。 通过巧妙地使用扩展卡片&#xff0c;开发者可…