文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2023-09-16 12:25

关注

 1、客户端选择

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

2、创建winform程序

 创建类库

 

编写C#各种类的转换库,该库由我提供,不用操心,文章最后提供。

项目引入这个类库 

 

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(从站地址,开始地址,寄存器数量)

  ///         /// 读取        ///         ///         ///         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个寄存器数量

如果要读取“摩头2温度”,怎么读了?

 

 大家想想,为什么开始地址是8?

9、写入的代码--ushort类型

 ///         /// 写入        ///         ///         ///         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类型

 

写入负数

 我们向“摩头2温度”这个寄存器写入数据

再看博途中的数据

 

11、小结

客户端创建tcp client对象,然后modbus利用tcp对象创建modbus通信,然后通过不同数据类型读写PLC数据,成功了

代码链接:

链接:https://pan.baidu.com/s/1aCqv3eSX-7SXAdGtrGNpTw 
提取码:kyqo  

来源地址:https://blog.csdn.net/hqwest/article/details/132450229

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-服务器
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯