47
如何在 C#中实现 OPC 数据访问 How to accomplish data accessing through OPC by C# Getting-started Edition (2009 03 )

如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

  • Upload
    others

  • View
    19

  • Download
    0

Embed Size (px)

Citation preview

Page 1: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

如何在 C#中实现 OPC 数据访问 How to accomplish data accessing through OPC by C#

Getting-started Edition (2009 年 03 月)

Page 2: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

摘 要 本文主要讲述了在 C#语言环境下,编程实现通过 SimaticNet 提供的 OPC Server,

访问 PLC 中数据的步骤。此方法同样适用于 WinCC 作为 OPC Server 时的数据访问。

关键词 SimaticNet、C#、OPC、WinCC

Key Words SimaticNet、C#、OPC、WinCC

IA&DT Service & Support

Page 2-47

Page 3: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

如何在C#中实现OPC数据访问 .................................................................................................1

1、概述 .............................................................................................................................4 1.1 OPC介绍 .................................................................................................................4 1.2 OPC的读写方式 .......................................................................................................5 1.3 OPC访问接口方式....................................................................................................6

2、测试环境 ......................................................................................................................7 2.1 硬件要求...................................................................................................................7 2.2 软件要求...................................................................................................................7

3、OPC Server端组态配置................................................................................................7 4、采用自定义接口过程 ....................................................................................................9

4.1 同步读写...................................................................................................................9 4.2 异步读写................................................................................................................. 12

5、采用自动化接口实现过程 ........................................................................................... 19 6、OPCItem的数据类型 .................................................................................................. 23 7、小结 ........................................................................................................................... 23 8、代码 ........................................................................................................................... 23

8.1 自动化接口 ............................................................................................................. 23 8.2 自定义接口同步读写............................................................................................... 28 8.3 自定义接口异步读写............................................................................................... 34

IA&DT Service & Support

Page 3-47

Page 4: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

1、概述

1.1 OPC 介绍

OPC 是 Object Linking and Embedding(OLE)for Process Control 的缩写,它是微软

公司的对象链接和嵌入技术在过程控制方面的应用。OPC 以 OLE/COM/DCOM 技术为基

础,采用客户/服务器模式,为工业自动化软件面向对象的开发提供了统一的标准,这个标

准定义了应用 Microsoft 操作系统在基于 PC 的客户机之间交换自动化实时数据的方法,采

用这项标准后,硬件开发商将取代软件开发商为自己的硬件产品开发统一的 OPC 接口程

序,而软件开发者可免除开发驱动程序的工作,充分发挥自己的特长,把更多的精力投入

到其核心产品的开发上。

SimaticNet 是西门子全集成自动化系统中的一个重要组成部分,它为完善的工业自动化

控制系统的通讯提供部件和网络,同时提供多个 OPCServer,为数据的外部访问提供接

口,本文主要以 OPC.SimaticNET 为例说明。

图 1:SimatcicNet 提供的 OPCServer

采用不同的通信方式,通过 OPC.SimaticNET,现场数据可以方便地提供给用户:

OPC.SimaticNET

OPC Client……ClientOPC

DP PNIOSNMP ……

图 2:多种数据提供方式

IA&DT Service & Support

Page 4-47

Page 5: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

1.2 OPC 的读写方式

在实际使用中,主要包括对现场数据的读写操作。

OPC 读数有三种方式:同步、异步、订阅。

同步通讯时,OPC 客户程序向 OPC 服务器进行请求时,OPC 客户程序必须等到 OPC

服务器对应的响应全部完成以后才能返回,在此期间 OPC 客户程序一直处于等待状态,若

进行读操作,那么必须等待 OPC 服务器响应后才返回。因此在同步通讯时,如果有大量数

据进行操作或者有很多 OPC 客户程序对 OPC 服务器进行读操作,必然造成 OPC 客户程序

的阻塞现象。因此同步通讯适用于 OPC 客户程序较少,数据量较小时的场合。

IA&DT Service & Support

Page 5-47

serverclient

Call

图 3 OPC 同步读写服务器-客户端数据流图

异步通讯时,OPC 客户程序对服务器进行请求时,OPC 客户程序请求后立刻返回,不

用等待 OPC 服务器的响应,可以进行其它操作。OPC 服务器完成响应后再通知 OPC 客户

程序,如进行读操作,OPC 客户程序通知 OPC 服务器后离开返回,不等待 OPC 服务器的

读完成,而 OPC 服务器完成读后,会自动的通知 OPC 客户程序,把读结果传送给 OPC 客

户程序。因此相对于同步通讯,异步通讯的效率更高。

图 4 OPC 异步读服务器-客户端数据流图

myGroup.SynchRead()

Reply

serveclien

CalmyGroup.AsyncRead(

myGroup_AsyncReadComplete(

Repl

Page 6: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

订阅方式时,OPC 客户程序对服务器进行请求时,OPC 客户程序操作后立刻返回,不

用等待 OPC 服务器的操作,可以进行其它操作, OPC 服务器的 Group 组在组内有数据发

生改变时,自动根据更新周期刷新相应的客户端数据,如下图,客户端只向 OPC 服务发送

一次请求,之后不再对服务器请求。

IA&DT Service & Support

Page 6-47

client serverSubscribemyGroup.IsSubscribed

Notify

myGroup_DataChange()Notify

myGroup_DataChange()

图 5 OPC 同步读服务器-客户端数据流图

OPC 写数有两种方式:同步、异步。区别与上面讲的机制一样,在生产应用中,如果写

数据参与控制,一般采用同步方式。

1.3 OPC 访问接口方式

OPC 主要包含两种接口:CUSTOM 标准接口和 OLE 自动化标准接口,自定义接口是

服务商必须提供的,而自动化接口则是可选的。

自定义接口是一组 COM 接口,主要用于采用 C++语言的应用程序开发;

自动化接口是一组 OLE 接口,主要用于采用 VB,DELPHI,Excel 等基于脚本编程语

言的应用程序开发。

图 6 自定义接口和自动化接口

Page 7: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

许多 OPC 服务器,包括 OPC.SimaticNet,是在 COM 平台开发的,从而对于基

于.NET 框架下的 C#语言,作为客户端程序语言访问 OPCServer,需要解决两个平台间无缝

迁移的问题。OPC 基金会对会员提供了 OpcRcw 动态链接库,OPC NET COM 包装器和

OPC NET API,将 OPC 复杂的规范封状成简单易用的 C#类 ,可以比较容易地实现数据访

问。

本文中通过实验,逐步讲解了通过 C#编写客户端程序,访问 OPC.SimaticNet,对

PLC 数据进行读写的实现过程。自定义接口及自动化接口都进行了测试,但基于 C#的语言

特性,建议采用自定义接口访问,同时有很多 OPCServer 服务商,对外是不提供自动化接

口的,西门子的 SimaticNet 及 WinCC 的 OPCServer 都提供自动化接口。

2、测试环境

2.1 硬件要求

采用 400 系列 PLC,通过以太网连接到安装有 simaticNet 的计算机上。

IA&DT Service & Support

Page 7-47

computer:windows 2003 server--------192.168.0.102

CPU:CPU414-3PN ------416-3FR05-0AB0--------192.168.0.1

2.2 软件要求

computer:

Simatic.net 2007

Visual studio 2005

Step7 V5.4 SP4

3、OPC Server 端组态配置

Page 8: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

在 CPU 中定义 DB 块:DB10

配置 PC Station,参考

其它文档。

如上图建立连接 S7_connection_1,然后在 OPC Scout 测试连接的正确性。

IA&DT Service & Support

Page 8-47

Page 9: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

从上面可以看到数据访问都是正常的。

4、采用自定义接口过程

4.1 同步

立同步 Sync_RW

读写

建 读写项目:

测试中,对 db10.dbw0 及 db10.dbw2 读写操作,在 Form 窗口做如下设计:

Control name Text

Button: Btn_Read Read

Button: Btn_Write Write

Button: Btn_DisConn disConn

Button: Btn_Conn Conn

IA&DT Service & Support

Page 9-47

Page 10: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

TextBox: Txt_R1_Value

TextBox: Txt_R1_Quality

TextBox: Txt_R1_TimeStamp

TextBox: Txt_R2_Value

TextBox: Txt_R2_Quality

TextBox: Txt_R2_TimeStamp

TextBox: Txt_W1

TextBox: Txt_W2

TextBox: Txt_WriteStatus

第一步,添加下面命名空间:(首先需要在项目中添加相应的引用)

using OpcRcw.Comn;

using OpcRcw.Da;

第二步,定义 OPC 相关变量,

OpcRcw.Da.IOPCServer SrverObj;//定义OPCServer 对象

OpcRcw.Da.IOPCSyncIO IOPCSyncIO2Obj = null;//同步读对象

OpcRcw.Da.IOPCGroupStateMgt IOPCGroupStateMgtObj = null;//管理OPCGroup组对象

internal const int LOCALE_ID = 0x407;//OPCServer语言码-英语

Object MyobjGroup1 = null;//OPCGroup对象

组,并添加需要读写的 Item

EventArgs e)

int[] ItemServerHandle;//Item句柄数组

int pSvrGroupHandle = 0;//OPCGroup 句柄

第三步,连接 OPCServer,建立相应 OPCGroup

private void Btn_Conn_Click(object sender, System.

{

……

//定义变量

IA&DT Service & Support

Page 10-47

Page 11: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

peFromProgID("OPC.SimaticNet", "192.168.0.102");

rver)Activator.CreateInstance(svrComponenttyp);

8.0.102"是 OPCServer 名称及所在 computer 地址

OPCSerer 的实例

写定义句柄

= (IOPCGroupStateMgt)MyobjGroup1; //组管理对象

Path = "";

ItemArray[0].szItemID = "S7:[S7 connection_1]DB10,INT0";

,不同数据类型表示方法不同

ItemArray[0].bActive = 1;//是否激活

ItemArray[0].hClient = 1;//标示ID,不同的Item不一样

ItemArray[0].dwBlobSize = 0;

ItemArray[0].pBlob = IntPtr.Zero;

ItemArray[0].vtRequestedDataType = 2;

……

((OpcRcw.Da.IOPCItemMgt)MyobjGroup1).AddItems(2, ItemArray, out pResults,

out pErrors); //将定义的 OPCTtem 加入组内,注意数量

……

}

这里需要注意两个地方,对于 hClient 每个 Item 是不一样的。

根据读写的数据类型,需更改 vtRequestedDataType 的值,具体区分在后面

说明。

d_Click(object sender, EventArgs e)

PCDATASOURCE.OPC_DS_DEVICE, 2, ItemServerHandle,

Txt_R1_Value.Text = String.Format("{0}", pItemState[0].vDataValue);//读值

//质量码

DateTime dt = ToDateTime(pItemState[0].ftTimeStamp);

//读取时间

息的指针,要通过 OPCITEMSTATE[]

uct OPCITEMSTATE

{

ftTimeStamp;

public int hClient;

svrComponenttyp = Type.GetTy

ServerObj = (OpcRcw.Da.IOPCSe

//"OPC.SimaticNet", "192.16

// CreateInstance 创建一个

ServerObj.AddGroup(……)//增加相应的组,定义组的特性,并输出组的句柄

IOPCSyncIO2Obj = (IOPCSyncIO)MyobjGroup1;

//为组同步读

IOPCGroupStateMgtObj

ItemArray[0].szAccess

//地址

第四步,同步读数据

private void Btn_Rea

{

IOPCSyncIO2Obj.Read(O

out pItemValues, out pErrors);//读数

……

Txt_R1_Quality.Text = GetQuality(pItemState[0].wQuality);

Txt_R1_TimeStamp.Text = dt.ToString();

}

在这里要注意 pItemValues 返回指向值信

pItemState 获得信息,其中 OPCITEMSTATE 是一个结构体,包含值,质量码,时间

等。

public str

public FILETIME

IA&DT Service & Support

Page 11-47

Page 12: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

public object vDataValue;

五步,同步写数据

private void Btn_Write_Click(object sender, EventArgs e)

emServerHandle, values, out pErrors);

void Btn_Disconn_Click(object sender, EventArgs e)

{

……

8 节代码。

4.2 异步读写

注意,订阅也是 异步方式。

建立异步读写项目

public short wQuality;

public short wReserved;

}

{

……

IOPCSyncIO2Obj.Write(2, It

……

}

这里注意,如果数据类型不正确,数据是不能正确写入的。

第六步,注销相应实例

private

}

参考第

测试中,对 w0 及 db10.dbw2 读写操作,在db10.db Form 窗口做如下设计:

Text Control name

IA&DT Service & Support

Page 12-47

Page 13: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

Button: Btn_Conn Conn

ad

n: Btn_Write Write

tn_DisConn disConn

tBox: Txt_R1_Quality

TextBox: Txt_R2_Value

TextBox: Txt_R2_Quality

TextBox: Txt_R2_TimeStamp

R3_Quality

TextBox: Txt_R4_Value

TextBox: Txt_R4_Quality

TextBox: Txt_R4_TimeStamp

TextBox: Txt_W1

TextBox: Txt_W2

TextBox: Txt_WriteStatus

CheckBox: CHK_Btn

Button: Btn_Read Re

Butto

Button: B

TextBox: Txt_R1_Value

Tex

TextBox: Txt_R1_TimeStamp

TextBox: Txt_R3_Value

TextBox: Txt_

TextBox: Txt_R3_TimeStamp

IA&DT Service & Support

Page 13-47

Page 14: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

第 添加下面命名空间:(首先需要在一步, 项目中添加相应的引用)

;//定义OPCServer 对象

null;//异步读对象

PCGroupStateMgtObj = null;//管理OPCGroup组对象

null;//异步事件点

Point = null;//

0x407;//OPCServer语言码-英语

p对象

句柄数组

Group 句柄

ent's sink

第三步, OPCGroup 组,并添加需要读写的 Item

ject sender, System.EventArgs e)

eFromProgID("OPC.SimaticNet", "192.168.0.102");

)Activator.CreateInstance(svrComponenttyp);

OPCServer 名称及所在 computer 地址

// CreateInstance 创建一个 OPCSerer 的实例

ServerObj.AddGroup(……)//增加相应的组,定义组的特性,并输出组的句柄

IOPCAsyncIO2Obj = (IOPCAsyncIO2)MyobjGroup1;

//为组异步读写定义句柄

IOPCGroupStateMgtObj = (IOPCGroupStateMgt)MyobjGroup1; //组管理对象

与同步不同,考虑增加如下语句:

pIConnectionPointContainer = (IConnectionPointContainer)MyobjGroup1;

//定义特定组的异步调用连接

Guid iid = typeof(IOPCDataCallback).GUID;

// 为所有的异步调用创建回调

pIConnectionPointContainer.FindConnectionPoint(ref iid, out pIConnectionPoint);

// 为OPC Server的连接点与客户端接收点之间建立连接

pIConnectionPoint.Advise(this, out dwCookie);

ItemArray[0].szAccessPath = "";

ItemArray[0].szItemID = "S7:[S7 connection_1]DB10,INT0";

//地址,不同数据类型表示方法不同

ItemArray[0].bActive = 1;//是否激活

ItemArray[0].hClient = 1;//标示ID,不同的Item不一样

ItemArray[0].dwBlobSize = 0;

ItemArray[0].pBlob = IntPtr.Zero;

using OpcRcw.Comn;

using OpcRcw.Da;

第二步,定义 OPC 相关变量

OpcRcw.Da.IOPCServer SrverObj

OpcRcw.Da.IOPCAsyncIO2 IOPCAsyncIO2Obj =

OpcRcw.Da.IOPCGroupStateMgt IO

IConnectionPointContainer pIConnectionPointContainer =

IConnectionPoint pIConnection

internal const int LOCALE_ID =

Object MyobjGroup1 = null;//OPCGrou

int[] ItemServerHandle;//Item

int pSvrGroupHandle = 0;//OPC

Int32 dwCookie = 0; //this cli

连接 OPCServer,建立相应

private void Btn_Conn_Click(ob

{

……

//定义变量

svrComponenttyp = Type.GetTyp

ServerObj = (OpcRcw.Da.IOPCServer

//"OPC.SimaticNet", "192.168.0.102"是

IA&DT Service & Support

Page 14-47

Page 15: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

……

ItemMgt)MyobjGroup1).AddItems(4, ItemArray, out pResults,

out pErrors); //将定义的 OPCTtem 加入组内,注意数量

这里同样需要注意两个地方,对于 hClient 每个 Item 是不一样的。

EventArgs e)

IOPCAsyncIO2Obj.Read(4,ItemServerHandle,2,out nCancelid,out pErrors);

对应

}

调用异步读回调函数

{

Txt_R1_Value.Tex alues[0]);

……

编译执行,程序会几方面的报错

ItemArray[0].vtRequestedDataType = 2;

((OpcRcw.Da.IOPC

……

}

根据读写的数据类型,需更改 vtRequestedDataType 的值,定义如上文。

另外,要注意理解异步调用时的服务器与客户端反馈关系。

第四步,异步读数据方式

private void btn_Read_A_Click(object sender, System.

{

……

//异步读,nCancelid、dwTransactionID 都是为了客户端服务器的

……

public virtual void OnReadComplete( System.Int32 dwTransid ,

System.Int32 hGroup ,

System.Int32 hrMasterquality ,

System.Int32 hrMastererror ,

System.Int32 dwCount ,

int[] phClientItems , //读数据句柄

object[] pvValues , //返回值

short[] pwQualities , //返回质量码

OpcRcw.Da.FILETIME[] pftTimeStamps , //返回时间戳

int[] pErrors ) //错误码

……

t = String.Format("{0}", pvV

Txt_R1_Quality.Text = GetQuality(pwQualities[0]);

DateTime dt = ToDateTime(pftTimeStamps[0]);

Txt_R1_TimeStamp.Text = dt.ToString();

}

第五步,订阅方式读回调函数及实现 COM 映射

上面程序会有以下几种情况的报错:

IA&DT Service & Support

Page 15-47

Page 16: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

问题 1:程序执行后,弹出如下错误,Add group 报错

主要原因

起,实现 COM

public p

CDataCallback 接口后

是 Form 要使用 IOPCDataCallback,目的是将 OPC 的接口与实现类结合在一

的映射。

需要做的处理是:

artial class Form1 : Form, IOPCDataCallback

问题 2:添加 IOP

主要原因是,IOPCDataCallback 有 4 个纯虚函数,必须实现

public virt .Int32 dwTransid, //异步读完成触发

System.Int

System.Int

System.Int

System.Int

in ] phCl

object[] p

short[] pwQualities,

OpcRcw.Da.FILETIME[] pftTimeStamps,

in ] pErr

public virtual void OnWriteComplete ( System.Int32 dwTransid , //异步写完成触发

System.Int

System.Int

System.Int32 dwCount ,

in ] pCli

in rrors )

lete(System.Int32 dwTransid, System.Int32 hGroup)

//取消特定操作触发

//订阅方式下读触发

ual void OnReadComplete(System

32 hGroup,

32 hrMasterquality,

32 hrMastererror,

32 dwCount,

t[ ientItems,

vValues,

t[ ors)

32 hGroup ,

32 hrMastererr ,

t[ enthandles ,

t[] pE

public virtual void OnCancelComp

public virtual void OnDataChange(Int32 dwTransid,

Int32 hGroup,

IA&DT Service & Support

Page 16-47

Page 17: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

Int32 hrMastererror,

Int32 dwCount,

int[] phClientItems,

object[] pvValues,

short[] pwQualities,

OpcRcw.Da.FILETIME[] pftTimeStamps,

int[] pErrors)

问题 3:运行时,有时会弹出 Cross-thread operation not valid 错误,这是 C#中

对控件

,InitealizeComponent 语句做如下处理:

{

InitializeComponent();

s = false;

}

第六步,异步写数据

private void Btn_Write_Click(object sender, EventArgs e)

{

……

object[] values = new object[4];

values[0] = Txt_W1.Text;

values[1] = Txt_W2.Text;

values[2] = "test";//采用常数

m.Int32 dwTransid ,

p ,

System.Int32 hrMastererr ,

System.Int32 dwCount ,

ServerObj.GetErrorString( pErrors[0], LOCALE_ID, out strResult);

Txt_WriteStatus1.Text = strResult;

}

第七步

Int32 hrMasterquality,

继承性的一种严格要求,在调试时会出现,可以做如下处理。

在 Form 的.ctor 中

public Form1()

Control.CheckForIllegalCrossThreadCall

values[3] = 1; //采用常数

IOPCAsyncIO2Obj.Write(4, ItemServerHandle, values, 3, out nCancelid, out

pErrors); //异步写数据

……

}

写完成处理(执行结果监视

public virtual void OnWriteComplete ( Syste

System.Int32 hGrou

int[] pClienthandles ,

int[] pErrors )

{

……

……

,订阅方式读数据

IA&DT Service & Support

Page 17-47

Page 18: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

OPC 服务器的 Group 组在组内有数据发生改变时,自动根据更新周期刷新相应的客户

据的操作使用订阅方式更有优势。

vo ged(object sender, EventArgs e)

{

et = 1;

t nRevUpdateRate,

hActive.AddrOfPinnedObject(), pTimeBias, pDeadband,

pLCID, hClientGroup); //为组设定特定信息

}

通过IOPCDa 实现

dwTransid,

Int3

int[] phClientItems,

object[] pvValues, //值

shor Qualities, //质量码

FILETIME[] pftTimeStamps, //时间戳

pErrors)

{

t] == 1) //根据Item在客户端注册句柄查询

Convert.ToString(pvValues[nCount]);

= GetQuality(pwQualities[nCount]);

ToDateTime(pftTimeStamps[nCount]);

t_R1_TimeStamp.Text = dt.ToString();

}

……

}

八步,注销相

vate void Btn_Disconn_Click(object sender, EventArgs e)

{

……

端数据。工程应用中,大量数

订阅方式下,要考虑数据更新速度,及是否采用订阅方式读写。

private id CHK_Btn_CheckedChan

……

GCHandle hActive = GCHandle.Alloc(nActive, GCHandleType.Pinned);

if (CHK_Btn.Checked != true)

hActive.Target = 0;

else

hActive.Targ

……

IOPCGroupStateMgtObj.SetState(pRequestedUpdateRate, ou

……

taCallback的虚函数OnDataChange

public virtual void OnDataChange(Int32

Int32 hGroup,

Int32 hrMasterquality,

Int32 hrMastererror,

2 dwCount,

t[] pw

OpcRcw.Da.

int[]

……

if (phClientItems[nCoun

{

Txt_R1_Value.Text =

Txt_R1_Quality.Text

DateTime dt =

Tx

第 应实例

pri

IA&DT Service & Support

Page 18-47

Page 19: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

}

实例参考

5、采用自动化接口实现过程

动化接口,程序相应简单些。

参考第 8 节代码。

对于自

建立项目:Automation_RW

测试中,对 db10.dbw0 及 db10.dbw2 读写操作,在 Form 窗口做如下设计:

ontrol name Text

Button: Btn_Write_A Write_A

utton: Btn_DisConn disConn

C

Button: Btn_Conn Conn

Button: Btn_Read_S Read_S

Button: Btn_Read_A Read_A

Button: Btn_Write_S Write_S

B

IA&DT Service & Support

Page 19-47

Page 20: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

xtBox: Txt_R1_Value

Txt_R1_Quality

TimeStamp

TextBox: Txt_R2_Value

t_R2_Quality

p

TextBox: Txt_W2

TextBox: Txt_ Txt_WriteStatus2

CheckBox: CHK_Btn

Te

TextBox:

TextBox: Txt_R1_

TextBox: Tx

TextBox: Txt_R2_TimeStam

TextBox: Txt_W1

第一步,添加下列命名空间(首先在 COM 组件中添加相应组件)

第二步,

rver

的句柄

第三步,建立连接及对象

"192.168.0.102");

MyOpcGroup = MyOpcServer.OPCGroups.Add("MyGroup1");

MyOpcItem1 = MyOpcGroup.OPCItems.AddItem("S7:[S7 connection_1]DB10,INT0",1);

MyOpcItem2 = MyOpcGroup.OPCItems.AddItem("S7:[S7 connection_1]DB10,INT2", 2);

ServerHandle[0] = MyOpcItem1.ServerHandle;

ServerHandle[1] = MyOpcItem2.ServerHandle;

using OPCSiemensDAAutomation;

定义 OPC 相关变量

OPCServer MyOpcServer; //定义OPCSe

OPCGroup MyOpcGroup; //定义组

OPCItem MyOpcItem1; //Item

OPCItem MyOpcItem2; //值

long[] ServerHandle = new long[2]; //Item

MyOpcServer = new OPCServer();

MyOpcServer.Connect("OPC.SimaticNet",

IA&DT Service & Support

Page 20-47

Page 21: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

ivate ender, EventArgs e)//同步读数据

{

ues, out Qualities, out TimeStamps);

Qualities,TimeStamps分别是值,质量码及时间

用SyncRead函数,参数可参考异步读函数

第四步,同步写

object sender, EventArgs e)

{

MyOpcItem1.Write(Txt_W1.Text);

//也可以通过调用SyncWrite函数,参数可参考异步写函数

……

}

第五步,异步事件定义,

在异步操作情况下,需要定义相应的异步事件

MyOpcGroup.DataChange += new

DIOPCGroupEvent_DataChangeEventHandler(MyOpcGroup_DataChange); //

//订阅方式下数据改变

iteComplete += new

DIOPCGroupEvent_AsyncWriteCompleteEventHandler(MyOpcGroup_WriteComplete);

//写完成事件

MyOpcGroup.AsyncReadComplete += new

DIOPCGroupEvent_AsyncReadCompleteEventHandler(MyOpcGroup_ReadComplete);

//读完成事件

MyOpcGroup.AsyncCancelComplete += new

DIOPCGroupEvent_AsyncCancelCompleteEventHandler(MyOpcGroup_CancelComplete);

使用中 接口: MyOpcGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles,

temValues, ref Array Qualities, ref Array TimeStamps)

void MyOpcGroup_WriteComplete( int NumItems, ref Array ClientHandles,

MyOpcGroup_ReadComplete( int NumItems, ref System.Array

Array ItemValues, ref System.Array Qualities,

Errors)

onID, int NumItems, ref Array ClientHandles,

tamps)

四步,同步读数据,

pr void Btn_Read_S_Click(object s

……

MyOpcItem1.Read(1,out ItemVal

//ItemValues,

//也可以通过调

……

}

数据

private void Btn_Write_S_Click(

……

//取消操作事件

在 注意,其事件函数要按照特定void

ref Array I

int TransactionID,

ref Array Errors)

void int TransactionID,

ClientHandles, ref System.

ref System.Array TimeStamps, ref System.Array

void MyOpcGroup_CancelComplete(int CancelID)

第六步订阅方式读

void MyOpcGroup_DataChange(int Transacti

ref Array ItemValues, ref Array Qualities, ref Array TimeS

{

……

//注意数据改变时,Item 数量要通过 NumItems 得到,也就是说只有数据改变时,才对一

遍,所以降低了服务器负担。要注意读语句写法。

IA&DT Service & Support

Page 21-47

Page 22: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

……

}

第七步异步读

ivate

Arra rverHandles = (Array)handle;

Array errors;

ID;

……

READASYNC_ID, out

cancelID);

……

id MyO tionID, int NumItems, ref System.Array

lities,

ref System.Array TimeStamps, ref System.Array Errors)

//注意TransactionID的对应

}

注 ,数据下标是从 1 开始的,所以要考虑将第 0 位空出

来,n 个 ,又是从左开始读的。

否则会报错。

第八步异

priva

WRITEASYNC_ID, ou cancel

void MyOpcGr ntHandles,

{

……

}

第九步释放对象

private void

}

参考第 8

pr void Btn_Read_A_Click(object sender, EventArgs e)//异步读事件

{

int[] handle = new int[3] {ServerHandle[0], ServerHandle[1],0};//注意方式

y MySe

int cancel

MyOpcGroup.AsyncRead(2, ref MyServerHandles, out errors,

}

vo pcGroup_ReadComplete(int Transac

ClientHandles, ref System.Array ItemValues, ref System.Array Qua

{

……

……

意 array 在函数内部做参数时

Item,就要定义 n+1 列数组,添加一个 0,但在函数使用时

步写

te void Btn_Write_A_Click(object sender, EventArgs e)

{

……

MyOpcGroup.AsyncWrite(2, ref MyServerHandles, ref Myvalues, out errors,

t ID);

……

}

oup_WriteComplete(int TransactionID, int NumItems, ref Array Clie

ref Array Errors)

同样要注意 Array 在函数内部做参数的传递。

Btn_Disconn_Click(object sender, EventArgs e)

{

……

节代码。

IA&DT Service & Support

Page 22-47

Page 23: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

6、OPCItem 的 型

在通过自定义接口访问时,

"";

据类型表示

mArray[1].bActive = 1;//是否激活

= IntPtr.Zero;

estedDataType = 5;

Item

ItemArray[2].szItemID onnection_1]DB10,STRING26.10";//地址,不同数据类型表

示方法不同

2].bActive = 1;//是否激活

ItemArray[2].

ItemArray[2].

mArray[2].vtRequestedDataType = 8;

ype 代表了不同数据类型,在使用中需要注意的。

VbBoolean V VbDecimal VbDouble Vbinteger VbLong VbSingle VbString

数据类

ItemArray[1].szAccessPath =

ItemArray[1].szItemID = "S7:[S7 connection_1]DB10,Real4";//地址,不同数

Ite

ItemArray[1].hClient = 2;//表示ID

ItemArray[1].dwBlobSize = 0;

ItemArray[1].pBlob

ItemArray[1].vtRequ

Array[2].szAccessPath = "";

= "S7:[S7 c

ItemArray[

ItemArray[2].hClient = 3;//表示ID

dwBlobSize = 0;

pBlob = IntPtr.Zero;

Ite

在上面可以看到,vtRequestedDataT

bByte

11 17 14 5 2 3 4 8

7、小结

在实际应用中,根据实际要求,合理选择读写方式是很重要的。同时实例中是以

SimaticNet 的 OPCServer 为例,对于 WinCC 作为 OPCServer 同样适用,只需要将

ticNet"改为"OPCServer.WinCC"。

同时需要注 配

,注册相应动 库。

8、代码

1 自动化接口

using System;

using Sy

using System.ComponentModel;

using System.Text;

using System.Windows.Forms;

ing System.Collections;

using OPCSiemensDAAutomation;//引用连接库

namespac

"OPC.Sima

意的是,测试环境客户端需要安装 simaticNet。如果不安装,需要另行

置 态连接

8.

stem.Collections.Generic;

using System.Data;

using System.Drawing;

us

e Automation_RW

IA&DT Service & Support

Page 23-47

Page 24: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

{

public partial class Form1 : Form

{

public Form1()

ver

OPCItem MyOpcItem2;

const int READASYNC_ID=1;//异步读事务

{

r

{

cServer w OPCServe ;

MyOpcServer.Connect("OPC.SimaticNet", "192.168.0.102");//OPCServer

MyOpcGroup = MyOpcServer.OPCGroups.Add("MyGroup1");

MyOpcGroup.IsActive = true;

需要

MyOpcGroup.UpdateRate = 1000;//更新速率s

;

0] = MyOpcItem1.ServerHandle;

ServerHandle[1] = MyOpcItem2.ServerHandle;

MyOpcGroup.AsyncWriteComplete += new

DIOPCGroupEvent_AsyncWriteCompleteEventHandler(MyOpcGroup_WriteComplete);

MyOpcGroup.AsyncReadComplete += new

DIOPCGroupEvent_AsyncReadCompleteEventHandler(MyOpcGroup_ReadComplete);

MyOpcGroup.AsyncCancelComplete += new

DIOPCGroupEvent_AsyncCancelCompleteEventHandler(MyOpcGroup_CancelComplete);

ge += new

Event_DataChangeEventHandler(MyOpcGroup_DataChange);

stem.Exception error)

"Result - connect server", MessageBoxButtons.OK,

MessageBoxIcon.Error);

{

InitializeComponent();

}

OPCServer MyOpcServer;//OPCSer

OPCGroup MyOpcGroup;//

OPCItem MyOpcItem1;

int[] ServerHandle = new int[2];//服务器端注册句柄

const int WRITEASYNC_ID=2;//异步写事务

private void Btn_Conn_Click(object sender, EventArgs e)//建立连接

t y

MyOp = ne r()

MyOpcGroup.IsSubscribed = true;//是否异步,在采用异步读写,订阅等方式下都

MyOpcGroup.DeadBand = 0;

MyOpcItem1 = MyOpcGroup.OPCItems.AddItem("S7:[S7 connection_1]DB10,INT0", 1);

MyOpcItem2 = MyOpcGroup.OPCItems.AddItem("S7:[S7 connection_1]DB10,INT2", 2)

ServerHandle[

MyOpcGroup.DataChan

DIOPCGroup

}

catch(Sy

{

MessageBox.Show(error.Message,

}

}

IA&DT Service & Support

Page 24-47

Page 25: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

private void Btn_Read_S_Click(object sender, EventArgs e)//同步读数据

{

object ItemValues;

object TimeStamps;

try

1,out ItemValues, out Qualities, out TimeStamps);

Text = String.Format("{0}", ItemValues);

ualities);

// Timestamp

ormat("{0}", TimeStamps);

}

catch (System.Exception error)

{

MessageBox.Show(error.Message, "Result - 同步读", MessageBoxButtons.OK,

}

void Btn_Write_S_Click(object sender, EventArgs e)//同步写数据

;

catch (System.Exception error)

步写", MessageBoxButtons.OK,

MessageBoxIcon.Error);

{

}

private void

int[] ha 方式

Array MyServerHandles = (Array)handle;

ay errors;

MyOpcGroup.Asy ndles, out errors, READASYNC_ID, out

cancelID);

}

object Qualities;

{

MyOpcItem1.Read(

Txt_R1_Value.

// Quality

Txt_R1_Quality.Text = String.Format("{0}", Q

Txt_R1_TimeStamp.Text = String.F

MessageBoxIcon.Error);

}

private

{

try

{

MyOpcItem1.Write(Txt_W1.Text)

}

{

MessageBox.Show(error.Message, "Result - 同

}

}

void MyOpcGroup_CancelComplete(int CancelID)

//增加相应代码

Btn_Read_A_Click(object sender, EventArgs e)//异步读事件

{

ndle = new int[3] {ServerHandle[0], ServerHandle[1],0};//注意写的

Arr

int cancelID;

try

{

ncRead(2, ref MyServerHa

IA&DT Service & Support

Page 25-47

Page 26: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

catch (System.Exception error)

MessageBox.Show(error.Message, "Result - 异步读", MessageBoxButtons.OK,

MessageBoxIcon.Error);

OpcGroup_ReadComplete(int TransactionID, int NumItems, ref System.Array

alities,

s)

{

if(Convert.ToInt32(ClientHandles.GetValue(1))==1)

if (Convert.ToInt32(Errors.GetValue(1))==0)

Txt_R2_Value.Text = ItemValues.GetValue(1).ToString();

Txt_R2_Quality.Text = Qualities.GetValue(1).ToString();

Txt_R2_TimeStamp.Text = TimeStamps.GetValue(1).ToString();

}

//增加其余的代码

ch (System.Exception error)

Messa , "Result - 异步读", MessageBoxButtons.OK,

MessageBoxIcon.Error);

}

d MyOpcGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles,

ItemValues, ref Array Qualities, ref Array TimeStamps)

try

for (int i = 0; i < NumItems; i++)

Convert.ToInt32(ClientHandles.GetValue(i + 1)) == j)

{

if (ItemValues.GetValue(i + 1) != null)

Txt_R2_Value.Text = ItemValues.GetValue(i + 1).ToString();

Txt_R2_Quality.Text = Qualities.GetValue(i + 1).ToString();

{

}

}

//读完成事

void My

ClientHandles, ref System.Array ItemValues, ref System.Array Qu

ref System.Array TimeStamps, ref System.Array Error

{

try

{

if (TransactionID == READASYNC_ID)

{

{

}

}

}

cat

{

geBox.Show(error.Message

}

//订阅方式

voi

ref Array

{

{

{

for (int j = 1; j < 3; j++)

{

if (

{

IA&DT Service & Support

Page 26-47

Page 27: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

Txt_R2_TimeStamp.Text = TimeStamps.GetValue(i + 1).ToString();

}

}

}

}

tem.Exception error)

Mess

d Btn_Write_A_Click(object sender, EventArgs e)//异步写

e = new int[3] { ServerHandle[0], ServerHandle[1],0 };

= new object[3] {14,Txt_W2.Text,""};

roup.AsyncWrite(2, ref MyServerHandles, ref Myvalues, out errors,

WRITEASYNC_ID, out cancelID);

n error)

e, "Result - 异步写", MessageBoxButtons.OK,

MessageBoxIcon.Error);

}

}

完成

d MyOpcGroup_WriteComplete(int TransactionID, int NumItems, ref Array ClientHandles,

ref Array Errors)

}

CHK_Btn_CheckedChanged(object sender, EventArgs e)

}

}

catch (Sys

{

ageBox.Show(error.Message, "Result - 订阅", MessageBoxButtons.OK,

MessageBoxIcon.Error);

}

}

private voi

{

int[] handl

Array MyServerHandles = (Array)handle;

object[] values

Array Myvalues=(Array)values;

Array errors;

int cancelID;

try

{

MyOpcG

}

catch (System.Exceptio

{

MessageBox.Show(error.Messag

//异步写

voi

{

Txt_WriteStatus2.Text = Errors.GetValue(1).ToString();

private void

{

if (CHK_Btn.Checked != true)

{

MyOpcGroup.IsSubscribed = false;

}

else

{

MyOpcGroup.IsSubscribed = true;

}

//推出释放连接及对象

private void Btn_Disconn_Click(object sender, EventArgs e)

IA&DT Service & Support

Page 27-47

Page 28: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

{

try

{

em1 != null)

pcItem1 = null;

null)

MyOpcItem2 = null;

MyOpcGroup = null;

}

catch (System.Exception error)

MessageBox.Show(error.Message, "Result - 异步写", MessageBoxButtons.OK,

MessageBoxIcon.Error);

}

llections;

using System.Drawing;

mponentModel;

flection;

using System.Data;

;//引用

w.Da;//引用

{

partial class Form1 : Form

lic Form1()

{

Da.IOPCServer ServerObj;//定义OPCServer 对象

;//同步读对象

Da.IOPCGroupStateMgt IOPCGroupStateMgtObj = null;//管理OPCGroup组对象

l const int LOCALE_ID = 0x407;//OPCServer语言码-英语

null;//OPCGroup对象

[] ItemServerHandle;//Item句柄数组

0;//OPCGroup 句柄

if (MyOpcIt

MyO

if (MyOpcItem2 !=

if (MyOpcGroup != null)

MyOpcServer.Disconnect();

{

}

}

}

8.2 自定义接口同步读写

using System;

using System.Co

using System.Runtime.InteropServices;

using System.Co

using System.Windows.Forms;

using System.Re

using System.Runtime.CompilerServices;

using OpcRcw.Comn

using OpcRc

namespace Sync_RW

public

{

pub

InitializeComponent();

}

OpcRcw.

OpcRcw.Da.IOPCSyncIO IOPCSyncIO2Obj = null

OpcRcw.

interna

Object MyobjGroup1 =

int

int pSvrGroupHandle =

IA&DT Service & Support

Page 28-47

Page 29: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

private void Btn_Conn_Click(object sender, EventArgs e)

e svrComponenttyp;

1000;//订阅读取速度

rray;

TimeBias = 0;

andle hTimeBias, hDeadband;

hDeadband = GCHa andleType.Pinned);

Guid iidRequiredInterface = typeof(IOPCItemMgt).GUID;

{

svrComponenttyp = Type.GetTypeFromProgID("OPC.SimaticNet",

"192.168.0.102");//OPCServer

ServerObj = (OpcRcw.Da.IOPCServer)Activator.CreateInstance(svrComponenttyp);

//注册

try

{

bj.AddGroup("MyOPCGroup1",//增加组

dwRequestedUpdateRate,

tGroup,

Bias.AddrOfPinnedObject(),

eadband.AddrOfPinnedObject(),

out pSvrGroupHandle,

out pRevUpdateRate,

f iidRequiredInterface,

out MyobjGroup1);

IOPCSyncIO2Obj = (IOPCSyncIO)MyobjGroup1;

//Query interface for sync calls on group object

j = (IOPCGroupStateMgt)MyobjGroup1;

ItemArray = new OPCITEMDEF[2];//定义读写的item,共个变量

ItemArray[0].szAccessPath = "";

temID = "S7:[S7 connection_1]DB10,INT0";

//地址,不同数据类型表示方法不同

ItemArray[0].bActive = 1;//是否激活

ItemArray[0].vtRequestedDataType = 2;

ItemArray[1].szAccessPath = "";

7 connection_1]DB10,STRING14.10";

不同数据类型表示方法不同

否激活

ItemArray[1].hClient = 2;//表示ID

{

Typ

Int32 dwRequestedUpdateRate =

Int32 hClientGroup = 1;

Int32 pRevUpdateRate;

OpcRcw.Da.OPCITEMDEF[] ItemA

float deadband = 0;

int

GCH

hTimeBias = GCHandle.Alloc(TimeBias, GCHandleType.Pinned);

ndle.Alloc(deadband, GCH

try

ServerO

0,

hClien

hTime

hD

LOCALE_ID,

re

IOPCGroupStateMgtOb

ItemArray[0].szI

ItemArray[0].hClient = 1;//表示ID

ItemArray[0].dwBlobSize = 0;

ItemArray[0].pBlob = IntPtr.Zero;

ItemArray[1].szItemID = "S7:[S

//地址,

ItemArray[1].bActive = 1;//是

IA&DT Service & Support

Page 29-47

Page 30: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

ItemArray[1].dwBlobSize = 0;

ItemArray[1].pBlob = IntPtr.Zero;

RequestedDataType =8;

= IntPtr.Zero;

o;

try

((OpcRcw.Da.IOPCItemMgt)MyobjGroup1).AddItems(2, ItemArray, out

pResults, out pErrors);

[2];

Marshal.Copy(pErrors, errors, 0, 2);

if (errors[0] == 0)

OPCITEMRESULT al.PtrToStructure(pos,

ItemServerHandle[0] = result.hServer;

}

if (errors[1] == 0)

pos = new IntPtr(pos.ToInt32() +

rshal.SizeOf(typeof(OPCITEMRESULT)));

LT result = (OPCITEMRESULT)Marshal.PtrToStructure(pos,

EMRESULT));

hServer;

rror) // catch for add items

or.Message, "Result - Adding Items",

MessageBoxButtons.OK, MessageBoxIcon.Error);

finally

if (pResults != IntPtr.Zero)

pResults =

rs);

}

catch (System.Exception

r while creating group object:-{0}",

ItemArray[1].vt

IntPtr pResults

IntPtr pErrors = IntPtr.Zer

{

int[] errors = new int

IntPtr pos = pResults;

ItemServerHandle = new int[2];

{

result = (OPCITEMRESULT)Marsh

typeof(OPCITEMRESULT));

{

Ma

OPCITEMRESU

typeof(OPCIT

ItemServerHandle[1] = result.

}

}

catch (System.Exception e

{

MessageBox.Show(err

}

{

// Free the memory

{

Marshal.FreeCoTaskMem(pResults);

IntPtr.Zero;

}

if (pErrors != IntPtr.Zero)

{

Marshal.FreeCoTaskMem(pErro

pErrors = IntPtr.Zero;

}

}

error) // catch for group adding

{

MessageBox.Show(String.Format("Erro

IA&DT Service & Support

Page 30-47

Page 31: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

error.Message), "Result - Add group", MessageBoxButtons.OK,

);

finally

band.Free();

eBias.Free();

{

MessageBox.Show(String.Format("Error while creating server object:-{0}",

,

tons.OK, MessageBoxIcon.Error);

}

ad_Click(object sender, EventArgs e)//同步读

IntPtr pItemValues = IntPtr.Zero;

Obj.Read(OPCDATASOURCE.OPC_DS_DEVICE, 2, ItemServerHandle, out

int[] errors = new int[2];

OPCITEMSTATE[] pItemState = n

emState[0] = (OPCITEMSTATE)Marshal.PtrToStructure(pItemValues,

emValues=new

peof(OPCITEMS

TATE)));

update the UI

Text = String.Format("{0}",pItemState.vDataValue);

_R1_Value.Text = String.Format("{0}", pItemState[0].vDataValue);

tQuality(pItemState[0].wQuality);

e[0].ftTimeStamp);

TimeStamp.Text = dt.ToString();

1] == 0)

ate[1]= (OPCITEMSTATE)Marshal.PtrToStructure(pItemValues,

lues.ToInt32()+

Marshal.SizeOf(typeof(OPCITEMSTATE)));

update the UI

Txt_R2_Value.Text = String.Format("{0}", pItemState[1].vDataValue);

y);

DateTime dt = ToDateTime(pItemState[1].ftTimeStamp);

MessageBoxIcon.Error

}

{

if (hDeadband.IsAllocated) hDead

if (hTimeBias.IsAllocated) hTim

}

}

catch (System.Exception error) // catch for server instance creation

error.Message), "Result - Create Server"

MessageBoxBut

}

private void Btn_Re

{

IntPtr pErrors = IntPtr.Zero;

try

{

IOPCSyncIO2

pItemValues, out pErrors);

Marshal.Copy(pErrors, errors, 0, 2);

ew OPCITEMSTATE[2];

if (errors[0] == 0)

{

pIt

typeof(OPCITEMSTATE));

pIt

IntPtr(pItemValues.ToInt32()+Marshal.SizeOf(ty

//

//txt_R1.

Txt

Txt_R1_Quality.Text = Ge

DateTime dt = ToDateTime(pItemStat

Txt_R1_

// quality

}

if (errors[

{

pItemSt

typeof(OPCITEMSTATE));

pItemValues = new IntPtr(pItemVa

//

Txt_R2_Quality.Text = GetQuality(pItemState[1].wQualit

Txt_R2_TimeStamp.Text = dt.ToString();

IA&DT Service & Support

Page 31-47

Page 32: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

// quality

}

.Exception error)

s", MessageBoxButtons.OK,

ally

// Free the unmanaged memory

{

Marshal.FreeCo

pItemValues = IntPtr.Zero;

}

if (pErrors != IntPtr.Zero)

{

pErrors = IntPtr.Zero;

{

1] = Txt_W2.Text;

{

2, ItemServerHandle, values, out pErrors);

int[] errors = new

Marshal.Copy(pErrors, errors, 0, 2);

or1);

System.Exception error)

sageBox.Show(error.Message, "Result - WriteItem", MessageBoxButtons.OK,

}

{

}

catch (System

{

MessageBox.Show(error.Message, "Result - Read Item

MessageBoxIcon.Error);

}

fin

{

if (pItemValues != IntPtr.Zero)

TaskMem(pItemValues);

Marshal.FreeCoTaskMem(pErrors);

}

}

}

private void Btn_Write_Click(object sender, EventArgs e)//同步写

IntPtr pErrors = IntPtr.Zero;

object[] values = new object[2];

values[0] = Txt_W1.Text;

values[

try

IOPCSyncIO2Obj.Write(

int[2];

String pstrError;

String pstrError1;

ServerObj.GetErrorString(errors[0], LOCALE_ID, out pstrError);

ServerObj.GetErrorString(errors[1], LOCALE_ID, out pstrErr

}

catch (

{

Mes

MessageBoxIcon.Error);

finally

if (pErrors != IntPtr.Zero)

{

Marshal.FreeCoTaskMem(pErrors);

pErrors = IntPtr.Zero;

}

IA&DT Service & Support

Page 32-47

Page 33: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

}

}

private String GetQuality(long wQuality)//质量码

String strQuality = ""

tch (wQuality)

case Qualities.OPC_QUALITY_GOOD:

e Qualities.OPC_QUALITY_BAD:

e Qualities.OPC_QUALITY_CONFIG_ERROR:

ationError";

break;

TED:

ected";

break;

case Qualities.OPC_QUALITY_DEVICE_FAILURE:

strQuality = "BadDeviceFailure";

break;

strQuality = "BadSensorFailure";

case Qualities.OPC_QUALITY_COMM_FAILURE:

re";

LITY_OUT_OF_SERVICE:

strQuality = "BadOutOfService";

break;

ForInitialData";

case Qualities.OPC_QUALITY_EGU_EXCEEDED:

ncertainEGUExceeded";

break;

default:

d";

break;

urn strQuality;

DateTime ToDateTime(OpcRcw.Da.FILETIME ft)

ghbuf = (long)ft.dwHighDateTime;

ateTime;

buffer);

{

;

swi

{

strQuality = "Good";

break;

cas

strQuality = "Bad";

break;

cas

strQuality = "BadConfigur

case Qualities.OPC_QUALITY_NOT_CONNEC

strQuality = "BadNotConn

case Qualities.OPC_QUALITY_SENSOR_FAILURE:

break;

strQuality = "BadCommFailu

break;

case Qualities.OPC_QUA

case Qualities.OPC_QUALITY_WAITING_FOR_INITIAL_DATA:

strQuality = "BadWaiting

break;

strQuality = "U

break;

case Qualities.OPC_QUALITY_SUB_NORMAL:

strQuality = "UncertainSubNormal";

strQuality = "Not handle

}

ret

}

private

{

long hi

long buffer = (highbuf << 32) + ft.dwLowD

return DateTime.FromFileTimeUtc(

}

IA&DT Service & Support

Page 33-47

Page 34: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

private void Btn_Disconn_Click(object sender, EventArgs e)//对象注销,断开连接

try

null)

Marshal.ReleaseComObject(IOPCSyncIO2Obj);

moveGroup(pSvrGroupHandle, 0);

up1 != null)

up1);

up1 = null;

p Server", MessageBoxButtons.OK,

{

{

if (IOPCSyncIO2Obj !=

{

IOPCSyncIO2Obj = null;

}

ServerObj.Re

if (IOPCGroupStateMgtObj != null)

{

Marshal.ReleaseComObject(IOPCGroupStateMgtObj);

IOPCGroupStateMgtObj = null;

}

if (MyobjGro

{

Marshal.ReleaseComObject(MyobjGro

MyobjGro

}

if (ServerObj != null)

{

Marshal.ReleaseComObject(ServerObj);

ServerObj = null;

}

}

catch (System.Exception error)

{

MessageBox.Show(error.Message, "Result - Sto

MessageBoxIcon.Error);

}

}

}

}

8.3 自定义接口异步读写

using System;

using System.Collections;

using System.Runtime.InteropServices;

using System.Drawing;

using System.ComponentModel;

using System.Windows.Forms;

System.Reflection; using

using System.Runtime.CompilerServices;

using System.Data;

w.Comn;//引用 using OpcRc

using OpcRcw.Da;//引用

namespace ASync_RW

{

IA&DT Service & Support

Page 34-47

Page 35: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

public partial class Form1 : Form, IOPCDataCallback

lic Form1()

tializeComponent();

Control.CheckForIllegalCrossThreadCalls = false;

ServerObj;//OPCServer

对象

pStateMgtObj = null;//组管理对象

= null;

jGroup1 = null;

upHandle = 0;

t sender, EventArgs e)

wRequestedUpdateRate = 1000;

Da.OPCITEMDEF[] ItemArray;

andle hTimeBias, hDeadband;

hTimeBias = GCHandle.Alloc(TimeBias, GCHandleType.Pinned);

hDeadband = GCHandle.Alloc(deadband, GCHandleType.Pinned);

Guid iidRequiredInterface = typeof(IOPCItemMgt).GUID;

try

nttyp = Type.GetTypeFromProgID("OPC.SimaticNet",

"192.168.0.102");//OPCServer

IOPCServer)Activator.CreateInstance(svrComponenttyp);

//注册

"MyOPCGroup1",//组对象

out pSvrGroupHandle,

out pRevUpdateRate,

ref iidRequiredInterface,

out MyobjGroup1);

{

pub

{

Ini

}

OpcRcw.Da.IOPCServer

OpcRcw.Da.IOPCAsyncIO2 IOPCAsyncIO2Obj = null;//异步读写

OpcRcw.Da.IOPCGroupStateMgt IOPCGrou

IConnectionPointContainer pIConnectionPointContainer

IConnectionPoint pIConnectionPoint = null;

internal const int LOCALE_ID = 0x407;

Object Myob

int[] ItemServerHandle;

int pSvrGro

Int32 dwCookie = 0;

private void Btn_Conn_Click(objec

{

Type svrComponenttyp;

Int32 d

Int32 hClientGroup = 1;

Int32 pRevUpdateRate;

OpcRcw.

float deadband = 0;

int TimeBias = 0;

GCH

{

svrCompone

ServerObj = (OpcRcw.Da.

try

{

ServerObj.AddGroup(

0,

dwRequestedUpdateRate,

hClientGroup,

hTimeBias.AddrOfPinnedObject(),

hDeadband.AddrOfPinnedObject(),

LOCALE_ID,

IA&DT Service & Support

Page 35-47

Page 36: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

IOPCAsyncIO2Obj = (IOPCAsyncIO2)MyobjGroup1;

//Query interface for Async calls on group object

tObj = (IOPCGroupStateMgt)MyobjGroup1;

pIConnectionPointContainer = (IConnectionPointContainer)MyobjGroup1;

调用连接

// Establish Callback for all async operations

iid, out

);

ween the OPC servers's connection point and

this client's sink (the callback object)

t.Advise(this, out dwCookie);

w OPCITEMDEF[4];//定义读写的item,共个变量

ItemArray[0].szItemID = "S7:[S7 connection_1]DB10,INT0";

//地址,不同数据类型表示方法不同

否激活

ent = 1;//表示ID

BlobSize = 0;

.Zero;

ItemArray[0].vtRequestedDataType = 2;

ItemArray[1].szAccessPath = "";

].szItemID = "S7:[S7 connection_1]DB10,Real4";

//地址,不同数据类型表示方法不同

;//是否激活

ItemArray[1].vtRequestedDataType = 5;

ItemA nection_1]DB10,STRING26.10";

ItemArray[2].bActive 否激活

temArray[2].hClient = 3;//表示ID

ItemArray[2].dwBlobSize = 0;

y[2].vtRequestedDataType = 8;

o;

tPtr.Zero;

temID = "S7:[S7 connection_1]DB10,X12.0";

数据类型表示方法不同

//是否激活

ID

= 0;

IOPCGroupStateMg

//定义特定组的异步

Guid iid = typeof(IOPCDataCallback).GUID;

pIConnectionPointContainer.FindConnectionPoint(ref

pIConnectionPoint

// Creates a connection bet

pIConnectionPoin

ItemArray = ne

ItemArray[0].szAccessPath = "";

ItemArray[0].bActive = 1;//是

ItemArray[0].hCli

ItemArray[0].dw

ItemArray[0].pBlob = IntPtr

ItemArray[1

ItemArray[1].bActive = 1

ItemArray[1].hClient = 2;//表示ID

ItemArray[1].dwBlobSize = 0;

ItemArray[1].pBlob = IntPtr.Zero;

ItemArray[2].szAccessPath = "";

rray[2].szItemID = "S7:[S7 con

//地址,不同数据类型表示方法不同

= 1;//是

I

ItemArray[2].pBlob = IntPtr.Zero;

ItemArra

IntPtr pResults = IntPtr.Zer

IntPtr pErrors = In

ItemArray[3].szAccessPath = "";

ItemArray[3].szI

//地址,不同

ItemArray[3].bActive = 1;

ItemArray[3].hClient = 4;//表示

ItemArray[3].dwBlobSize

IA&DT Service & Support

Page 36-47

Page 37: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

ItemArray[3].pBlob = IntPtr.Zero;

y

((OpcRcw.Da.IOPCItemMgt)MyobjGroup1).AddItems(4, ItemArray, out

int[] errors = new int[4];

Marshal.Copy(pErrors, errors, 0, 4);

if (errors[0] == 0)

OPCITE rToStructure(pos,

ItemServerHandle[0] = result.hServer;

if (errors[1] == 0)

EMRESULT)));

EMRESULT)Marshal.PtrToStructure(pos,

RESULT));

result.hServer;

{

t32() +

;

OPCITE rshal.PtrToStructure(pos,

SULT));

t.hServer;

pos = new IntPtr(pos.ToInt32() +

Of(typeof(OPCITEMRESULT)));

ure(pos,

hServer;

atch for add items

e, "Result - Adding Items",

Buttons.OK, MessageBoxIcon.Error);

}

// Free th

pResults);

ItemArray[3].vtRequestedDataType = 11;

tr

{

pResults, out pErrors);

IntPtr pos = pResults;

ItemServerHandle = new int[4];

{

MRESULT result = (OPCITEMRESULT)Marshal.Pt

typeof(OPCITEMRESULT));

}

{

pos = new IntPtr(pos.ToInt32() +

Marshal.SizeOf(typeof(OPCIT

OPCITEMRESULT result = (OPCIT

typeof(OPCITEM

ItemServerHandle[1] =

}

if (errors[2] == 0)

pos = new IntPtr(pos.ToIn

Marshal.SizeOf(typeof(OPCITEMRESULT)))

MRESULT result = (OPCITEMRESULT)Ma

typeof(OPCITEMRE

ItemServerHandle[2] = resul

}

if (errors[3] == 0)

{

Marshal.Size

OPCITEMRESULT result = (OPCITEMRESULT)Marshal.PtrToStruct

typeof(OPCITEMRESULT));

ItemServerHandle[3] = result.

}

}

catch (System.Exception error) // c

{

MessageBox.Show(error.Messag

MessageBox

finally

{

e memory

if (pResults != IntPtr.Zero)

{

Marshal.FreeCoTaskMem(

IA&DT Service & Support

Page 37-47

Page 38: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

pResults = IntPtr.Zero;

}

if (pErrors != IntPtr.Zero)

Marshal.FreeCoTaskMem(pErrors);

}

}

{

while creating group object:-{0}",

p", MessageBoxButtons.OK,

ror);

{

meBias.IsAllocated) hTimeBias.Free();

instance creation

{

error.Message r", MessageBoxButtons.OK,

private void Btn_Read_Click(objec

int nCancelid;

2Obj != null)

IOPCAsyncIO2Obj.Read( t pErrors);

Marshal.Copy(pErrors,

System.Exception error)

essageBox.Show(error.Message, "Error-Async Read", MessageBoxButtons.OK,

}

void OnReadComplete(System.Int32 dwTransid,//异步读完成

Mastererror,

{

pErrors = IntPtr.Zero;

}

catch (System.Exception error) // catch for group adding

MessageBox.Show(String.Format("Error

error.Message), "Result - Add grou

MessageBoxIcon.Er

}

finally

if (hDeadband.IsAllocated) hDeadband.Free();

if (hTi

}

}

catch (System.Exception error) // catch for server

MessageBox.Show(String.Format("Error while creating server object:-{0}",

), "Result - Create Serve

MessageBoxIcon.Error);

}

}

t sender, EventArgs e)//异步读

{

IntPtr pErrors = IntPtr.Zero;

if (IOPCAsyncIO

{

try

{

4, ItemServerHandle, 2, out nCancelid, ou

int[] errors = new int[4];

errors, 0, 4);

}

catch (

{

// M

MessageBoxIcon.Error);

}

}

public virtual

System.Int32 hGroup,

System.Int32 hrMasterquality,

System.Int32 hr

System.Int32 dwCount,

IA&DT Service & Support

Page 38-47

Page 39: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

int[] phClientItems,

object[] pvValues,//值

IME[] pftTimeStamps,//事件戳

string aa;

;

// Value

Txt_R1_V t("{0}", pvValues[0]);

]);

// Timestamp

DateTime dt = ToDateTime(pftTimeStamps[0]);

// .Net 2.0 ThreadExceptionDialog.CheckForIllegalCrossThreadCalls = true;

else

{

String strResult = "";

ServerObj.GetErrorString(pErrors[0], LOCALE_ID, out strResult);

MessageBox.Show(strResult, "Result - OnReadCOmpleate",

MessageBoxButtons.OK, MessageBoxIcon.Error);

if (pErrors[1] == 0)

tionDialog.CheckForIllegalCrossThreadCalls = false;

Txt_R2_Value.Text = String.Format("{0}", pvValues[1]);

lities[1]);

// Timestamp

DateTime dt = ToDateTime(pftTimeStamps[1]);

oString();

// .Net 2.0 ThreadExceptionDialog.CheckForIllegalCrossThreadCalls = true;

else

String strResult = "";

ServerObj.GetErrorString(pErrors[0], LOCALE_ID, out strResult);

MessageBoxButtons.OK, MessageBoxIcon.Error);

)

short[] pwQualities,//质量码

OpcRcw.Da.FILET

int[] pErrors)

{

try

{

if (pErrors[0] == 0)

{

// .Net 2.0 ThreadExceptionDialog.CheckForIllegalCrossThreadCalls = false

alue.Text = String.Forma

// txt_R4.Text = String.Format("{0}", pvValues[1]);

// Quality

Txt_R1_Quality.Text = GetQuality(pwQualities[0

Txt_R1_TimeStamp.Text = dt.ToString();

}

}

{

// .Net 2.0 ThreadExcep

// Value

// txt_R4.Text = String.Format("{0}", pvValues[1]);

// Quality

Txt_R2_Quality.Text = GetQuality(pwQua

Txt_R2_TimeStamp.Text = dt.T

}

{

MessageBox.Show(strResult, "Result - OnReadCOmpleate",

}

if (pErrors[2] == 0

IA&DT Service & Support

Page 39-47

Page 40: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

{

// .Net 2.0 ThreadExceptionDialog.CheckForIllegalCrossThreadCalls = false;

}", pvValues[2]);

R4.Text = String.Format("{0}", pvValues[1]);

// Quality

t = GetQuality(pwQualities[2]);

// Timestamp

= ToDateTime(pftTimeStamps[2]);

0 ThreadExceptionDialog.CheckForIllegalCrossThreadCalls = true;

{

String strResult = "";

tErrorString(pErrors[0], LOCALE_ID, out strResult);

e",

MessageBoxButtons.OK, MessageBoxIcon.Error);

.Net 2.0 ThreadExceptionDialog.CheckForIllegalCrossThreadCalls = false;

// Value

ng.Format("{0}", pvValues[3]);

// Quality

t = GetQuality(pwQualities[3]);

// Timestamp

eStamp.Text = dt.ToString();

readCalls = true;

else

{

sult = "";

strResult);

w(strResult, "Result - OnReadCOmpleate",

oxIcon.Error);

System.Exception exp)

sageBox.Show(exp.Message, "OnReadComplete-Runtime Error",

xButtons.OK, MessageBoxIcon.Error);

ual void OnCancelComplete(System.Int32 dwTransid, System.Int32 hGroup)

{

sample.

// Value

Txt_R3_Value.Text = String.Format("{0

// txt_

Txt_R3_Quality.Tex

DateTime dt

Txt_R3_TimeStamp.Text = dt.ToString();

// .Net 2.

}

else

ServerObj.Ge

MessageBox.Show(strResult, "Result - OnReadCOmpleat

}

if (pErrors[3] == 0)

{

//

Txt_R4_Value.Text = Stri

// txt_R4.Text = String.Format("{0}", pvValues[1]);

Txt_R4_Quality.Tex

DateTime dt = ToDateTime(pftTimeStamps[3]);

Txt_R4_Tim

// .Net 2.0 ThreadExceptionDialog.CheckForIllegalCrossTh

}

String strRe

ServerObj.GetErrorString(pErrors[0], LOCALE_ID, out

MessageBox.Sho

MessageBoxButtons.OK, MessageB

}

}

catch (

{

Mes

MessageBo

}

}

public virt

// Not implemented in this

IA&DT Service & Support

Page 40-47

Page 41: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

}

public virtual void OnDataChange(Int32 dwTransid,//订阅方式

Int32 hGroup,

Int32 hrMasterquality,

Int32 hrMastererror,

Int32 dwCount,

int[] phClientItems,

ftTimeStamps,

(int nCount = 0; nCount < dwCount; nCount++)

if (phClie

{

Txt_R1_Value.Text = Convert.ToString(pvValues[nCount]);

lity.Text = GetQuality(pwQualities[nCount]);

DateTime dt = ToDateTime(pftTimeStamps[nCount]);

Txt_R2_Value.Text = Convert.ToString(pvValues[nCount]);

Txt_R2_Quality.Text = GetQuality(pwQualities[nCount]);

Time dt = ToDateTime(pftTimeStamps[nCount]);

Txt_R3_Quality.Text = GetQuality(pwQualities[nCount]);

DateTime dt = ToDateTime(pftTimeStamps[nCount]);

Txt_R3_TimeStamp.Text = dt.ToString();

Txt_R4 ]);

Txt_R4_Quality.Text = GetQuality(pwQualities[nCount]);

DateTime dt = ToDateTime(pftTimeStamps[nCount]);

mp.Text = dt.ToString();

}

else

{

String strItemErr;

ServerObj.GetErrorString(pErrors[0], LOCALE_ID, out strItemErr);

uttons.OK, MessageBoxIcon.Error);

object[] pvValues,

short[] pwQualities,

OpcRcw.Da.FILETIME[] p

int[] pErrors)

{

try

{

for

{

if (pErrors[nCount] == 0)

{

ntItems[nCount] == 1)

Txt_R1_Qua

Txt_R1_TimeStamp.Text = dt.ToString();

}

if (phClientItems[nCount] == 2)

{

Date

Txt_R2_TimeStamp.Text = dt.ToString();

}

if (phClientItems[nCount] == 3)

{

Txt_R3_Value.Text = Convert.ToString(pvValues[nCount]);

}

if (phClientItems[nCount] == 4)

{

_Value.Text = Convert.ToString(pvValues[nCount

Txt_R4_TimeSta

}

//MessageBox.Show(strItemErr, "OnDataChange-Error",

MessageBoxB

}

IA&DT Service & Support

Page 41-47

Page 42: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

}

exp)

p.Message, "OnDataChange-Runtime Error",

MessageBoxButtons.OK, MessageBoxIcon.Error);

long wQuality)

y = "";

switch (wQuality)

case Qualities.OPC_QUALITY_GOOD:

strQuality = "Good";

e Qualities.OPC_QUALITY_BAD:

ak;

ity = "BadConfigurationError";

ies.OPC_QUALITY_DEVICE_FAILURE:

ity = "BadCommFailure";

ies.OPC_QUALITY_OUT_OF_SERVICE:

ity = "UncertainEGUExceeded";

Quality = "Not handled";

FILETIME ft)

}

catch (System.Exception

{

MessageBox.Show(ex

}

}

private String GetQuality(

{

String strQualit

{

break;

cas

strQuality = "Bad";

bre

case Qualities.OPC_QUALITY_CONFIG_ERROR:

strQual

break;

case Qualities.OPC_QUALITY_NOT_CONNECTED:

strQuality = "BadNotConnected";

break;

case Qualit

strQuality = "BadDeviceFailure";

break;

case Qualities.OPC_QUALITY_SENSOR_FAILURE:

strQuality = "BadSensorFailure";

break;

case Qualities.OPC_QUALITY_COMM_FAILURE:

strQual

break;

case Qualit

strQuality = "BadOutOfService";

break;

case Qualities.OPC_QUALITY_WAITING_FOR_INITIAL_DATA:

strQuality = "BadWaitingForInitialData";

break;

case Qualities.OPC_QUALITY_EGU_EXCEEDED:

strQual

break;

case Qualities.OPC_QUALITY_SUB_NORMAL:

strQuality = "UncertainSubNormal";

break;

default:

str

break;

}

return strQuality;

}

private DateTime ToDateTime(OpcRcw.Da.

{

IA&DT Service & Support

Page 42-47

Page 43: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

long highbuf = (long)ft.dwHighDateTime;

g buffer = (highbuf << 32) + ft.dwLowDateTime;

c(buffer);

private void Btn_Write_C

int nCancelid;

object[] values = new object[4];

Text;

ues[2] = "test";

cIO2Obj.Write(4, ItemServerHandle, values, 3, out nCancelid, out

em.Exception ex = new Exception("Error in reading item");

w ex;

m.Exception error)

, MessageBoxButtons.OK,

MessageBoxIcon.Error);

完成

stererr,

legalCrossThreadCalls = false;

adExceptionDialog.CheckForIllegalCrossThreadCalls = true;

lt = "";

2 = "";

ing strResult3 = "";

ring(pErrors[0], LOCALE_ID, out strResult);

ServerObj.GetErrorString(pErrors[1], LOCALE_ID, out strResult1);

out strResult2);

ServerObj.GetErrorString(pErrors[3], LOCALE_ID, out strResult3);

lon

return DateTime.FromFileTimeUt

}

lick(object sender, EventArgs e)//

{

IntPtr pErrors = IntPtr.Zero;

values[0] = Txt_W1.Text;

values[1] = Txt_W2.

val

values[3] = 1;

if (IOPCAsyncIO2Obj != null)

{

try

{

IOPCAsyn

pErrors);

int[] errors = new int[4];

Marshal.Copy(pErrors, errors, 0, 4);

if (errors[0] != 0 || errors[1] != 0)

{

Syst

Marshal.FreeCoTaskMem(pErrors);

pErrors = IntPtr.Zero;

thro

}

}

catch (Syste

{

MessageBox.Show(error.Message, "Result-Async Read"

}

}

}

public virtual void OnWriteComplete(System.Int32 dwTransid,//写

System.Int32 hGroup,

System.Int32 hrMa

System.Int32 dwCount,

int[] pClienthandles,

int[] pErrors)

{

// .Net 2.0 ThreadExceptionDialog.CheckForIl

// .Net 2.0 Thre

String strResu

String strResult1 = "";

String strResult

Str

ServerObj.GetErrorSt

ServerObj.GetErrorString(pErrors[2], LOCALE_ID,

IA&DT Service & Support

Page 43-47

Page 44: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

Txt_WriteStatus1.Text = strResult;

Txt_WriteStatus2.Text = strResult1;

}

private void CHK_Btn_CheckedChanged(object sender, EventArgs e)

IntPtr pRequestedUpdateRate = IntPtr.Zero;

te = 0;

ro;

;

oup according to checkbox status

andle hActive = GCHandle.Alloc(nActive, GCHandleType.Pinned);

tn.Checked != true)

tive.Target = 0;

hActive.Target =

ateRate, out nRevUpdateRate,

hActive.AddrOfPinnedObject(), pTimeBias, pDeadband, pLCID,

Box.Show(error.Message, "Result-Change Group State",

MessageBoxButtons.OK, MessageBoxIcon.Error);

hActive.Free();

断开连接

cked = false;

if (dwCookie != 0)

e

ect(pIConnectionPoint);

pIConnectionPoint = null;

{

int nRevUpdateRa

IntPtr hClientGroup = IntPtr.Ze

IntPtr pTimeBias = IntPtr.Zero;

IntPtr pDeadband = IntPtr.Zero;

IntPtr pLCID = IntPtr.Zero

int nActive = 0;

// activates or deactivates gr

GCH

if (CHK_B

hAc

else

1;

try

{

IOPCGroupStateMgtObj.SetState(pRequestedUpd

hClientGroup);

}

catch (System.Exception error)

{

Message

}

finally

{

}

}

private void Btn_Disconn_Click(object sender, EventArgs e)//释放对象及

{

try

{

CHK_Btn.Che

{

pIConnectionPoint.Unadvise(dwCookie);

dwCookie = 0;

}

// Free unmanaged cod

Marshal.ReleaseComObj

Marshal.ReleaseComObject(pIConnectionPointContainer);

pIConnectionPointContainer = null;

IA&DT Service & Support

Page 44-47

Page 45: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

if (IOPCAsyncIO2Obj != null)

{

Marshal.ReleaseComObject(IOPCAsyncIO2Obj);

IOPCAsyncIO2Obj = null;

);

Obj != null)

IOPCGroupStateMgtObj);

ll;

1 != null)

{

)

mObject(ServerObj);

ServerObj = null;

}

catch (System.

{

MessageBox.Show(error.Message, "Result - Stop Server", MessageBoxButtons.OK,

n.Error);

}

}

ServerObj.RemoveGroup(pSvrGroupHandle, 0

if (IOPCGroupStateMgt

{

Marshal.ReleaseComObject(

IOPCGroupStateMgtObj = nu

}

if (MyobjGroup

Marshal.ReleaseComObject(MyobjGroup1);

MyobjGroup1 = null;

}

if (ServerObj != null

{

Marshal.ReleaseCo

}

Exception error)

MessageBoxIco

}

}

}

IA&DT Service & Support

Page 45-47

Page 46: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

附录-推荐网址 自动化系统 西门子(中国)有限公司 工业自动化与驱动技术集团 客户服务与支持中心 网站首页:www.4008104288.com.cn自动化系统 下载中心:http://www.ad.siemens.com.cn/download/DocList.aspx?TypeId=0&CatFirst=1

术资源:045/130000

自动化系统 全球技http://support.automation.siemens.com/CN/view/zh/10805

ens.com.cn/service/answer/category.asp?cid=1027“找答案”自动化系统版区:http://www.ad.siem 通信/网络 西门子(中国)有限公司

w.4008104288.com.cn

工业自动化与驱动技术集团 客户服务与支持中心

网站首页:ww通信/网络 下载中心:

n/download/DocList.asphttp://www.ad.siemens.com.c x?TypeId=0&CatFirst=12 技术资源:

ort.automation.siemens.com/CN/view/zh/10805868/130000通信/网络 全球http://supp

案”Net版区:tp://www.ad.siemens.com.cn/service/answer/category.asp?cid=1031

“找答ht

IA&DT Service & Support

Page 46-47

Page 47: 如何在C#中实现OPC数据访问 - ad.siemens.com.cn · 自定义接口是一组com 接口,主要用于采用c++语言的应用程序开发; 自动化接口是一组OLE 接口,主要用于采用VB,DELPHI,Excel

注意事项 应用示例与所示电路、设备及任何可能结果没有必然联系,并不完全相关。应用示例不表示

户的具体解决方案。它们仅对典型应用提供支持。用户负责确保所述产品的正确使用。这

能免除用户在确保安全、专业使用、安装、操作和维护设备方面的责任。当使

到西门子不对在所述责任条款范围之外的任何损坏/索赔承担责

另行通知。如果这些应用示例与其它西门

,则以其它文档的内容为准。

些应用示例不

用这些应用示例时,应意识

任。我们保留随时修改这些应用示例的权利,恕不

子出版物(例如,目录)给出的建议不同 声明 我们已核对过本手册的内容与所

保证完全一致。我们会经常对手册中的数据进行检查,并在后续的版本中

描述的硬件和软件相符。由于差错难以完全避免,我们不能

进行必要的更正。

迎您提出宝贵意见。

西门子(中国)有限公司 2001-2008 版权保留

必须经过权利人书面明确同意。侵权者将承担权利人

留一切权利,包括复制、发行,以及改编、汇编的权利。

西门子(中国)有限公司

版权©

复制、传播或者使用该文件或文件内容

的全部损失。权利人保

IA&DT Service & Support

Page 47-47