Upload
others
View
19
Download
0
Embed Size (px)
Citation preview
如何在 C#中实现 OPC 数据访问 How to accomplish data accessing through OPC by C#
Getting-started Edition (2009 年 03 月)
摘 要 本文主要讲述了在 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
如何在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
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
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
订阅方式时,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 自定义接口和自动化接口
许多 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 端组态配置
在 CPU 中定义 DB 块:DB10
配置 PC Station,参考
其它文档。
如上图建立连接 S7_connection_1,然后在 OPC Scout 测试连接的正确性。
IA&DT Service & Support
Page 8-47
从上面可以看到数据访问都是正常的。
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
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
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
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
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
第 添加下面命名空间:(首先需要在一步, 项目中添加相应的引用)
;//定义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
……
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
问题 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
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
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
}
实例参考
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
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
第
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
……
}
第七步异步读
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
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
{
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
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
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
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
{
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
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
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
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
// 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
}
}
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
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
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
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
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
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
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
{
// .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
}
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
}
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
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
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
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
附录-推荐网址 自动化系统 西门子(中国)有限公司 工业自动化与驱动技术集团 客户服务与支持中心 网站首页: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
注意事项 应用示例与所示电路、设备及任何可能结果没有必然联系,并不完全相关。应用示例不表示
户的具体解决方案。它们仅对典型应用提供支持。用户负责确保所述产品的正确使用。这
能免除用户在确保安全、专业使用、安装、操作和维护设备方面的责任。当使
到西门子不对在所述责任条款范围之外的任何损坏/索赔承担责
另行通知。如果这些应用示例与其它西门
,则以其它文档的内容为准。
客
些应用示例不
用这些应用示例时,应意识
任。我们保留随时修改这些应用示例的权利,恕不
子出版物(例如,目录)给出的建议不同 声明 我们已核对过本手册的内容与所
保证完全一致。我们会经常对手册中的数据进行检查,并在后续的版本中
描述的硬件和软件相符。由于差错难以完全避免,我们不能
进行必要的更正。
迎您提出宝贵意见。
西门子(中国)有限公司 2001-2008 版权保留
必须经过权利人书面明确同意。侵权者将承担权利人
留一切权利,包括复制、发行,以及改编、汇编的权利。
西门子(中国)有限公司
欢
版权©
复制、传播或者使用该文件或文件内容
的全部损失。权利人保
IA&DT Service & Support
Page 47-47