0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

PLC遇见IT:C#+S7Net+PLCSIM实现西门子PLC仿真通讯

智能制造之家 来源:CSDN-虚梦年华 2023-01-04 11:43 次阅读

PLCSIM Advanced 简介

PLCSIM Advanced是西门子推出的一款功能强大的仿真软件,目前最新发布的版本为4.0,但鉴于新版本可能存在未知的bug,故本文使用V3.0。

V3.0支持仿真1500PLC及ET 200SP,可实现Socket网络通讯功能,也可实现PLC之间、PLC与设备直接的ModbusTCP等通讯。

V3.0安装时需要先安装WinPcap_4_1_3,V4.0则不需要。

以下为两个版本的官网下载链接,下载时需要西门子账号,可以免费注册。

以下为V3.0下载链接:

PLCSIM Advanced V3.0

V3.0的两个升级包(可选安装)

以下为V4.0下载链接

PLCSIM Advanced V4.0

S7 Net Plus 简介

西门子PLC通讯库,支持200、200smart、300、400、1200、1500系列PLC。

说明文档

配置PLCSIM Advanced

打开PLCSIM Advanced V3.0,如下图:

d0bfc0ec-8660-11ed-bfe3-dac502259ad0.png

Online Access要选择右边的PLCSIM Virtual Eth.Adapter,左侧的PLCSIM不支持外部网络访问。

TCP/IP communication with 可选以太网或者是本地虚拟网卡。local即为本地虚拟网卡,是在安装PLCSIM Advanced时自动安装的网络适配器。打开控制面板-->网络和 Internet-->网络连接,Siemens PLCSIM Virtual Ethernet Adapter就是此虚拟网卡。使用虚拟网卡只能在本机进行通讯仿真,而使用以太网则可以在局域网内进行仿真通讯。

Start Virtual S7-1500 PLC为PLC设置,包括IP地址、子网掩码、默认网关及PLC型号。设置完成后点击Start按钮则会生成一个PLC实例。创建成功后就可以开始通讯仿真了。

Virtual SIMATIC Memory Ca为打开保存PLC历史记录的文件夹的按钮。

如下图所示,在Active PLC Instance(s)可以看到已成功创建的PLC。

d0d7b710-8660-11ed-bfe3-dac502259ad0.png

下载测试DB块

TIA Protal软件中,添加一个S7-1511的设备,然后在程序块中添加一个新的DB块,DB号设置为10。

打开设备的属性 --> 防护与安全 -->连接机制,勾选“允许来自远程对象的PUT/GET通讯访问”。

打开设备的属性 --> PROFINET 接口 [X1] -->以太网地址,按需设置PLC的IP地址。

打开DB10的属性,取消勾选“优化块的访问”,并在DB10中新建如下图所示的变量,编译完成后则可以得到每个变量的偏移量,即此变量在DB10上的地址。

d0ede5bc-8660-11ed-bfe3-dac502259ad0.png

设置完成后,下载到刚刚使用PLCSIM Advanced创建的仿真PLC中,需要注意网段要设置成与仿真PLC同一网段。

引用S7NetPlus

创建一个测试程序,此处创建的是一个控制台应用程序。

在NuGet下载S7NetPlus,如下图所示,版本可按需选择

d102489a-8660-11ed-bfe3-dac502259ad0.png

新建一个名为PLCInstance的类,创建PLC单例。

    class PLCInstance
    {
        private PLCInstance()
        {
            plcObj = new Plc(CpuType.S71500, "192.168.10.230", 0, 1);
        }


        /// 
        /// PLC单例
        /// 
        public static PLCInstance Instance
        {
            get
            {
                return Nested.instance;
            }
        }


        /// 
        /// 防止调用此类静态方法时,创建新的实例
        /// 
        private class Nested
        {
            internal static readonly PLCInstance instance = null;
            static Nested()
            {
                instance = new PLCInstance();
            }
        }


        /// 
        /// 私有PLC单例对象
        /// 
        private static Plc plcObj;


        /// 
        /// 连接至PLC并返回连接状态
        /// 
        /// 
        private bool ConnectToPLC()
        {
            try
            {
                plcObj.Open();
                return plcObj.IsConnected ? true : false;
            }
            catch (Exception)
            {


                return false;
            }


        }


        /// 
        /// 关闭连接
        /// 
        private void Disconnect()
        {
            plcObj.Close();
        }
    }

读写数据

S7NetPlus提供了多种读写的方式,可以读取字节自行解析或者按照指定格式写入字节,也可以指定地址进行读写,还可以使用变量、结构体或者类进行单个或者批量读写。

1、指定地址读写

这种方法可以在Read方法中以字符串形式传入需要读取的地址,返回的是Object类型的值,需要使用者自行做类型转换。Write方法则同理,以字符串的形式指定需要写入的地址,并在第二个参数传入需要写入的值,但是需要注意西门子PLC内的数据类型与C#的数据类型的对应。以下为读写DB10的0.0地址上的布尔量的值示例,此方式均支持读取与写入。

//读取
bool result = (bool)plc.Read("DB10.DBX0.0");


//写入
plc.Write("DB10.DBX0.0",!result);

虽然这种方式比较简单且方便,但是它是作者不推荐的方式,文档中原文如下:

This method reads a single variable from the plc, by parsing the string and returning the correct result. While this is the easiest method to get started, is very inefficient because the driver sends a TCP request for every variable.

意思就是,这种方法会通过解析传入的地址字符串来获取需要读写的地址,对于使用者来说是非常简单的使用方式,但是S7NetPlus会为每个通过这种方式读写的变量生成一个新的TCP请求,因此在读写多个变量时,执行效率会比较低。

S7NetPlus使用的通讯本质上是西门子的S7通讯,通过发送七层通讯报文来建立与西门子PLC的TCP连接,后续也是根据S7通讯的通讯协议生成并发送报文来实现PLC的数据读写。所以当使用这种方式读写多个变量的时候,S7NetPlus内部为每个变量重复建立新的S7连接与发送读写报文的操作,而不是单个连接成功建立后在这个连接上进行批量的读写。

简单理解就是这种方式效率比较低,会占用更多的资源。

2、解析读写

这种方法需要指定DB的类型、DB号、起始地址、PLC数据类型及读取数量。虽然它需要传入的参数变多了,但是当需要读取多个地址连续且类型相同的变量时,仅需修改最后的读取数量,S7NetPlus就会自动读取这一连串的地址,并按照指定的变量类型解析出对应的值,文档中后面说到的多类型变量批量读取也是基于这种方法的。不过这种方式读取PLC内的字符串类型时,仍存在bug,所以当需要读写字符串的时候,推荐使用本文后面提及的字节读写的方式。

示例如下:

//读取
bool result = (bool)plc.Read(DataType.DataBlock, 10, 0, VarType.Bit, 1);


//写入
plc.Write(DataType.DataBlock, 10, 0, true);

Read:

第一个参数是DB的数据类型,可以是DB、定时器、计数器、Merker(内存)、输入、输出。

第二个参数是DB号。

第三个参数是起始地址。

第四个参数是PLC内该变量的类型。

第五个参数是需要读取的个数。

Write:

第一个参数是DB的数据类型,可以是DB、定时器、计数器、Merker(内存)、输入、输出。

第二个参数是DB号。

第三个参数是起始地址。

第四个参数是需要写入的值。

3、字节读写

这种方法将会读取指定DB块上一段连续的地址上的字节,不做任何解析直接以字节数组的形式返回。

第一个参数是DB的数据类型,可以是DB、定时器、计数器、Merker(内存)、输入、输出。

第二个参数是DB号。

第三个参数是起始地址。

第四个参数是读取的字节数。

要使用这种方式读写数据,则需要非常熟悉PLC内各类型数据存储的格式,可以自行将读取上来的字节进行解析以获得所需数据。

虽然这种方式理论上能读写任意的数据,但是解析数据的过程会比较麻烦,所以若非万不得已,个人建议尽量少用。

此处仅提供PLC内String类型及WString类型的读取示例。

//String读取
byte[] data = plc.ReadBytes(DataType.DataBlock, 10, 2, 254);
string result = Encoding.Default.GetString(data);
//Wstring读取
byte[] data = plc.ReadBytes(DataType.DataBlock, 10, 4, 508);
string result = Encoding.BigEndianUnicode.GetString(data);

在S7-1500中,一个String类型的变量占用256个字节,但是第一个字节是总字符数,第二个字节是当前字符数,所以真正的字符数据是从第三个字节开始的,共254个字节。

同理,WString类型其实就是双字节的Sring,也就是说一个字符占用两个字节,所以一个WString类型的变量占用512个字节,第一、二个字节是总字符数,第三、四个字节是当前字符数,真正的字符数据是从第五个字节开始的,共508个字节。

按照以上示例的方法,读取上来的字符串后面会带很多个"�"的字符,那是因为后面的空字节也读取上来了,正式使用时可以考虑使用.Replace("�", "")来去除,或者解析第二个字节来获取字符长度进而转码。

当写入字符串时,则需要根据不同的数据类型来生成对应字符串的字节数组,然后将该数组写入到指定地址中即可。

需要注意的是,String类型的编码格式对应的是ASCII,而WString的则是C#中的BigEndianUnicode格式。在WString中,由于总长度与当前字符数是都是双字节数,所以在转换成字节数组的时候存在高低字节顺序问题。在这里就有一个大坑:这两个变量在C#中转换出来的字节数组跟PLC中存储的,高低字节是反过来的。这也就是为什么下面的WString的示例中需要对总字符数和当前字符数的两个字节数组进行反转。

此处提供一种生成String类型和WString的字节数组的方法,可供参考:

        /// 
        /// 获取西门子PLC字符串数组--String
        /// 
        /// 
        /// 
        private byte[] GetPLCStringByteArray(string str)
        {
            byte[] value = Encoding.Default.GetBytes(str);
            byte[] head = new byte[2];
            head[0] = Convert.ToByte(254);
            head[1] = Convert.ToByte(str.Length);
            value = head.Concat(value).ToArray();
            return value;
        }


        /// 
        /// 获取西门子PLC字符串数组--WString
        /// 
        /// 
        /// 
        private byte[] GetPLCWStringByteArray(string str)
        {
            byte[] value = Encoding.BigEndianUnicode.GetBytes(str);
            byte[] head = BitConverter.GetBytes((short)508);
            byte[] length = BitConverter.GetBytes((short)str.Length);
            Array.Reverse(head);
            Array.Reverse(length);
            head = head.Concat(length).ToArray();
            value = head.Concat(value).ToArray();
            return value;
        }

使用示例如下:

//写入String 
string str = "Example";
plc.Write(DataType.DataBlock, 10, 0, GetPLCStringByteArray(str));
//写入WString
string str = "示例";
plc.Write(DataType.DataBlock, 10, 0, GetPLCWStringByteArray(str));

4、旧版本的字节读取注意事项

旧版本的单次字节读取是有字节数限制的,每一次读取的最大字节数为200,如果需要读写更多的字节,则需要多次读写并进行拼接,以下提供两种方法,可供参考:

        /// 
        /// 循环读取
        /// 
        /// 要读取的字节数
        /// DB号
        /// 起始地址
        /// 
        private byte[] CyclicReadMultipleBytes(int numBytes, int db, int startByteAdr = 0)
        {
            byte[] resultBytes = new byte[0];
            int index = startByteAdr;
            while (numBytes > 0)
            {
                var maxToRead = Math.Min(numBytes, 200);
                byte[] bytes = plc.ReadBytes(DataType.DataBlock, db, index, maxToRead);
                if (bytes == null)
                    return null;
                resultBytes = resultBytes.Concat(bytes).ToArray();
                numBytes -= maxToRead;
                index += maxToRead;
            }
            return resultBytes;
        }


        /// 
        /// 递归读取
        /// 
        /// 要读取的字节数
        /// DB号
        /// 起始地址
        /// 
        public static byte[] RecursiveReadMultipleBytes(int numBytes, int db, int startByteAdr = 0)
        {
            byte[] result = new byte[0];
            if (numBytes > 200)
            {
                byte[] temp = plc.ReadBytes(DataType.DataBlock, db, startByteAdr, 200);
                numBytes -= 200;
                result = temp.Concat(RecursiveReadMultipleBytes(numBytes, db, startByteAdr + 200)).ToArray();
            }
            else
            {
                byte[] temp = plc.ReadBytes(DataType.DataBlock, db, startByteAdr, numBytes);
                result = result.Concat(temp).ToArray();
                return result;
            }


            return result;
        }

在读取一两千个字节的情况下,这两种方法速度都差不多,递归会稍微快一点点。不过新版本没有单次读取限制,所以正常情况下是不需要这两个方法的。

5、其余读取方式

其它的读取方式可参考文档,本文不再赘述。

读取数据示例

PLCInstance:

using S7.Net;
using System;
using System.Text;


namespace S7NetPlusExample
{
    class PLCInstance
    {
        private PLCInstance()
        {
            plcObj = new Plc(CpuType.S71500, "192.168.10.230", 0, 1);
        }


        /// 
        /// PLC单例
        /// 
        public static PLCInstance Instance
        {
            get
            {
                return Nested.instance;
            }
        }


        /// 
        /// 防止调用此类静态方法时,创建新的实例
        /// 
        private class Nested
        {
            internal static readonly PLCInstance instance = null;
            static Nested()
            {
                instance = new PLCInstance();
            }
        }


        /// 
        /// 私有PLC单例对象
        /// 
        private static Plc plcObj;


        /// 
        /// 连接至PLC并返回连接状态
        /// 
        /// 
        private bool ConnectToPLC()
        {
            try
            {
                plcObj.Open();
                return plcObj.IsConnected ? true : false;
            }
            catch (Exception)
            {


                return false;
            }


        }


        /// 
        /// 关闭连接
        /// 
        private void Disconnect()
        {
            plcObj.Close();
        }


        /// 
        /// 读取示例数据
        /// 
        /// 
        public string GetPLCInfo()
        {
            if (ConnectToPLC())
            {
                StringBuilder sbr = new StringBuilder();


                //读取BOOL值
                bool boolResult = (bool)plcObj.Read(DataType.DataBlock, 10, 0, VarType.Bit, 1);


                //读取Int值
                int intResult = (short)plcObj.Read(DataType.DataBlock, 10, 2, VarType.Int, 1);


                //读取Real值
                float realResult = (float)plcObj.Read(DataType.DataBlock, 10, 4, VarType.Real, 1);


                //读取String值
                byte[] stringData = plcObj.ReadBytes(DataType.DataBlock, 10, 10, 254);
                string stringResult = Encoding.Default.GetString(stringData);


                //读取WString
                byte[] wstringData = plcObj.ReadBytes(DataType.DataBlock, 10, 268, 508);
                string wstringResult = Encoding.BigEndianUnicode.GetString(wstringData);


                Disconnect();


                sbr.AppendLine($"{boolResult}");
                sbr.AppendLine($"{intResult}");
                sbr.AppendLine($"{realResult}");
                sbr.AppendLine($"{stringResult}");
                sbr.AppendLine($"{wstringResult}");


                return sbr.ToString();                
            }
            else
            {
                return "连接PLC失败";
            }           
        }
    }
}

主程序:

using System;


namespace S7NetPlusExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(PLCInstance.Instance.GetPLCInfo());


            Console.ReadKey();
        }
    }
}

运行结果:

d11a93b4-8660-11ed-bfe3-dac502259ad0.png

结尾

本文简单介绍了S7 Net Plus和PLCSIM Advanced的使用,以上内容均由本人亲自实践得出的结果,但仍有可改进的的地方。S7NetPlus的文档也有非常详细的介绍,如有更复杂的读写需求,可以参考文档。

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • plc
    plc
    +关注

    关注

    4975

    文章

    12255

    浏览量

    454921
  • 西门子
    +关注

    关注

    92

    文章

    2850

    浏览量

    113778
  • 仿真
    +关注

    关注

    50

    文章

    3872

    浏览量

    132163
  • Advanced
    +关注

    关注

    0

    文章

    30

    浏览量

    23160

原文标题:PLC遇见IT:C#+S7Net+PLCSIM实现西门子PLC仿真通讯

文章出处:【微信号:智能制造之家,微信公众号:智能制造之家】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Labview利用S7.NET.DLL与西门子PLC模拟通讯

    ,并模拟通信成功,网上关于用C#调用S7.NET.DLL进行通信的例子很多,用Labview来调用的很少,故决定写这篇文章,把自己这几天摸索的经验分享给大家。要模拟西门子PLC
    发表于 03-03 21:43

    西门子S7-PLCSIM仿真软件的应用

    本文主要介绍了西门子S7-PLCSIM仿真软件的应用。
    发表于 04-11 14:44 105次下载

    西门子plc数据类型_西门子plc通讯方式

    西门子PLC的品质非常好,其网络稳定性、开放性深受工控人员的喜爱,而且编程软件分类多,使用方便。在国内工控界具有显著地位。虽然大家对西门子PLC都有一定的了解,但要你说出
    发表于 07-25 10:57 1.3w次阅读
    <b class='flag-5'>西门子</b><b class='flag-5'>plc</b>数据类型_<b class='flag-5'>西门子</b><b class='flag-5'>plc</b><b class='flag-5'>通讯</b>方式

    西门子变频器与plc通讯

    西门子变频器与plc通讯资料免费下载。
    发表于 04-22 11:28 90次下载

    西门子PLC和组态王通讯设置

    西门子PLC和组态王通讯设置说明。
    发表于 05-06 16:35 42次下载

    西门子PLC实现两灯交替闪烁的两种编程法

    今天给大家说一个西门子案例——西门子S7-200SMART PLC怎么实现两灯交替闪烁。
    发表于 02-19 10:52 1.3w次阅读

    西门子PLC工作原理及仿真

    西门子(SIEMENS)公司生产的可编程序控制器在我国的应用也相当广泛,在冶金、化工、印刷生产线等领域都有应用。西门子(SIEMENS)公司的PLC产品包括 LOGO、S7-200、S7-1200
    发表于 04-17 15:12 1次下载
    <b class='flag-5'>西门子</b><b class='flag-5'>PLC</b>工作原理及<b class='flag-5'>仿真</b>

    三分钟轻松实现连接西门子PLC

    西门子PLC广泛应于工业控制领域,是一款性能高、稳定性强的PLC设备。此文档将介绍如何使用EG系列网关通过网口连接西门子PLC,并添加到EM
    发表于 04-18 11:28 5次下载
    三分钟轻松<b class='flag-5'>实现</b>连接<b class='flag-5'>西门子</b><b class='flag-5'>PLC</b>

    Unity3D与西门子PLC通讯

    本文是做实体PLC和Unity的通讯,如果没有实体PLC可以参考我之前的博文上位机与西门子博途TIA的PROFINET通讯
    发表于 04-19 11:43 0次下载
    Unity3D与<b class='flag-5'>西门子</b><b class='flag-5'>PLC</b><b class='flag-5'>通讯</b>

    西门子PLC通讯知识汇总

    西门子 200 plc 使用 MPI 协议与组态王进行通讯时需要哪些设置?
    的头像 发表于 05-06 10:35 1213次阅读
    <b class='flag-5'>西门子</b><b class='flag-5'>PLC</b><b class='flag-5'>通讯</b>知识汇总

    西门子软件PLC与组态软件通讯实例

    【导读】分享西门子虚拟PLC与组态软件iFIX之间TCP/IP仿真通讯
    的头像 发表于 06-19 11:00 1194次阅读
    <b class='flag-5'>西门子</b>软件<b class='flag-5'>PLC</b>与组态软件<b class='flag-5'>通讯</b>实例

    西门子1200PLC之间的ModbusTCP通讯如何实现

    西门子1200PLC通过以太网通讯与其它设备进行数据交互,西门子1200PLC可以和西门子系列
    发表于 07-30 14:49 7296次阅读
    <b class='flag-5'>西门子</b>1200<b class='flag-5'>PLC</b>之间的ModbusTCP<b class='flag-5'>通讯</b>如何<b class='flag-5'>实现</b>?

    天拓分享:西门子plc S7-300/400系列的PLC通讯

    今天看一下西门子plc S7-300/400系列的PLC通讯西门子plc S7-400与s7-
    的头像 发表于 08-03 16:35 2341次阅读
    天拓分享:<b class='flag-5'>西门子</b><b class='flag-5'>plc</b> S7-300/400系列的<b class='flag-5'>PLC</b><b class='flag-5'>通讯</b>

    西门子1200PLC西门子1500PLC的ModbusRTU通讯

    在现代化高度集成工业中,经常用到PLCPLC之间的通讯,常见的通讯modbus,profibus DP,profinet ,CANOPEN等等通讯
    的头像 发表于 08-23 09:15 2513次阅读
    <b class='flag-5'>西门子</b>1200<b class='flag-5'>PLC</b>和<b class='flag-5'>西门子</b>1500<b class='flag-5'>PLC</b>的ModbusRTU<b class='flag-5'>通讯</b>

    仿真神器—S7-PLCSIM Advanced知识详解

    S7-PLCSIM Advanced是西门子公司推出的一款PLC仿真软件,其中“PLCSIM”是“PLC
    的头像 发表于 12-18 09:33 3752次阅读
    <b class='flag-5'>仿真</b>神器—S7-<b class='flag-5'>PLCSIM</b> Advanced知识详解