运用 C#编程通过 OPC 方式实现 PC 机与西门子 PLC 通讯-同步通讯篇[转]
1、 OPC 服务介绍
西门子提供的最新软件:Simatic Net PC-Software CD 2005 为各种组态软件的开发提
供了一个统一的平台,它建立的 PC 站既为一些组态软件,如:WinCC、Protol 等提供了与 PLC
的通讯平台,也提供了一套编程接口,可使用高级语言编程通过 Simatic Net 访问 PLC 数据。
本文讨论的主要就是这个编程接口,最新版的 Simatic NET 支持五种编程方式:
<1>、ActiveX 控件
提供了一系列数据访问控件,以便于向 VB6 这种语言使用控件的方式与 PLC 通讯。
<2>、OPC 自动化
为 VB6、Dephi 等语言运用 OLE 自动化的方式进行编程。
<3>、OPC 用户接口
这是专门为 VC++提供的一种高效编程方式,其灵活程度与执行效率比前面的两种方式均
要高得多。
<4>、针对微软的.NET 平台的 OPC 用户接口
这也是一种非常灵活的编程接口,不过它针对的是.NET 平台,其提供了大量的.NET 类库,
以便于像 VC#、VB.NET 等高级语言编程。本文将详细的介绍该接口。
<5>、OPL XML 接口
顾名思义,主要是针对 XML 编程的。
对于<2>、<3>、<4>编程方式,他们各自又可以分为同步访问方式和异步访问方式。按
西门子的文档解释:同步通讯指的是当一个客户在访问服务器时,其他客户的访问必须等待,直
到服务器处理完该客户的请求,才能继续进行下一个服务,异步访问与之正好相反,本文主要讲
的是同步编程篇,异步篇以后再提供。
2、 配置 OPC 服务器
要进行编程,必须先配置服务器。本文以 Prfibus DP 网络为例,介绍 PC 站的配置。其内
容主要来自西门子文档。
需要的软件:
Step7 V5.3
Simatic Net PC-Software CD 2005
需要的硬件:
至少为 CP5611 或以上级别,笔记本可以为 CP5511,带 DP 口的 S7-300 PLC(若使用
Simatic NET 的仿真功能可以不需要这些硬件,后面会介绍到)
<1>、组态一个 S7 站,配置 Profibus DP 网络,其 DP 地址设为 3,并下载到 PLC,然后
把网线由 MPI 口转到 DP 口。S7 站的配置这里就不介绍了。
<2>、在 Step7 V5.3 中建立一个新工程,插入一个 PC 站,并把该 PC 站的名字改成与
你的计算机名字相同。打开该 PC 站的硬件组态界面。插入 OPC 服务器和连接卡 CP5611(或者
CP5511),他们在 PC 槽中处的位置可以任意,如下图:
注:在插入 CP5611 时,应该选择与组态 S7 站一样的 Profibus 网络,并将网络地址设为
2,一定不要与 PLC 的地址冲突。
然后点击下面工具条标为红色的按钮:
选中”OPC Server”,然后插入一个新的连接,如下图:
在弹出的对话框中选择连接类型为 S7 Connection,如下图:
在 OK 后,然后在新对话框的红色标志位置输入 3,表示 PLC 的地址,如下图:
并选择 Address Details…,设置 CPU 的槽号为 2,如下图:
OK 后,然后编译并保存。
<3>、然后建立 OPC 服务器,有两种方式,本文介绍较简单的一种。
打开,Simatic Net 中的 Station Configurator,一般安装后,他会自动启动,并点击
Import Station…按钮,找到你刚才在 Step 7 中建立 PC 站时创建的
XDBs 文件夹下的 XDB 文件,然后导入成功。
<4>、可以使用 Simatic Net 中的 OPC Scout,并选择 Simatic NET 服务,然后在它下
面创建组,然后在组下创建变量,这样可以监控 PLC 数据,VC#编程不需要使用该程序,但熟
悉使用 OPC Scout 有利于了解 Simatic Net 中的编程结构。
说明:打开 Simatic Net 中的 Configuration Console,选中 S7 进行如下的配置后,可
以不需要 PLC、CP5611 等并可以模拟,如下图:
上面的所有步骤,均可在 Configuration Console 下,PC Station 的根树下,选择相应的
帮助文档得到。
3、 OPC 编程
<1>、西门子的变量结构如下:
----------------------服务器------------------------------
/ OPC.SimaticNet OPCServer.Wincc .... (一系列类型的服务器)
/ Group1 Group2 Group3 ...(把更新时间一致的变量统一为一个组)
/ Item1 Item2 ... (变量:I、Q、M、DB 等,指向网络中某个 PC 站 OPC Server 服务的某
个连接)
-------------------------------------------------------------------------------------------
----------------------
第一层是不同种类的服务器,如:OPC.SimaticNET 类型,OPC.SimaticNET.DP 类型,
OPCServer.WinCC 等一系列类型,这里选择 OPC.SimaticNET 类型。
第二层是 Group,一个服务器下可以有多个组,可以把组理解为扫描周期相同的一系列变量的集
合。在开发组态界面时,可以把一个界面中的所有变量统一到一个组中。
第三层是 Item,项是指向网络中某个 PC 站 OPC Server 服务的某个连接的一系列变量,如:
I、Q、M、DB 等
<2>、项的命名
项即 Item,在 S7 连接中针对的直接是 PLC 中的变量,因此它的命名很重要:
格式: :[]
其中的 protocolID 表示连接类型,在上面的组态 PC 站时可以选择,这里应该与它一致,类型
有 9 种,最常用的为 S7,即 S7 连接,其他类型请参看文档。
Connectionname:顾名思义,即在上面的组态 PC 站时产生的连接名,如果使用仿真功
能,连接名为 DEMO Variablename:变量名有一系列规则,这里举例说明,读者也可以使用
OPC Scout 创建变量,学习程序是如何生成变量名的。
S7:[DEMO]MB1 :表示连接类型为 S7,连接名为 DEMO(这里为仿真),变量为 MB1
S7:[DEMO]QB0,3: 表示为从 QB0 开始的三个连续变量。
S7:[DEMO]DB10,X4.6 :表示 DB10 的 DBX4.6。
<3>、添加引用
在 VC#开发环境中添加对 OpcRcw.Da 库的引用引用,该库属于.NET 库,不属于 COM
库,西门子虽然编写了类库,以提供对.NET 平台的支持,但这些类库仍然难于编程,
里面包含了大量的在托管和非托管区传输数据,因此我们需要在它的基础上再开发一个类库,
以简化以后的编程,首先在类的开头使用命名空间:
using System.Runtime.InteropServices;
using OpcRcw.Da;
using System.Collections;
<4>、编程
1、 在类的开头部分生名变量
private string serverType="";
private IOPCServer pIOPCServer; // OPC server 接口
private Object pobjGroup1; // Pointer to group object
private int nSvrGroupID; // server group handle for the added group
private System.Collections.Hashtable groupsID=new Hashtable(11); //用于记录组名
和组 ID 号
private System.Collections.Hashtable hitemsID=new Hashtable(17); //用于记录项名
和项 ID 号
private Guid iidRequiredInterface;
private int hClientGroup = 0; //客户组号
private int hClientItem=0; //Item 号
2、 创建服务器,编写 Open()方法
///
/// 创建一个 OPC Server 接口
///
///
/// 若为 true,创建成功,否则创建失败
public bool Open(out string error)
{
error="";bool success=true;
Type svrComponenttyp ;
//获取 OPC Server COM 接口
iidRequiredInterface = typeof(IOPCItemMgt).GUID;
svrComponenttyp = System.Type.GetTypeFromProgID(serverType);
try
{
//创建接口
pIOPCServer =(IOPCServer)System.Activator.CreateInstance(svrComponenttyp);
error="";
}
catch (System.Exception err) //捕捉失败信息
{
error="错误信息:"+err.Message;success=false;
}
Return true;
}
3、 在服务器上添加用于添加 Group 的函数
///
/// 添加组
///
///
///
///
///
/// 若为 true,添加成功,否则添加失败
public bool AddGroup(string groupName,int bActive,int updateRate,out string error)
{
error="";
int dwLCID = 0x407; //本地语言为英语
int pRevUpdateRate;
float deadband = 0;
// 处理非托管 COM 内存
GCHandle hDeadband;
IntPtr pTimeBias = IntPtr.Zero;
hDeadband = GCHandle.Alloc(deadband,GCHandleType.Pinned);
try
{
pIOPCServer.AddGroup(groupName, //组名
bActive, //创建时,组是否被激活
updateRate, //组的刷新频率,以 ms 为单位
hClientGroup, //客户号
pTimeBias, //这里不使用
(IntPtr)hDeadband,
dwLCID, //本地语言
out nSvrGroupID, //移去组时,用到的组 ID 号
out pRevUpdateRate, //返回组中的变量改变时的最短通知时间间隔
ref iidRequiredInterface,
out pobjGroup1); //指向要求的接口
hClientGroup=hClientGroup+1;
int groupID=nSvrGroupID;
groupsID.Add(groupName,groupID);
}
catch (System.Exception err) //捕捉失败信息
{
error="错误信息:"+err.Message;
}
finally
{
if (hDeadband.IsAllocated) hDeadband.Free();
}
if(error=="")
return true;
else
return false;
}
4、 向指定的组中添加变量的函数