基于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
参考文档: 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:快速生成有趣的动态图片
随着社交媒体的快速发展,GIF动态图片已经成为了人们表达情感、分享生活片段的重要方式。将视频片段转换成GIF动态图片,可以让人们更好地分享和表达自己的情感,也可以让一些有趣的瞬间变得更加生动有趣。本文将介绍如何将视频快速转换成GIF动态…...

vue3 vscode no tsconfig与找不到名称“ref”。ts(2304)
如题,这两个问题都与tsconfig的配置有关,先看下问题表现: 解决方法,应当正确配置如下,之后保存或重启vscode:...

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

目标识别项目实战:基于Yolov7-LPRNet的动态车牌目标识别算法模型(二)
前言 目标识别如今以及迭代了这么多年,普遍受大家认可和欢迎的目标识别框架就是YOLO了。按照官方描述,YOLOv8 是一个 SOTA 模型,它建立在以前 YOLO 版本的成功基础上,并引入了新的功能和改进,以进一步提升性能和灵活性…...
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....

【算法小课堂】二分查找算法
简单思路: 当我们要从一个序列中查找一个元素的时候,最快想到的方法就是顺序查找法(即:从前到后依次查找)。但这种方法过于无脑,就是暴力的把每个元素都排查一遍。元素个数少的时候还行,一旦元…...
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信号量之计数信号量 简介例程 简介 计数信号量(Counting Semaphore)用于管理共享资源的访问。以下是计数信号量的常用函数及其说明: 1)xSemaphoreCreateCounting(unsignedportBASE_TYPE uxMaxCount, unsignedportBASE_T…...
wc命令使用指南 | 教你如何高效统计文件字数、行数和字符数
文章目录 wc命令使用指南1. 引言1.1 什么是wc命令?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攻击 原理:CSRF 的本质实际上是利用了 Cookie 会自动在请求中携带的特性,通过伪造请求来执行恶意操作。 1、目标网站信息: 接口地址:https://victim.com/change-password 请求类型:get/post 接…...
java上传文件到指定服务器
首先要知道服务器的用户名和密码。 注意:一般情况,如果不是强制要求,尽量不要将文件上传到服务器 步骤: 1.导入依赖 <!--图片上传到服务器需要的依赖--> <dependency> <groupId>com.jcr…...

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

【Spring Cloud】深入探索统一网关 Gateway 的搭建,断言工厂,过滤器工厂,全局过滤器以及跨域问题
文章目录 前言为什么需要网关以及网关的作用网关的技术实现 一、Gateway 网关的搭建1.1 创建 Gateway 模块1.2 引入依赖1.3 配置网关1.4 验证网关是否搭建成功1.5 微服务结构分析 二、Gateway 断言工厂2.1 Spring 提供的断言工厂2.2 示例:设置断言工厂 三、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目录,fsbackend目录有数据,checkpoint目录没数据 env.getCheckpointConfig().setCheckpointStorage(PropUtils.getValueStr(Constant.ENV_FLINK_CHECKPOINT_PATH)); env.setStateBackend(new FsStat…...

从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...

短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...

基于Java+MySQL实现(GUI)客户管理系统
客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息,对客户进行统一管理,可以把所有客户信息录入系统,进行维护和统计功能。可通过文件的方式保存相关录入数据,对…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...