最近在做一个数据采集项目,涉及到许多与西门子S7系列PLC的通信,由于自己的VC水平属于半瓶子晃荡,所以就想利用C#来进行开发(个人比较喜欢C#的代码风格,看着很清爽),虽然C#这种高级语言与底层的设备通讯效率确实不如C++,但好在数据量不大,实时性要求不算太高,用C#还是可以应付的。在界面开发方面,高级语言确实不如WinCC,Intouch之类的组态软件,但在数据处理上面,却有很大的灵活性。
      在这里很感谢吴向阳,在中国工控网上面发现了他的文章,因为我是从C#转到工控方向的,以前对PLC一点都不懂,刚接触这一行时,学习起来很吃力,看了他的这篇文章,让我对PLC的有了更加深入的了解。我的这个DCProdave.cs就是在他的基础上修改的,加了一些自己的东西。还望各位多多提意见,多多交流!
      原文章地址:http://www.gongkong.com/webpage/paper/200507/8-A01D-6654393A02CC.htm

Prodave版本: PRODAVE6.0 - W95_S7.DLL
PLC模拟环境: PLCSIM V5.4
开发环境: VS.NET 2005
附件1:Prodave6.0手册[English] ,下载地址:https://files.cnblogs.com/J0YANG/Prodave.pdf
附件2:Prodave6.0动态库[W95_S7.DLL],下载地址: https://files.cnblogs.com/J0YANG/W95_S7.rar
附件3:DCProdave.cs[C#封装的源代码],下载地址: https://files.cnblogs.com/J0YANG/DCProdave.rar

一.从w95_s7.dll中导入PLC通讯函数的方法[DllImport]
在使用DllImport之前,必须引入InteropServices, 代码如下: using System.Runtime.InteropServices;
具体使用方法可以参考我的博客中转载的一篇文章 《C#(.net)中的DllImport用法[转] 》写的很不错,千万要注意C++数据类型到C#的对应关系,选用合适的类型。比如 char* 可以用string来转换,指针类型可以ref  或者数组。
原文地址:http://www.cnblogs.com/xumingming/archive/2008/10/10/1308248.html

二.定义结构体类型
2.1 PLC连接参数结构体
1浅谈西门子Prodave6.0在C#中的应用//定义结构体[连接PLC所需参数]
2浅谈西门子Prodave6.0在C#中的应用public struct PLCConnParam 
3}

2.2 PLC存储区域类别编号
 1浅谈西门子Prodave6.0在C#中的应用//定义枚举类型[PLC的存储区域编号]
 2浅谈西门子Prodave6.0在C#中的应用public enum PLCBlockType
 3}

三.常用函数详细讲解
3.1 建立PLC连接函数
     首先从W95_S7.DLL中导入连接函数,访问权限为私有,C#将会对此函数进行封装,供外部调用,稍后讲解.
1[,] adr_table);
说明:
      在一个MPI/DP网络中若有多个PLC时,可指定多个连接列。最后一列的所有参数须置0,以标志参数列结束。例如一个MPI/DP网中有两个PLC,他们的MPI地址分别为2和3,槽号均为2,机架号均为0,则可按如下方式调用:byte[,] ba={{2,0,2,0},{3,0,2,0},{0,0,0,0}};  int err=load_tool(1, "s7online",ba); 返回值为int型,如果返回0则表示执行成功,非零值,则需要根据错误号查找到错误具体信息,具体参照本文第五部分:错误代码字典
      当然如果PLC使用的是DP网络时,只需要将Set PG/PC Interface中接口参数分配选为PLCSIM(PROFIBUS)即可,Prodave不需要做任何修改(当然PLC地址肯定也是DP口的地址哦),具体如下图:
浅谈西门子Prodave6.0在C#中的应用
个人不太习惯西门子的这种函数命名,索性就按照C#的常用习惯,进行一下简单的封装,供外部调用.
 1}
    建立于PLC的连接,只需在数采程序启动的时候调用即可,并且只能打开一次,否则报错. 驱动设备名称"S7ONLINE",一般情况下是不会有变化的,所以这里就写死了.特别需要指出的是,这个函数的第一个参数(连接号),是指当前连接有多少个PLC连接(严格意义上来讲,是CPU的个数,因为有可能2个PLC共用1个CPU,之间通过IM467组态),激活连接并交换数据的时候,和这个值有点关系. 在建立连接的时候默认激活第1个连接.

3.2 断开与PLC的连接
从W95_S7.DLL中导入函数,依然是私有,因为我要对所有的导入函数进行封装.
1 unload_tool();
关闭PLC的连接函数进行C#封装,没有改变任何代码,只是换了个函数名.
1浅谈西门子Prodave6.0在C#中的应用public static int Close()
2}

3.3 激活连接,当前连接列中某个时刻有且只有1个PLC是激活状态.建立连接的时候,默认激活第1个连接.
1 no);
      其参数与load_tool中参数adr_table所传递的连接参数顺序对应譬如byte[,] btr={{2,0,2,0},{3,0,2,0},{0,0,0,0}} , new_ss(1)则激活第1个连接即与MPI地址为2的PLC通讯,类似的new_ss(2)则激活与MPI地址为3的PLC通讯,在数采系统中,为了读取所有PLC的数据,采用定时循环激活每个PLC的连接,然后读取其数据.
C#封装如下:
1浅谈西门子Prodave6.0在C#中的应用public static int ActiveConn(int connNO)
2}

3.4 从DB块中读取字节数据(返回BYTE数组)
1[] buffer);
C#封装如下:
 1}
      这个函数是用的最多的一个函数,在数采系统中,习惯一次性的将所有需要用到数据,全部读到字节数组中,统一处理,避免不同时期凌乱读取造成的数据不一致.需要提醒的是,必须保证数据处理函数得到的数据,是PLC一次扫描周期内的.

3.5 从DB中读取整数值(int32型)
1 buffer);
C#封装
 1}
这个函数读取的是一个整数,因为DB中有DBB,DBW,DBD3种数据类型,最大的DBD是4个字节,所以设计了这个函数,读取单个的整型值,不用再进行BYTE[]到INT的转换了.这里值得一提的是整数高位优先,还是低位优先的问题,字节数组的顺序切记要矫正,另外buffer = bbuf[0* 0x1000000 + bbuf[1* 0x10000 + bbuf[2* 0x100 + bbuf[3];这行代码很有意思,16进制的字节进位是0x100.

3.6 M,I,Q 3种块的读取函数类似(参数都是一样的),这里放在一起进行说明
M区读取函数
1[] buffer);
I区读取函数,一直不明白为什么输入区(单词input)简称为I,而函数名却为A, 后来才晓得,这个A是德文的表示方法,(Pordave是西门子公司的东东).
1[] buffer);
Q区读取函数,参数与I一样.
1浅谈西门子Prodave6.0在C#中的应用[DllImport("w95_s7.dll")]
2浅谈西门子Prodave6.0在C#中的应用private extern static int e_field_read(int no, int anzahl, byte[] buffer);

C#封装,  M,I,Q 3种块的读取函数类似,这里放在一个函数里,利用枚举类型PLCBlockType进行区分
 1}

四, BYTE,INT,BOOL几种类型的数据转换函数
4.1 从32位整数中提取字节数组(4个byte)
 1}

4.2 从字节数据中提取bit数组(8个bit),以bool型数据代替位表示.
 1}

4.3 从字节数据中提取某一位的状态,以bool型返回
 1}


五.错误代码字典
 1}
      由于错误代码比较多,这里只罗列了几个,详细信息请下载源代码DCProdave.cs进行查看,这里不再详述.

六. DCProdave.cs应用举例
      为保证数据的一致性,可以使用一个定时器,触发时间设为PLC扫描周期,在其触发事件中,把需要用到的PLC变量一次性读取.建立与PLC的连接,示例如下
1浅谈西门子Prodave6.0在C#中的应用PLCConnParam[] Conn=new PLCConnParam[2]; //MPI网中有2个PLC,地址分别为2,3
2浅谈西门子Prodave6.0在C#中的应用Conn[0] .Addres=2;  Conn[0].Slot=2;  Conn[0].Rack=0;  
3浅谈西门子Prodave6.0在C#中的应用Conn[1] .Addres=3;  Conn[1].Slot=2;  Conn[1].Rack=0;
4浅谈西门子Prodave6.0在C#中的应用errCode= DCProdave.Open(1,Conn);  //建立连接
5浅谈西门子Prodave6.0在C#中的应用errCode= DCProdave.ActiveConn(1); //激活第一个连接
6浅谈西门子Prodave6.0在C#中的应用errCode= DCProdave.GetDBByteData(206, buf, 0); //DB2.DBW0--DBW5 共6个字节的变量,从buf的0位存储
7如果返回值不=0,则将错误写入日志

还有很多往PLC写入数据的函数,这里没有介绍,文中有很多不足之处,欢迎希望路过的各位XDJM在此留言.
邮箱: J0YANG@163.com

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-06-04
  • 2021-07-09
  • 2022-12-23
  • 2022-12-23
  • 2021-04-25
猜你喜欢
  • 2021-08-25
  • 2021-11-21
  • 2021-06-25
  • 2022-12-23
  • 2022-12-23
  • 2021-10-08
  • 2022-02-16
相关资源
相似解决方案