您的当前位置:首页正文

.Net平台的OPC DA客户端开发

2023-10-04 来源:九壹网
.Net平台的OPC DA客户端开发曹红萍。等 .Net平台的OPC DA客户端开发 Development of the O PC DA Cl ient on.Net PfatfOrm 暂 住 山 邹至乒 孟 巍 (上海海得控制系统股份有限公司,上海200233) 摘要:针对基于COM的OPC服务器信息的复杂性和不透明性,调用OPC基金会发布的程序集,快速实现了.Net平台的客户端程 序。通过对平台互操作原理、COM组件对象与.Net类对象差异的比较,解析了程序集中对服务器接口数据进行重新封送、重组的必 要性,并结合OPC规范,程序集结构逐步实现了对数据服务器的访问。试验结果表明,基于程序集的客户端开发对系统集成及信息共 享起到了极大的作用。 关键词:.Net程序集中图分类号:TP274 数据访问服务器接口指针OPC客户端监控系统 文献标志码:A Abstract:Aiming at the complexity and the opacity of information from OPC server based on COM,the client program of.Net platform is implemented quickly by calling the assembly issued by OPC foundation.Based on interoperable principle of platform and the comparison of the difference between the COM component object and the.Net object,the necessity to re'marshal and restructure the server interface data in assembly is analyzed,and OPC specification and assembly stucture arre combined for implementing access to the data server gradually.The experiments result show that the development of client based on assembly plays a vital role in system integration and iformatnion sharing. Keywords:.Net Assembly Data access server Interface pointer OPC client Monitoring system 0 引言 基于COM的OPC组件在过去几年里被广泛应用 和错误处理机制上的差异,.Net框架提供了运行库 可调用包装RCW(runtime callable wrapper)隐藏这些 差异。RCW的主要功能是代表被包装的COM对象 封送在托管和非托管代码之间传递的数据,并向 .于工业控制领域,并被集成到众多监控系统中作为信 息服务器。随着.Net Framework的发展,.Net逐渐成 Net客户端公开服务器接口的方法、属性、事件。当 为架构、运行网络服务等应用程序的开发平台。在实 现基于Windows通信接口(Windows communication fundation,WCF)的OPC uA组件前,如何在.Net中实 现OPC客户端访问基于COM的OPC服务器,如常用 的OPC数据访问服务器(data access,DA)成为.Net程 .Net客户端激活COM对象时,运行库根据元数据生 成一个RCW实例来包装COM类型。RCW内保存 COM对象的接口指针缓存,并维护引用计数。断开 服务器时,释放对COM对象的引用,运行库执行对 RCW的垃圾回收。 .序急需解决的问题。OPC基金会发布的.Net API程序 集实现了访问OPC DA的相关托管类,可以快速实现 Net OPC客户端。本文从COM包装、.Net API结构等 方面介绍了如何在.Net平台下实现OPC客户端(案例 基于C#),并调用OPC DA服务器。 Net类库的System.Runtime.InteropServices命名 空间提供多种平台互调用服务的成员,实现平台的互 操作,控制RCW的封送行为。常用的类如System. Runtime.InteropServices.Marshal,用于分配、释放非托 管内存、复制非托管内存块、将托管类型转换为非托管 类型 ],也可用于显式地释放与COM对象关联的 RCW计数等。 1 CoM包装 1.1平台互操作 基于COM的OPC DA是非托管对象,它与.Net 1.2 oPC接口元数据 平台的托管对象模型之间存在数据类型、方法签名 修改稿收到日期:2011—10—24。 根据OPC规范可知,对服务器的所有操作都基于 COM接口,.Net客户端首先需要获取接口元数据调用 服务器。当OPC DA服务器带有类型库( .tlb)时, .第一作者曹红萍(1979一),女,2006年毕业于中国科学院上海应用 物理研究所,获博士学位,工程师;主要从事OPC数据访问服务器的开发 和研究。 Net运行库使用Tlblmp.exe(类型库导入程序)或添加 类型库引用的方式,由.Net平台根据类型库自动为类 2012年9月 45 《自动化仪表》第33卷第9期.Net平台的OPC DA客户端开发曹红萍,等 //显式减少COM对象的引用计数,断开OPC DA 型信息创建互操作程序集和命名空间,并输出二进制 文件(程序集)。该文件中包含原始类型库中定义的 类型运行库元数据。当OPC DA服务器没有类型库 时,需参考OPC接13规范,实现元数据文件。根据类 上述的应用只能访问本机的OPC DA,不能访问局 域网内远程机器上的服务器。在类似COM环境中调用 CoCreateInstanceEx函数创建远程OPC DA对象 实例, 获取接口指针的方法访问服务器。CoCreateInstanceEx 函数来自ole32.dll,从托管代码中访问非托管DLL的函 型库生成的元数据有时并不能完全反映接口信息,进 而导致封送处理失败。下面介绍两种典型情况。 ①变长数组参数 在OPC DA的接口文件IDL中,对接口IOPCItemMgt 的Addltems方法定义如下: HRESULT Addltems([in]DWORD dwCount, [in,size—is(dwCount)]OPCITEMDEF pItemArray, [out,size—is(,dwCount)]OPCITEMRESULT ppAddResults, 数,在.Net中用Dlllmport标志DLL和函数,其语句 如下: [Dlllmport(”ole32.dll”)] private static extern void CoCreateInstanceEx(…) 在COM环境中,客户端调用Querylnterface方法获取 服务器的接口指针。在.Net中通过两种方法获得接口指 针。①强制类型转换,如查询m—server是否支持 ppErrors) [out,size—is(,dwCount)]HRESULT IOPCServer接口:((IOPCServer)m_server).RemoveGmup(); 其中phemArray表示含有dwCount个元素的数组指 针。在.Net平台下,导入程序对该参数默认解释为整数 的引用IntPtr,而不是希望的托管数组。原因是Microsoft 接口定义语言(MIDL)编译器编译OPC DA的IDL文件 ②调用System.Type.IsInstanceOtType方法,语句为: if(!typeof(OpcRcw.Da.IOPCServer).IsInstanceOfFype (m_server))。这两种方法的结果一样,后台都会通过 RCW调用IUnknown一>QueryInterface检查OPC DA对 时不会将size_is信息 传播到类型库。此时需要重新定 义MarshalAs的数组元素大小。参数pltemArray修改为: [In,MarshalAs(UnmanagedType.LPArray,ArraySubType= 象是否支持该接口。 由上文分析可知,OPC DA接口的.Net定义非常 耗时,且需对OPC规范或COM编程比较熟悉。 UnmanagedType. Struet, SizeParamIndex =0)] .OPCITEMDEF[]pItemArray。客户端程序接收或发送数 据时,再调用Marshal类中的方法,根据数组的大小手动 分配或释放内存 。 鉴于此,OPC基金会提供.Net API程序集,使 Net客户端程序无需关心COM环境到.Net的封装、 接口定义的细节以及调用远程或本机的OPC DA服 ②COM接口返回结果 OPC DA的接口方法以HRESULT作为返回值类 型,类型库导入程序对返回值一般默认为void。运行 时.Net自动忽略返回S—OK(即0)的结果,且不会发 生异常。对于指示失败的HRESULT值都将引发与 HRESULT对应的异常。如果HRESULT值是用户自 务器的差别。.Net API程序集是一个封装器,主要具 有两个功能:①定义与OPC DA接口同等意义的元数 据;②封送处理接口的复杂数据类型 。程序集的 组成框图如图1所示。 I .NetoPc客户端(托管) I ● 定义或对于运行库是未知的,运行库向客户端传递 一Il C  OPDA对象的・Net封装 ll c OpNetApi・dli 个COMException。OPC DA中一些接口方法返回 l c0M接口方法的调用 l 的HRESULT值有多种解释时,托管客户端应在程序 集中设置接口方法的PreserveSig属性为真,并设置 方法的返回值类型为int 。PreserveSig字段为真 时,C#可以直接转换为带有HRESULT或retval值的 非托管签名。 1.3 OPC DA访问 I ̄躲IC误返回的处理I I圳 I I ・ et元数据定义 l : :二 : Il 包装 I .Net互操作类 ● I oPc数据服务器(j}托管) I 图1程序集组成框图 Fig.1 Architecture and assembly 生成有效的元数据文件后,添加引用到.Net客户 端程序,生成互操作类。访问OPC DA的方法如下。 XXXClass mServer=new XXXClass() —//XXXClass是.Net平台自动生成的操作类名称 … 图1中各模块的功能如下。 //调用m—Server的接口方法 ①OpcRcw.Da、OpcRcw.Com实现了OPC DA接 口、OPC常量、数据类型的.Net定义,使RCW能正确 Marsha1.ReleaseComObject(m—Server): 46 PROCESS AUTOMATION INSTRUMENTATION Vo1.33 No.9 September 2012 .Net平台的OPC DA客户端开发曹红萍。等 地封送数据。 安装OPC基金会的OPC Core Components Redistributable以后,在安装文件夹下可以找到 OPCEnum.exe。.Net API调用OPCEnum内的 IOPCServerList2接口浏览本地或者远程计算机上注册过 ②OpcNetApi.com实现了调用OPC DA各个接口 的托管类,包括factory(类厂)、Server、group、item。其 中OpcCom.Interop和OpcCom.Da.Interop这两个类实 现OPC DA对象的创建、释放及COM对象引用计数的 维护,解析相关接口的数据类型,处理错误的接口返 的OPC DA的PmglD及CLSID,语句如下: Opc.IDiscovery m—discovery new OpcCom. 回值。 ③OpcNetApi定义了OpcNetApi.COB相关类的基 类,.Net客户端程序直接调用。 至此,.Net OPC客户端程序可以像处理其他托管 类一样调用OPC DA的方法。下面简要介绍基于.Net API的OPC客户端的实现。 2.Net API的应用 .Net API实现了OPC报警、历史数据规范的.Net 封装,本文只介绍OPC规范中比较常用的数据服务器 的访问。添加OpcNetApi.dll和OpcNetApi.Com.dll这 两个引用到客户端程序,调用COM组件的过程严格遵 循COM对象的创建和使用。.Net API的主要托管类流 程如图2所示。 l 浏览OPCDA  l创建类厂  I连接server l 创建Group  l浏览Item地址空间 l I添加Item  『产生数据变化事件 图2.Net API主要托管类流程图 Fig.2 Flowchart of main managed classes of.Net API 下面依次介绍.Net客户端对OPC DA的操作 过程。 2.1 OPC DA的浏览 基于.Net API的客户端,浏览本机或远程机器上 的OPC DA时,需在服务器上启用OPCEnum.exe进 程。OPCEnum是由ATL开发的一个Windows服务,主 要作用是浏览本地或者远程计算机上OPC组件的 ProgID(程序标志符)。 《自动化仪表》第33卷第9期2012年9月 ServerEnumerator() 搜索所有遵循COM—DA一20规范的OPC DA服务 器代码为: Opc.Server[]servers=m—discovery.GetAvailableServers (Specification.COM—DA一20,”localhost”,nul1) 其中,localhost为服务器名,null或localhost表示本 机或指定远程机器名(调用前需配置服务器的DCOM访 问权限)。GetAvailableServers为每个浏览到的OPC DA 创建一个类厂OpcCom.Factory和Opc.Server实例 对象。 2.2 oPC DA的连接及断开 获得OPC DA的ProgID后,类厂调用OpcCom. 1nterop.CoCreateInstanceEx创建OPC DA对象,获得 OPC DA对象的IOPCServer、IOPCItemProperties等接口 指针,并在OpcNetApiCom.dll中创建一个OpcCom. Da20.Server类实例。 Opc.Da.Server server=(Opc.Da.Sevrer)servers[0] //连接上述浏览到的第一个OPC DA if(!sevrer.IsConnected) server.Connect() 当断开server时,调用server.Disconnect()。后台 OpcNetApi.com将首先删除变量组及组内变量,然后 调用Marsha1.ReleaseCom0bject(server),递减运行库 可调用包装的引用计数,并断开OPC DA服务器。 2.3变量组的增加及删除 成功连接OPC DA后,可以添加变量组group到 server,.Net API中的变量组称为Subscription。成功添 加变量组后,在OpcNetApiCom.dll中创建一个OpcCom. Da20.Subscription类实例。 SubscriptionState state=new Ope.Da.Subscripti0nState() state.Name=“Group1” //组名 state.Active=false //建议添加Item(数据项)后再使能变量组 Subscription group=(Opc.Da.Subscription)server. CreateSubscription(state) NetApi.du的Opc.Da命名空间中定义了public delegate void DataChangedEVentHandler,在 Opc.Da. ISubscription中定义了相关事件,即:event 47 .Net平台的OPC DA客户端开发曹红萍,等 地址的定义。规范中IOPCBrowseServerAddressSpace接 DataChangedEventHandler DataChanged。 if(group!=nul1) //如果添加组成功 口属于Server对象。连接OPC DA对象后,可以轻易获 得IOPCBrowseServerAddressSpace接口对象,无需先创建 变量组。调用serveF的Browse方法浏览变量地址及数 group.DataChanged=this.OnDataChange 删除组时,先移除组内的所有Items,再调用server. CancelSubscription(subscription)删除组。.Net客户端无 需关心引用计数及资源的释放。 group.Removeltems(group.Items) //删除变量 据类型等属性,返回BrowseElement数组对象。每个 BrowseElement元素包含节点的地址路径、地址名。调 用BrowseElement.Ishem判断是否是最底层节点变量, server.CancelSubscription(group) //删除变量组 2.4数据项的增加及删除 可以指定地址空间中任一字符串名作为开始浏览的节 点。其语句如下: if(itemname!=””) //指定开始浏览的地址 ItemIdentiifer itemID = new ItemIdentiifer(null, itemname) 创建了变量组后,调用group.Addhems批量添加 数据项到该组。一次添加9 999个数据项的语句具体 如下: string[]itemhandles=new string[9 999] //.Net AP1中数据项的句柄存储为GUID字符串 BrowseElement[]elements=server.Browse(itemID,m— iflters,out position) ItemNode[]node=new hemNode[9 999] f0r(int i=0;i<9 999;i++) … 3 结束语 通过分析.Net平台与COM平台的互操作性以及 OPC DA服务器接口的.Net封装过程,说明了.Net平 台下OPC客户端访问OPC DA服务器的原理。.Net //初始化node的每一个数据项 //获得数据项句柄 itemhandles=comgroup.Addhems(m—grouphandlel, node) OpcNetApi.Com.dU中添加数据项的处理过程具 体如下。 ①((IOPCItemMgt)m—group).AddItems(count, definitions,out pResults,out pErrors); API程序集封装了OPC DA的接口调用和资源的重新 分配,隐藏了访问COM组件中接口的引用计数、接口 释放等复杂规范,使不熟悉COM和OPC操作的.Net 用户能尽快实现对OPC DA服务器的访问 具有非常大的实用价值。 参考文献 [1]Microsoft.Marshal成员[EB/OL].[2011一ll—O1].http://msdn. microsoft.com/zh—cn/library/system.mntime.interopservices.marshal②解析pResults,pErrors; ③分析pErrors返回结果,把添加成功的数据项 添加到Subscription.m—items链表。 ,这对 工业自动化领域的数据采集、系统集成、系统兼容方面 由上述添加数据项的过程可知,在增加变量组时, 如果使能state.Active,数据项数目多时,会出现已经添 加到服务器的变量触发第一次变量变化回调时,客户 端的group.Addhems(items)方法还没返回。此时.Net 程序的回调函数中会因为数据项的ServerHandle为 空,导致刷新变量信息失败。 members(VS.95).aspx. [2]OPC基金会.OPC and.Net version 0.2[R].2001. [3]VISCOM.Net team.OPC and.Net with COM Interoperability[EB/ OL].[201 1—1 1一O1].http://www.codepr ̄ect.com/KB/COM/ opcdotnet.aspx. 为修复这种现象,可以采取以下两种方法:①修改 OpcNetApi.Com.dll的AddItems方法,先添加items到 [4]Microsoft.PreserveSig字段[EB/OL].[2011—11—01].http:// msdn.microsoft.com/zh—cn/library/system.runtime.interopservices. dllimportattribute.preservesig.aspx. 客户端的Subscription.m—items链表,再调用OPC DA 接口((IOPChemMgt)m—group).Addhems添加数据项, 根据pErrors结果,从Subscription.m—items中删除添加 失败的数据项;②添加完所有的数据项后,再使能变量 组的Active属性。 [5]Advosol Inc.OPC.Net Wrappers[R].2007. [6]潘爱民.COM原理与应用[M].北京:清华大学出版社,1999. [7]OPC基金会.OPC data access custom interface speciifcation 2.05『S1. 2001:4—66. 调用group.Removehems(new hemldentifier[] {item}),删除数据项。 2.5地址空间的浏览 根据OPC 2.05规范,IOPCBrowseServerAddressSpace [8]马龙华,刘俊,钱积新.OPC数据存取规范的研究和应用[J].化工自 动化及仪表,2O02(1):45—47. [9]成功,杨佃福,阳宪惠,等.OPC技术应用初探[J].计算机工程, 2002(2):8. 接口为可选 ,但一般OPC DA都会实现该接口,以使 OPC客户端可以获得OPC DA的地址结构,更便于变量 48 [1O]赵艳秋,王建民.采用OPC技术实现竖炉烧结DCS系统与VB 软件的接口[J].计算机与数字工程,2006(34):114—1l7. PROCESS AUTOMATION INSTRUMENTATION Vo1.33 No.9 September 2012 

因篇幅问题不能全部显示,请点此查看更多更全内容