当前位置: 首页 > news >正文

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

目录

一、西门子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; }}
}

相关文章:

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

目录 一、西门子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模拟仿…...

Oralce数据库管理 -操作系统cpu 内存 io指标分析查询

1 前35个cpu消耗较大的进程 ps aux|head -1;ps aux|grep -v PID|sort -rn -k 3|head -35 1 前35个内存消耗较大的进程 ps aux|head -1;ps aux|grep -v PID|sort -rn -k 4|head -35...

my_print_defaults 及perror

参考文档&#xff1a; https://mysql.net.cn/doc/refman/8.0/en/my-print-defaults.html https://mysql.net.cn/doc/refman/8.0/en/perror.html -- my.cnf的内容 [rootredhat762100 mysql3306]# more my.cnf [mysqld] datadir/mysql/mysql3306/data #socket/tmp/mysql3306.so…...

视频转GIF:快速生成有趣的动态图片

随着社交媒体的快速发展&#xff0c;GIF动态图片已经成为了人们表达情感、分享生活片段的重要方式。将视频片段转换成GIF动态图片&#xff0c;可以让人们更好地分享和表达自己的情感&#xff0c;也可以让一些有趣的瞬间变得更加生动有趣。本文将介绍如何将视频快速转换成GIF动态…...

vue3 vscode no tsconfig与找不到名称“ref”。ts(2304)

如题&#xff0c;这两个问题都与tsconfig的配置有关&#xff0c;先看下问题表现&#xff1a; 解决方法&#xff0c;应当正确配置如下&#xff0c;之后保存或重启vscode&#xff1a;...

Docker基本操作【一篇学会项目部署】

文章目录 一、Docker简介二、Docker安装三、配置镜像加速四、Docker部署五、Docker基础操作1. 常见命令2. 操作演示3. 数据卷①nginx的html目录挂载②分析匿名数据卷③MySQL的本地目录挂载 4. 自定义镜像①Dockerfile②构建镜像 5. 网络①常见命令②自定义网络 六、DockerCompo…...

目标识别项目实战:基于Yolov7-LPRNet的动态车牌目标识别算法模型(二)

前言 目标识别如今以及迭代了这么多年&#xff0c;普遍受大家认可和欢迎的目标识别框架就是YOLO了。按照官方描述&#xff0c;YOLOv8 是一个 SOTA 模型&#xff0c;它建立在以前 YOLO 版本的成功基础上&#xff0c;并引入了新的功能和改进&#xff0c;以进一步提升性能和灵活性…...

Ceph入门到精通-sysctl.conf 配置

sysctl.conf Ubuntu server out of box is not optimized to make full use of available hardware. This means “out-of-box” setup might fail under high load. So we need to tweak system configuration for maximum concurrancy. Sysctl Tweaks Open vim /etc/sys…...

Cesium 展示——实体点击的相关属性,进行增删改

文章目录 需求分析1. 实体创建2. 相关属性需求 点击已加载的实体,获取该实体的所有属性,从而对实体进行增删改 分析 1. 实体创建 var viewer = new Cesium.Viewer(cesiumContainer, {terrainProvider: Cesium....

【算法小课堂】二分查找算法

简单思路&#xff1a; 当我们要从一个序列中查找一个元素的时候&#xff0c;最快想到的方法就是顺序查找法&#xff08;即&#xff1a;从前到后依次查找&#xff09;。但这种方法过于无脑&#xff0c;就是暴力的把每个元素都排查一遍。元素个数少的时候还行&#xff0c;一旦元…...

git修改提交历史中的author信息

全局设置 git config --global user.name "作者名" 局部设置(本项目) git config user.name "作者名" git修改提交作者和邮箱-CSDN博客 git修改提交作者和邮箱-CSDN博客...

【gitlab】本地项目上传gitlab

需求描述 解决方法 下面的截图是gitlab空项目的描述 上传一个本地项目按其中“Push an existing folder”命令即可。 以renren-fast项目为例 # 用git bash 下载renren-fast项目 git clone https://gitee.com/renrenio/renren-fast.git# 在renren-fast的所属目录 打开git ba…...

freertos信号量之计数信号量

freertos信号量之计数信号量 简介例程 简介 计数信号量&#xff08;Counting Semaphore&#xff09;用于管理共享资源的访问。以下是计数信号量的常用函数及其说明&#xff1a; 1&#xff09;xSemaphoreCreateCounting(unsignedportBASE_TYPE uxMaxCount, unsignedportBASE_T…...

wc命令使用指南 | 教你如何高效统计文件字数、行数和字符数

文章目录 wc命令使用指南1. 引言1.1 什么是wc命令&#xff1f;1.2 wc命令的作用和用途1.3 wc命令的常用参数 2. 基本使用2.1 安装和启动wc命令2.2 统计文件的行数2.3 统计文件的字数2.4 统计文件的字符数2.5 统计文件的词数2.6 统计文件的最长行长度 3. 高级使用3.1 统计多个文…...

网络安全:发起一次CSRF攻击!

一、如何发起一次CSRF攻击 原理&#xff1a;CSRF 的本质实际上是利用了 Cookie 会自动在请求中携带的特性&#xff0c;通过伪造请求来执行恶意操作。 1、目标网站信息&#xff1a; 接口地址&#xff1a;https://victim.com/change-password 请求类型&#xff1a;get/post 接…...

java上传文件到指定服务器

首先要知道服务器的用户名和密码。 注意&#xff1a;一般情况&#xff0c;如果不是强制要求&#xff0c;尽量不要将文件上传到服务器 步骤&#xff1a; 1.导入依赖 <!--图片上传到服务器需要的依赖--> <dependency> <groupId>com.jcr…...

揭秘 Go 中的 new() 和 make() 函数

Go&#xff08;或 Golang&#xff09;是一种现代、静态类型、编译型的编程语言&#xff0c;专为构建可扩展、并发和高效的软件而设计。它提供了各种内置的函数和特性&#xff0c;帮助开发人员编写简洁高效的代码。其中包括 new() 和 make() 函数&#xff0c;这两个函数乍看起来…...

【Spring Cloud】深入探索统一网关 Gateway 的搭建,断言工厂,过滤器工厂,全局过滤器以及跨域问题

文章目录 前言为什么需要网关以及网关的作用网关的技术实现 一、Gateway 网关的搭建1.1 创建 Gateway 模块1.2 引入依赖1.3 配置网关1.4 验证网关是否搭建成功1.5 微服务结构分析 二、Gateway 断言工厂2.1 Spring 提供的断言工厂2.2 示例&#xff1a;设置断言工厂 三、Gateway …...

计算机竞赛 题目:基于卷积神经网络的手写字符识别 - 深度学习

文章目录 0 前言1 简介2 LeNet-5 模型的介绍2.1 结构解析2.2 C1层2.3 S2层S2层和C3层连接 2.4 F6与C5层 3 写数字识别算法模型的构建3.1 输入层设计3.2 激活函数的选取3.3 卷积层设计3.4 降采样层3.5 输出层设计 4 网络模型的总体结构5 部分实现代码6 在线手写识别7 最后 0 前言…...

关于flink重新提交任务,重复消费kafka的坑

异常现象1 按照以下方式设置backend目录和checkpoint目录&#xff0c;fsbackend目录有数据&#xff0c;checkpoint目录没数据 env.getCheckpointConfig().setCheckpointStorage(PropUtils.getValueStr(Constant.ENV_FLINK_CHECKPOINT_PATH)); env.setStateBackend(new FsStat…...

Win11右键恢复Win10老版本

Win11右键恢复Win10老版本 最近自己更新了windows11的OS,整体感觉都是不错的,但是就是每次右键菜单我都要再次点击下展开更多选项,这对追求极简主义的我,就是不爽, 手动恢复win10操作吧! 第一种:创建文件(简单快速) 1.新建一个resoreRightKey.reg文件,并在里面填入如下代码 W…...

ur机械臂30003端口socket通信踩坑(double类型数据怎么解析)

坑的由来 都知道在网络通信时要把网络字节序转化为主机字节序才行&#xff0c;但是c里的标准库函数ntohl默认是转换32位字节序的数据&#xff0c;也就是说默认是转换float类型的数据&#xff1b;而ur机械臂30003端口发送的是double类型的数据&#xff0c;没法直接用ntohl进行转…...

代理IP与Socks5代理的技术奇妙之旅

随着数字化时代的崛起&#xff0c;网络工程师们日益承担着维护网络稳定性和保护数据安全的重任。在这个充满挑战的世界里&#xff0c;代理IP与Socks5代理技术成为了他们的秘密武器&#xff0c;本文将带您踏上一段技术奇妙之旅&#xff0c;深入了解这两项技术在不同领域中的应用…...

自动化测试定位不到元素?可能是 frame 在搞鬼

很多人在用Splinter或Selenium定位页面元素的时候会遇到定位不到的问题&#xff0c;明明元素就在那儿&#xff0c;就是定位不到&#xff0c;这种情况很有可能是frame在搞鬼。 说白了就是网站上的网页A&#xff0c;又嵌入了其他网页B。你访问了网页A&#xff0c;在里面可以看到…...

uni-app 开发中,监听 input 键盘事件获取不到按下按键值怎么办?

uniapp 开发 H5 时&#xff0c;无法监听按钮键盘事件的原因以及解决方法。 问题描述&#xff1a; 不少 uni-app 开发者在使用 input 组件时&#xff0c;监听 keyup 事件时&#xff0c;获取不到键盘的 keyCode。编写的代码如下&#xff1a; <template><input keyup&…...

【juc】countdownlatch实现并发网络请求

目录 一、截图示例二、代码示例2.1 测试代码2.2 接口代码 一、截图示例 二、代码示例 2.1 测试代码 package com.learning.countdownlatch;import lombok.extern.slf4j.Slf4j; import org.springframework.web.client.RestTemplate;import java.util.Arrays; import java.uti…...

在供应链管理中,如何做好库存分析?库存分析有哪些监控指标?

在供应链管理中&#xff0c;库存分析是其重要的一环。库存分析的方法繁杂且广泛&#xff0c;选择正确的方法才能更好的进行库存分析&#xff0c;下面就为大家盘点一些常用的库存分析方法和监控指标&#xff0c;全程干货&#xff0c;建议收藏&#xff01; 01 如何进行库存分析&…...

黑豹程序员-架构师学习路线图-百科:Database数据库

文章目录 1、什么是Database2、发展历史3、数据库排行网4、总结 1、什么是Database 当今世界是一个充满着数据的互联网世界&#xff0c;各处都充斥着大量的数据。即这个互联网世界就是数据世界。 支撑这个数据世界的基石就是数据库&#xff0c;数据库也可以称为数据的仓库。 …...

你相信光吗?黑灯工厂重新相信“光”

你知道“黑灯工厂”吗&#xff1f;望文生义&#xff0c;所谓黑灯工厂&#xff0c;就是可以不需要照明的工厂。全程流水线自动化生产&#xff0c;无人干预、无人值守&#xff0c;工厂变成黑匣子&#xff0c;原材料进去&#xff0c;成品出来&#xff0c;流水线上百分百自动化。 完…...

Ubuntu 20.04使用源码安装nginx 1.14.0

nginx安装及使用&#xff08;详细版&#xff09;是一篇参考博文。 http://nginx.org/download/可以选择下载源码的版本。 sudo wget http://nginx.org/download/nginx-1.14.0.tar.gz下载源代码。 sudo tar xzf nginx-1.14.0.tar.gz进行解压。 cd nginx-1.14.0进入到源代码…...