基于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…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
