C#与PLC通讯的实现代码
作者:宥小稚 发布时间:2021-10-29 13:34:39
最近因为工作的原因用到了西门子PLC,在使用过程中一直在思考上位机和PLC的通讯问题,后来上网查了一下,找到了一个专门针对S7开发的一个.net库–《S7netPlus》,PLC通讯方法比较多,所以也是在不断地学习中,以下内容如有不足之处,望大神予以指教。
公司设备一直都用的PLC做下端设备的控制,但是目前都没有专职做上位机的,而我之前对PLC又接触的比较少,做起来还是比较难的。。
查找了一堆资料后,终于找到了这个.net库,在大致学习了一下之后,总结了一下,当作自己的学习笔记。
一、开发环境准备
最近因为疫情的影响,只能呆在总公司混日子,手里没有设备,只能用博图的仿真器来测试通讯,需要安装的软件包括:
Visual Studio 2015
TIA Portal V15
S7-PLCSIMV15
NetToPLCSIM-S7
这里先放个下载连接:S7Net.dll、NetToPLCSIM、S7Net使用手册
TIA Protal(博图)&S7-PLCSIM
西门子针对于PLC专门开发的一款编程软件,相信各位肯定比我熟悉这个软件了,这里就不作过多介绍了,同时提供了S7系列的仿真软件S7-PLCSIM,这里我们就用这两个设备仿真PLC设备来测试S7NETPlus库的通讯。
博图V15.1
S7-PLCSIM
NetToPLCSIM
这个软件是用于将西门子的PLCsim映射到网络内,如果之前没有用过这个软件,建议按照后面的操作来,否则很容易出现Start server之后还是连不上仿真器。
二、开发测试
PLC配置
1、在组态好的PLC设备属性中,找到“防护与安全”–>“连接机制”中,勾选“允许来自远程对象的PUT/GET通信访问”;
2、新建DB块,同时将该DB快属性中的“优化块的访问取消”;
3、在新建的DB块中新增一些数据,完成后点击编译计算偏移量。
4、以上步骤完成后,点击开始仿真,将工程下载到仿真器中;
NetToPLCSIM配置
1、打开软件后,点击Add增加设备
2、在弹出的窗口中,Network IP Address中填入本地回环IP“127.0.0.1”(如果你是在两台设备中测试,首先保证两台设备在同一个内网中,该处IP就可以设置为运行仿真环境的IP了)
3、Plcsim IP Address中,点击后面两个点,选择软件自己识别出来的仿真器地址;
4、Plcsim Rack/Slot中Rack为机架号,Slot为插槽号,这两个可以在PLC的设备组态属性->项目信息中找到
配置完成后点击完成,这时候就可以点击Start Server开启服务了。
上面的操作一定要按照以上的步骤一步一步完成,否则很容易出现即使点Start Server显示状态为running,但是实际连接仍然连不上的情况。
另外需要注意的是,可能在打开NetToPLCSim的时候,会弹出“Port 102 is in use!”的警告,如果遇到这个情况,点击是,之后在将PLCSIM关掉重新启动一下就可以了。
创建连接
配置连接
这里使用的是S7-1215的模块,所以CpuType选择S71200,IP地址使用回环地址“127.0.0.1”,机架号和插槽号在PLC工程中查。
using S7.Net;
Plc plc = new Plc(CpuType.S71200, "127.0.0.1", 0, 1);
配置完成后,使用Open()来打开,在最早的一个版本中,Open有返回值,可以通过返回值获取 ErrorCode 和 ErrorMessage,我目前使用的是最新版0.8.1.0,没有返回值,所以用try…catch来接收异常
try
{
plc.Open();
}
catch(Exception)
{
Console.WriteLine($"连接到PLC设备失败:IsConnect = {plc.IsConnected},IsAvailable={plc.IsAvailable}");
return;
}
连接是否成功,可以用IsConnected去判断一下。
访问数据块
连接成功后,我们就可以去访问PLC的数据块了,访问数据块,我们先尝试一下读取数据块
读取单个数据–Read
这里主要用到了DBX,DBW,DBD读取数据,其他的各位可以在查一下PLC的资料
/*
方法:public object Read(string variable)
入参:读取数据地址
出参:Object类型数据,可强制类型转换
*/
var db1Bool1 = plc.Read("DB1.DBX0.0");
Console.WriteLine("DB1.DBX0.0:" + db1Bool1);
bool db1Bool2 = (bool)plc.Read("DB1.DBX0.1");
Console.WriteLine("DB1.DBX0.1:" + db1Bool2);
int IntVariable = (ushort)plc.Read("DB1.DBW2.0");
Console.WriteLine("DB1.DBW2.0:" + IntVariable);
float RealVariable = ((uint)plc.Read("DB1.DBD4.0")).ConvertToFloat();
Console.WriteLine("DB1.DBD4.0:" + RealVariable);
var dIntVariable = (uint)plc.Read("DB1.DBD8.0");
Console.WriteLine("DB1.DBD8.0: " + dIntVariable);
var dWordVariable = (uint)plc.Read("DB1.DBD12.0");
Console.WriteLine("DB1.DBD12.0: " + Convert.ToString(dWordVariable, 16));
var wordVariable = (ushort)plc.Read("DB1.DBW16.0");
Console.WriteLine("DB1.DBW16.0: " + Convert.ToString(wordVariable,16));
读取批量数据块–ReadBytes
/*
方法:public byte[] ReadBytes(DataType dataType, int db, int startByteAdr, int count)
入参:
1、DataType数据类型,可选择从DB块或者Memory中读取;
2、db:1:DataBlock=1,Memory=0;
3、startByteAdr:起始地址,即DB块的起始偏移量;
4、count:读取大小,该大小由读取的DB块的最后一个数据的偏移量和大小决定,这里最后一个字节WordVariable偏移量为16,数据类型为word,2个字节,因此此次读取为16+2=18个字节。
出参:Byte[],这里Byte[]的大小必然和count的大小是相同的,
*/
//读取数据选择从DB块中读取,db设置为1,起始地址为0,读取18个字节
var bytes = plc.ReadBytes(DataType.DataBlock, 1, 0, 18);
//取字节0中的第0位
var db1Bool1 = bytes[0].SelectBit(0);
Console.WriteLine("DB1.DBX0.0:" + db1Bool1);
//取字节0中的第1位
bool db1Bool2 = bytes[0].SelectBit(1); ;
Console.WriteLine("DB1.DBX0.1:" + db1Bool2);
//跳到字节2并连续取两个字节数据
int IntVariable = S7.Net.Types.Int.FromByteArray(bytes.Skip(2).Take(2).ToArray());
Console.WriteLine("DB1.DBW2.0:" + IntVariable);
//...
double RealVariable = S7.Net.Types.Real.FromByteArray(bytes.Skip(4).Take(4).ToArray());
Console.WriteLine("DB1.DBD4.0:" + RealVariable);
//...
int dIntVariable = S7.Net.Types.DInt.FromByteArray(bytes.Skip(8).Take(4).ToArray());
Console.WriteLine("DB1.DBD8.0: " + dIntVariable);
//...
uint dWordVariable = S7.Net.Types.DWord.FromByteArray(bytes.Skip(12).Take(4).ToArray());
Console.WriteLine("DB1.DBD12.0: " + Convert.ToString(dWordVariable, 16));
//...
ushort wordVariable = S7.Net.Types.Word.FromByteArray(bytes.Skip(16).Take(2).ToArray());
Console.WriteLine("DB1.DBW16.0: " + Convert.ToString(wordVariable, 16));
写入单个数据–Write
/*
方法:public void Write(string variable, object value)
入参:
1、string variable:写入地址
2、object value,写入数据
*/
plc.Write("DB1.DBX0.0", true);
plc.Write("DB1.DBD12.0", 123457);
写入多个数据–WriteBytes
/*
public void WriteBytes(DataType dataType, int db, int startByteAdr, byte[] value)
用法如同ReadBytes,这里就不在写例程了,有兴趣的可以自己研究一下
*/
关闭连接
在通讯完之后,千万不要忘了关闭通讯链路哈,这里使用Close来关闭。
plc.Close();
来源:https://blog.csdn.net/guanxiaozhi/article/details/113546156


猜你喜欢
- 前言回调的核心就是回调方将本身即this传递给调用方,这样调用方就可以在调用完毕之后告诉回调方它想要知道的信息。1、什么是回调软件模块之间总
- 现在很多第三方Launcher((如360Launcher,GoLauncher)带有iphone主题,相信玩Android的人大都知道。本
- 目录前言Lottie案例尝试1. 集成依赖2. 添加 LottieAnimationView 加载网络资源3. 加载本地资源4. 循环播放
- 一、SpringBoot中的main方法注入service在springboot中使用main方法常规无法注入service,因为以后也可能
- 方法一class Program { [STAThread] static
- 本文介绍C# lock关键字,C#提供了一个关键字lock,它可以把一段代码定义为互斥段(critical section),互斥段在一个时
- 抛出问题:Long a = 4l;Long b = 4l;a == b //trueLong a = 128l;Long b = 128l;
- 一段看似平平无奇的代码Classroom classroom = this.getOne( Wrappers.<Class
- java.util.concurrent.ExecutionException错误信息,这里给出解决方案,大家根据具体要求更改。SEVERE
- 在intellij中忽略提交文件,分两种情况,文件没有纳入版本管理第一种方法文件还没有纳入版本管理,这种通过 svn的ignore配置ver
- 枚举是 C# 中最有意思的一部分,大部分开发人员只了解其中的一小部分,甚至网上绝大多数的教程也只讲解了枚举的一部分。那么,我将通过这篇文章向
- 前端向后端传递参数,后端怎么去接收,就会想到 spring 的注解之前的话,我一直用的是 RequestParam("userNa
- 一、logback日志技术介绍Spring Boot中使用的日志技术为logback。其与Log4J都出自同一人,性能要优于Log4J,是L
- 一般在web应用中,对客户端提交上来的图片肯定需要进行压缩的。尤其是比较大的图片,如果不经过压缩会导致页面变的很大,打开速度比较慢,影响用户
- 过滤器是Servlet的规范,是基于函数回调的,需要实现javax.servlet.Filter接口,依赖于Tomcat等容器,一般用于过滤
- BufferedInputStream BufferedInputStream 是缓冲输入流。它继承于FilterInputSt
- 文件写入为提供相对较高性能的文件读写操作,这里果断选择了 NIO 对文件的操作,因为业务背景需要数据的安全落盘。这里主要采用 ByteBuf
- 在使用ComboBox控件时,遇到了重新绑定赋值出问题的情况。正常情况下,对于数据重新赋值的或者绑定数据源的时候,为了防止数据出现问题,都会
- 前言上一篇文章中我们通过自己开发了一个负载均衡组件,实现了随机算法的负载均衡功能,如果要实现其他算法,还需要修改代码增加相应的功能。这一篇文
- using System;using System.Collections.Generic;using System.Linq;using