基于ModbusTCP与西门子PLC通讯项目案例

news2024/11/19 19:19:26

目录

一、西门子PLC仿真环境搭建

【1.1】创建PLC项目

【1.2】编写PLC程序

二、C#代码编写

【2.1】窗口制作

【2.2】效果演示

【2.3】读取源码

【2.4】FrmSiemensSet源码

【2.5】Variable源码


一、西门子PLC仿真环境搭建

【1.1】创建PLC项目

  • 搭建PLCSIM-Advacend模拟仿真

  • 设置PLC的IP地址和PLCSIM一致

  • 勾选GET/PUT(如果是S7协议必须勾选)选项

  • 勾选块编译时仿真

  • 创建变量用于测试,相关的DB块需要设置为去除优化访问

【1.2】编写PLC程序

【PLC作为服务器】

【Modbus Poll测试】

二、C#代码编写

【2.1】窗口制作

【2.2】效果演示

【2.3】读取源码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
​
using Approach.DataConvertLib;
using Approach.ModbusTCPLib;
​
namespace Approach.ModbusTCP
{
    public partial class FrmSiemens : Form
    {
        /// <summary>
        /// 【字段】ModbusTCP连接对象
        /// </summary>
        private ModbusTcp _modbusTcp = new ModbusTcp();
​
        /// <summary>
        /// 【字段】ModbusTCP连接成功标识
        /// </summary>
        private bool _isConnected = false;
​
        /// <summary>
        /// 【字段】多线程取消对象
        /// </summary>
        private CancellationTokenSource _cts = null;
​
        public FrmSiemens()
        {
            InitializeComponent();
        }
​
        /// <summary>
        /// 【控件事件】建立连接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Connect_Click(object sender, EventArgs e)
        {
            // 获取UI信息
            string plcIp = txt_Ip.Text.Trim();
            int plcPort = int.Parse(txt_Port.Text.Trim());
​
            _isConnected = _modbusTcp.Connect(plcIp, plcPort);
            if (_isConnected == true)
            {
                MessageBox.Show("与西门子PLC连接成功!", "连接提示");
                // 开启线程(创建取消线程对象)
                _cts = new CancellationTokenSource();
                // 开启线程(创建线程对象)
                Task.Run(new Action(() =>
                {
                    PlcCommunicatin();
                }), _cts.Token);
            }
            else
            {
                MessageBox.Show("与西门子PLC连接失败!", "连接提示");
            }
        }
​
        /// <summary>
        /// 【方法】与PLC进行通信的线程方法
        /// </summary>
        private void PlcCommunicatin()
        {
            // 线程循环执行
            while(!_cts.IsCancellationRequested)
            {
                // 通过ModbusTCP读取寄存器信息(PLC发送182字节->读取91个字)
                byte[] readData = _modbusTcp.ReadOutputRegisters(0, 91);
                // 读取对应的字节个数
                if(readData != null && readData.Length == 182) // 读取成功
                {
                    // 解析报文,跟新UI数据
                    Invoke(new Action(() => {
                        UpdateUI(readData, gb_Read);
                        UpdateUI(readData, gb_Write);
                    }));
                }
            }
        }
​
        /// <summary>
        /// 【方法】
        /// </summary>
        /// <param name="data"> 被处理的数据字节数组 </param>
        /// <param name="conrtol"> 控件对象 </param>
        private void UpdateUI(byte[] data, Control control)
        {
            if (!control.HasChildren)
                return;
​
            // 程序走到这里说明:Control控件中包含一个或者多个子控件
            foreach (var item in control.Controls.OfType<Label>())
            {
                // 排除掉一些标签控件
                if (item.Tag == null || item.Tag.ToString().Length == 0)
                    continue;
                // 程序走到这里说明:获取的控件是需要进行操作的控件
                Variable variable = GetVariableByTag(item.Tag.ToString());
                if (variable != null)
                {
                    switch (variable.ValueType)
                    {
                        case DataType.Bool:
                            {
                                item.BackColor = BitLib.GetBitFromByteArray(data, variable.Start, variable.OffsetOrLength) == true ? Color.LimeGreen : Color.Red;
                                break;
                            }
                        case DataType.Byte:
                            {
                                item.Text = ByteLib.GetByteFromByteArray(data, variable.Start).ToString();
                                break;
                            }
                           
                        case DataType.Short:
                            {
                                item.Text = ShortLib.GetShortFromByteArray(data, variable.Start).ToString();
                                break;
                            }
                        case DataType.UShort:
                            {
                                item.Text = UShortLib.GetUShortFromByteArray(data, variable.Start).ToString();
                                break;
                            }
                        case DataType.Int:
                            {
                                item.Text = IntLib.GetIntFromByteArray(data, variable.Start).ToString();
                                break;
                            }
                        case DataType.UInt:
                            {
                                item.Text = UIntLib.GetUIntFromByteArray(data, variable.Start).ToString();
                                break;
                            }
                        case DataType.Float:
                            {
                                item.Text = FloatLib.GetFloatFromByteArray(data, variable.Start).ToString();
                                break;
                            }
                        case DataType.Double:
                            {
                                item.Text = DoubleLib.GetDoubleFromByteArray(data, variable.Start).ToString();
                                break;
                            }
                        case DataType.Long:
                            {
                                item.Text = LongLib.GetLongFromByteArray(data, variable.Start).ToString();
                                break;
                            }
                        case DataType.ULong:
                            {
                                item.Text = ULongLib.GetULongFromByteArray(data, variable.Start).ToString();
                                break;
                            }
                        case DataType.String:
                            {
                                item.Text = StringLib.GetSiemensStringFromByteArray(data, variable.Start, variable.OffsetOrLength);
                                break;
                            }
                    }
                }
            }
        }
​
        /// <summary>
        /// 【方法】解析Lable->Tag的内容,将其转换为Variable这个类
        /// </summary>
        /// <param name="tagText"></param>
        /// <returns></returns>
        private Variable GetVariableByTag(string tagText)
        {
            if (!tagText.Contains(";"))
                return null;
​
            // 程序走到这里说明该控件Tag内容是可以解析的
            string[] values = tagText.Split(';'); // 分割字符串
            if (values.Length != 3)
                return null;
​
            try
            {
                return new Variable()
                {
                    ValueType = (DataType)Convert.ToInt32(values[0]),
                    Start = Convert.ToInt32(values[1]),
                    OffsetOrLength = Convert.ToInt32(values[2])
                };
            }
            catch (Exception)
            {
                return null;
            }
        }
​
        /// <summary>
        /// 【控件事件】断开连接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_DisConnect_Click(object sender, EventArgs e)
        {
            if (_isConnected == true)
            {
                _modbusTcp.DisConnect();
            }
        }
​
        /// <summary>
        /// 【控件事件】双击Label控件弹出数据修改对话框
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CommonModify_DoubleClick(object sender, EventArgs e)
        {
            if(sender is Label label)
            {
                if (label.Tag == null || label.Tag.ToString().Length <= 0)
                    return;
​
                // 程序走到这里说明:Label的Tag的值可以解析
                // 获取源值
                string srcValue = label.Text;
                // 获取Variable对象
                Variable variable = GetVariableByTag(label.Tag.ToString());
                FrmSiemensSet frmSiemensSet = new FrmSiemensSet(srcValue, variable, _modbusTcp);
                frmSiemensSet.ShowDialog();
            }
        }
    }
}

【2.4】FrmSiemensSet源码

using Approach.DataConvertLib;
using Approach.ModbusTCPLib;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
​
namespace Approach.ModbusTCP
{
    public partial class FrmSiemensSet : Form
    {
        private Variable _variable = null;
        private ModbusTcp _modbusTcp = null;
​
        public FrmSiemensSet(string srcValue, Variable variable, ModbusTcp modbusTcp)
        {
            InitializeComponent();
            lbl_SrcValue.Text = srcValue;
            lbl_ValueType.Text = variable.ValueType.ToString();
​
            _variable = variable;
            _modbusTcp = modbusTcp;
        }
​
​
​
        private void btn_Ok_Click(object sender, EventArgs e)
        {
            bool bResult = false;
            try
            {
                switch (_variable.ValueType)
                {
                    case DataType.Short:
                        {
                            bResult = _modbusTcp.PreSetSingleRegister((ushort)(_variable.Start / 2), Convert.ToInt16(txt_SetValue.Text.Trim()));
                            break;
                        }
                    case DataType.UShort:
                        {
                            bResult = _modbusTcp.PreSetSingleRegister((ushort)(_variable.Start / 2), Convert.ToInt16(txt_SetValue.Text.Trim()));
                            break;
                        }
                    case DataType.Int:
                        {
                            bResult = _modbusTcp.PreSetMultiRegisters((ushort)(_variable.Start / 2), ByteArrayLib.GetByteArrayFromInt(Convert.ToInt32(txt_SetValue.Text.Trim())));
                            break;
                        }
                    case DataType.UInt:
                        {
                            bResult = _modbusTcp.PreSetMultiRegisters((ushort)(_variable.Start / 2), ByteArrayLib.GetByteArrayFromUInt(Convert.ToUInt32(txt_SetValue.Text.Trim())));
                            break;
                        }
                    case DataType.Float:
                        {
                            bResult = _modbusTcp.PreSetMultiRegisters((ushort)(_variable.Start / 2), ByteArrayLib.GetByteArrayFromFloat(Convert.ToSingle(txt_SetValue.Text.Trim())));
                            break;
                        }
                    case DataType.Double:
                        {
                            bResult = _modbusTcp.PreSetMultiRegisters((ushort)(_variable.Start / 2), ByteArrayLib.GetByteArrayFromDouble(Convert.ToDouble(txt_SetValue.Text.Trim())));
                            break;
                        }
                    case DataType.Long:
                        {
                            bResult = _modbusTcp.PreSetMultiRegisters((ushort)(_variable.Start / 2), ByteArrayLib.GetByteArrayFromLong(Convert.ToInt64(txt_SetValue.Text.Trim())));
                            break;
                        }
                    case DataType.ULong:
                        {
                            bResult = _modbusTcp.PreSetMultiRegisters((ushort)(_variable.Start / 2), ByteArrayLib.GetByteArrayFromULong(Convert.ToUInt64(txt_SetValue.Text.Trim())));
                            break;
                        }
                    case DataType.String:
                        {
                            bResult = _modbusTcp.PreSetMultiRegisters((ushort)(_variable.Start / 2), ByteArrayLib.GetByteArrayFromSiemensString(txt_SetValue.Text.Trim()));
                            break;
                        }
                    default:
                        {
                            MessageBox.Show("不支持该数据类型写入", "参数设置");
                            return;
                        }
                }
            }
            catch(Exception ex)
            {
                MessageBox.Show("请检查参数数据类型是否正确:" + ex.Message, "参数设置");
                return;
            }
​
            if (bResult == true)
            {
                this.DialogResult = DialogResult.OK;
            }
        }
        private void btn_Cancel_Click(object sender, EventArgs e)
        {
            this.DialogResult = DialogResult.Cancel;
        }
​
        private void FrmSiemensSet_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
                btn_Ok_Click(null, null);
        }
    }
}

【2.5】Variable源码

using Approach.DataConvertLib;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
​
namespace Approach.ModbusTCP
{
    /// <summary>
    /// 变量实体类封装
    /// </summary>
    public class Variable
    {
        /// <summary>
        /// 标签名称
        /// </summary>
        public string ValueName { get; set; }
​
        /// <summary>
        /// 起始字节
        /// </summary>
        public int Start { get; set; }
​
        /// <summary>
        /// 数据类型
        /// </summary>
        public DataType ValueType { get; set; }
​
        /// <summary>
        /// (位偏移)或(字符串)长度
        /// </summary>
        public int OffsetOrLength { get; set; }
​
        /// <summary>
        /// 变量实际值
        /// </summary>
        public object Value { get; set; }
    }
}

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

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

相关文章

2023年【道路运输企业主要负责人】试题及解析及道路运输企业主要负责人复审考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 道路运输企业主要负责人试题及解析参考答案及道路运输企业主要负责人考试试题解析是安全生产模拟考试一点通题库老师及道路运输企业主要负责人操作证已考过的学员汇总&#xff0c;相对有效帮助道路运输企业主要负责人…

黑豹程序员-架构师学习路线图-百科:JavaScript-网页三剑客

文章目录 1、为什么需要JavaScript2、发展历史3、什么是JavaScript3.1、JavaScript介绍3.2、JavaScript内部结构3.3、主要功能 4、TypeScript 1、为什么需要JavaScript 前面我们已经了解了网页三剑客的HTML和CSS&#xff0c;已经明确了它们的职责。 HTML负责页面的展现&#x…

【Java】cron表达式

文章目录 1、语法2、取值3、cron表达式生成工具 定时任务相关的需求很多&#xff0c;对应的实现技术与框架也有不少&#xff0c;例如xxl-job、Quartz、Spring Task。不论怎么技术选型&#xff0c;cron表达式通常都是必要的。虽然现在生成cron表达式的小工具网站很多&#xff0c…

从某达OA到Yii2框架的cookie反序列化漏洞研究

序言 近期网上流传的某达OA存在PHP反序列化漏洞&#xff0c;导致命令执行。因为该漏洞底层是Yii2框架的漏洞&#xff0c;所以搭建好了Yii2框架环境&#xff0c;在Yii2框架的环境下来进行模拟研究&#xff0c;希望能达到举一反三和类比分析学习的目的。该cookie处反序列化漏洞属…

VBA技术资料MF66:使用代码插入行或列

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

LangChain 用例(未完)

在Azure上的OpenAI端点 注意 OpenAI key 可以用微软 用例【1. 嵌入 &#xff0c;2. 问答】 1. import os import openai from langchain.embeddings import OpenAIEmbeddings os.environ["OPENAI_API_KEY"] "****" # Azure 的密钥 os.environ["OP…

[ 车牌识别 License Plate Detection and Recognition ]

从0到1 视频讲解&#xff1a;图像处理和模式识别案例-一个简单的车牌识别系统 车牌识别 整体流程可以分成三个部分&#xff0c; (1) 车牌定位/检测&#xff1b; (2) 字符分割&#xff1b; (3) 字符识别。 它们各自发展出很多方法以应对不同的情况。 下面用一种完整的技术路线…

Java循环结构:一件事重复做才能有效果。

&#x1f451;专栏内容&#xff1a;Java⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、while循环1、基本语法2、注意事项3、break语句4、continue语句 二、do-while循环三、for循环1、基本语法2、注意事项 四、循…

kafka集群工作机制

一、kafka在zookeeper上的元数据解释 kafka中的broker要选举Controller角色来管理整个kafka集群中的分区和副本状态。一个Topic下多个partition要选举Leader角色和客户端进行交互数据 Zookeeper客户端工具&#xff1a; prettyZoo。 下载地址&#xff1a;https://github.com/vr…

单调队列---数据结构与算法

简介 队列也是一种受限制的线性表和栈相类似&#xff0c;栈是先进后出&#xff0c;而队列是先进先出&#xff0c;就好像一没有底的桶&#xff0c;往里面放东西&#xff0c;如图 在这里也是用数组来实现队列&#xff0c;用数组实现的叫做顺序队列 队列的数组模拟 const int N…

网络安全的发展方向是什么?网络安全学什么内容

前言 不少小伙伴开始学习网络安全技术&#xff0c;但却不知道学习网络安全能找什么工作&#xff1f;网络安全是现下较为火热的职业岗位&#xff0c;吸引了许多企业和个人对网络安全技术的青睐。学习网络安全的人越来越多&#xff0c;网络安全也有很多发展方向。那么如何选择网…

mysql日期月份相关函数

从给定日期提取最后一天&#xff1a; 要知道2017年12月的最后日期&#xff0c;可以按以下方式执行LAST_DAY()函数&#xff1a;用法:输出&#xff1a; 2017-12-31 从给定的日期时间中提取最后一天&#xff1a; 要使用日期时间格式了解月份的最后日期&#xff0c;可以按以下方式…

目标检测算法改进系列之Backbone替换为NextViT

NextViT介绍 由于复杂的注意力机制和模型设计&#xff0c;大多数现有的视觉Transformer&#xff08;ViTs&#xff09;在现实的工业部署场景中不能像卷积神经网络&#xff08;CNNs&#xff09;那样高效地执行&#xff0c;例如TensorRT 和 CoreML。这带来了一个明显的挑战&#…

Html+Css+Js计算时间差,返回相差的天/时/分/秒(从未来的一个日期时间到当前日期时间的差)。

Html部分 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title><link rel"stylesheet" type"text/css" href"css/index.css" /><script src"js/index.js" t…

库函数的模拟实现

前言&#xff1a; 在上一篇文章中我们了解到了一些库函数的使用&#xff0c;为了加深我们对库函数的理解&#xff0c;我们来模拟实现一下这些库函数的用法。 这是上一篇文章的链接&#xff1a; http://t.csdnimg.cn/r7SKNhttp://t.csdnimg.cn/r7SKN 1.模拟实现strlen 模拟…

【C++】运算符重载 ⑥ ( 一元运算符重载 | 后置运算符重载 | 前置运算符重载 与 后置运算符重载 的区别 | 后置运算符重载添加 int 占位参数 )

文章目录 一、后置运算符重载1、前置运算符重载 与 后置运算符重载 的区别2、后置运算符重载添加 int 占位参数 上 2 2 2 篇博客 【C】运算符重载 ④ ( 一元运算符重载 | 使用 全局函数 实现 前置 自增运算符重载 | 使用 全局函数 实现 前置 - - 自减运算符重载 )【C】运算符…

算法题:盛最多水的容器

这个题目乍一看就是双指针&#xff0c;没想到官方解答也是双指针&#xff0c;我在官方的基础上优化了一下下&#xff0c;左右两边各一个指针&#xff0c;每次移动短的那一头的时候&#xff0c;不是移动一格&#xff0c;而是找到比短的那一头要长一点的&#xff0c;再进行比较。…

文心一言 VS 讯飞星火 VS chatgpt (106)-- 算法导论10.1 4题

四、用go语言&#xff0c;重写 ENQUEUE 和 DEQUEUE 的代码&#xff0c;使之能处理队列的下溢和上溢。 文心一言&#xff1a; 在 Go 语言中&#xff0c;我们可以使用切片来实现队列。下面是如何实现 ENQUEUE 和 DEQUEUE 操作&#xff0c;同时处理队列的下溢和上溢的情况&#…

坦克世界WOT知识图谱三部曲之爬虫篇

文章目录 关于坦克世界1. 爬虫任务2. 获取坦克列表3. 获取坦克具体信息结束语 关于坦克世界 《坦克世界》(World of Tanks, WOT)是我在本科期间玩过的一款战争网游&#xff0c;由Wargaming公司研发。2010年10月30日在俄罗斯首发&#xff0c;2011年4月12日在北美和欧洲推出&…

目标检测算法改进系列之Backbone替换为RepViT

RepViT简介 轻量级模型研究一直是计算机视觉任务中的一个焦点&#xff0c;其目标是在降低计算成本的同时达到优秀的性能。轻量级模型与资源受限的移动设备尤其相关&#xff0c;使得视觉模型的边缘部署成为可能。在过去十年中&#xff0c;研究人员主要关注轻量级卷积神经网络&a…