1. 前言 在企业"管控一体化"的信息系统建设中,实时信息系统起着非常重要的作用。它可以采集控制系统中的实时数据,使管理者在办公室里就可以监测到生产现场的生产情况及报警信息;并可以对取得的实时数据进行优化、分析,从而为保证生产设备正常运转、降低成本提供重要基础。作为制造执行系统,MES必然要求与企业其它生产管理系统有密切关系,MES在其中起到了信息集线器(Information Hub)的作用,它相当于一个通讯工具为其它应用系统提供生产现场的实时数据。
作为信息集线器的MES要将来自不同硬件生产厂商的控制系统的现场数据汇总在实时数据库中,必须要和不同的设备,比如PLC,变频器,现场总线的仪表等通讯,如果不同的设备厂家都遵守一个相同的程序接口标准的话,那么程序和不同设备的沟通将变得非常容易。OPC 就是这样一种工业标准,它是OLE for process control 的英文缩写。OPC 是基于微软的COM(Component Object Model)和OLE(Object Linking and Embedding)技术之上的。和以前不同的是,现在设备厂家提供不同的OPC Server。OPC Server负责从设备中取数据和写数据。用户所要做的就是利用统一的COM 规范编写OPC Client。客户程序和OPC Server 打交道。OPC Server是一座在客户和硬件设备之间的桥梁,通过它,我们可以很容易的取得现场的温度,压力,流量,位置等信号,以及控制现场的阀门开度,电机转速等。注意客户程序和服务器程序可以在同一台计算机上,也可以在不同的计算机上,区别是使用COM 还是使用DCOM。
2. 项目背景 我们开发的OPC服务器的项目背景是贵铝热电厂的数据上网项目。此项目中的一个子项目需要将现场的一些重要辅机的温度值上传至热电厂的热网之中。在数据的接口上我们选择使用OPC作为统一的数据接口,因此我们在辅机温度巡检系统中需要实际开发其相应的数据访问服务器,用于和实时数据库服务器通讯以便于将数据上传至热电厂的热网之中。以下我以此项目为例来介绍OPC数据访问服务器的开发过程。服务器的开发工具通常是在VC6环境中使用MFC类库搭配ATL,我选择了Delphi7。因为Delphi是功能强大的应用程序开发工具。它具有功能强大,运行速度快,易于学习和使用以及开发效率高等特点。它是可视化应用编程环境,可重用性面向对象编程语言,快速编译器和数据库的完美组合。编写OPC定制接口的客户程序的本质就是编写COM客户程序,而使用Delphi进行COM开发时,我们会发现Object Pascal为COM提供了强大的语言支持。

图表 1 辅机温度巡检网络连接图
3. OPC服务器的结构 下面首先给出OPC服务器的内部结构。

图表 2 OPC服务器的内部结构 OPC规范提供了两套接口方案,即COM接口和自动化。COM接口效率高,通过该接口,客户能够发挥OPC服务器的最佳性能,采用C++语言的客户一般采用COM接口方案;自动化接口一般为采用VB语言的客户所采用。自动化接口使解释性语言和宏语言编写客户应用程序变得简单,然而自动化客户运行时需进行类型检查,这一点则大大牺牲了程序的运行速度。OPC服务器必须实现COM接口,是否实现自动化接口则取决于供应商的主观意愿。通常可以直接使用OPC基金会提供的OPC自动化包装DLL(OPCDAAuto.DLL),将OPC定制接口变换成OPC自动化接口。当然也可以自己编制自动化包装器。

图表 3 OPC服务器通过自动化包装器提供OPC自动化接口
4. OPC服务器对象模型 OPC服务器由三类对象组成,相当于三种层次上的接口:服务器(Server)、组(Group) 和数据项(Item)。
1) 服务器对象(Server) 拥有服务器的所有信息,同时也是组对象(Group)的容器,一个服务器对应于一个OPC Server,即一种设备的驱动程序。在一个Server中,可以有若干个组。
2) 组对象(Group) 拥有本组的所有信息,同时包容并逻辑组织OPC数据项(Item)。
OPC组对象(Group)提供了客户组织数据的一种方法,组是应用程序组织数据的一个单位。客户可对之进行读写,还可设置客户端的数据更新速率。当服务器缓冲区内数据发生改变时,OPC将向客户发出通知,客户得到通知后再进行必要的处理,而无需浪费大量的时间进行查询。OPC规范定义了两种组对象:公共组(或称:全局组,public)和局部组(或称:局域组、私有组,Local)。公共组由多个客户共有,局部组只隶属于一个OPC客户。全局组对所有连接在服务器上的应用程序都有效,而局域组只能对建立它的Client有效。一般说来,客户和服务器的一对连接只需要定义一个组对象。在一个组中,可以有若干个项。
3) 项 项是读写数据的最小逻辑单位。一个项与一个具体的位号相连。项不能独立于组存在,必须隶属于某一个组。在每个组对象中,客户可以加入多个OPC数据项(Item)。OPC数据项是服务器端定义的对象,通常指向设备的一个寄存器单元。OPC客户对设备寄存器的操作都是通过其数据项来完成的,通过定义数据项,OPC规范尽可能的隐藏了设备的特殊信息,也使OPC服务器的通用性大大增强。OPC数据项并不提供对外接口,客户不能直接对之进行操作,所有操作都是通过组对象进行的。每个数据项的数据结构包括三个成员变量:数据值、数据质量和时间戳。数据值是以VARIANT形式表示的。应当注意,数据项表示同数据源的连接而不等同于数据源,无论客户是否定义数据项,数据源都是客观存在的。可以把数据项看作数据源的地址,即数据源的引用,而不应看作数据源本身。组与项的关系如下图所示:

图表 4 OPC服务器的对象模型
应用程序作为OPC接口中的Client方,硬件驱动程序作为OPC接口中的Server方。每一个OPC Client应用程序都可以接若干个OPC Server,每一个硬件驱动程序可以为若干个应用程序提供数据。
5. OPC服务器对象接口 OPC服务器是通过接口向客户提供服务的,接口实际上是一组相关函数的集合。
(1) OPCServer对象接口 OPCServer对象是OPC中的首要对象,它提供了如下接口: IUnknown接口是COM的标准接口 IOPCServer接口可对OPCGroup对象进行有关操作 IOPCServerPublicGroups接口为客户和服务器提供了管理公共组的功能 IOPCBrowseServerAddressSpace接口提供了客户浏览服务器数据项的功能 IOPCItemProperties接口让客户能够浏览与ItemID相关的可访问属性 IOPCCommon接口提供了设置和询问LocaleID的功能 IPersistFile接口允许客户装载和保存服务器的配置信息 IConnectionPointContainer接口允许用户探查发现连接点

图表 5 OPCServer对象接口图 (2) OPCGroup对象接口 OPCGroup对象是管理数据项集合的对象,它提供的接口如下: IUnknown接口是COM的标准接口 IOPCItemMgt接口为客户提供了添加,删除和控制组中数据项的功能 IOPCGroupStateMgt接口允许客户管理组中的所有状态信息 IOPCPublicGroupStateMgt接口用来将私有组转换为公共组 IOPCSyncIO接口允许用户对服务器执行同步读写操作 IOPCAsyncIO接口允许客户对服务器执行异步读写操作 IOPCAsyncIO2接口用来替代IOPCAsyncIO接口 IConnectionPointContainer接口允许用户探查发现连接点 IDataObject接口允许客户和使用OPC数据流格式的组之间产生连接

图表 6 OPCGroup对象接口图
6. OPC服务器的Delphi实现 本项目的辅机温度巡检OPC数据访问服务器使用Delphi7开发,其遵守OPC Data Access 2.05规范。本服务器功能如下: 服务器的注册与反注册 定时读取现场数据并更新数据缓冲池 OPCServer对象的实现 OPCGroup对象的实现 服务器的界面显示
程序实现如下: (1) 服务器的注册与反注册 OPC服务器必须按照规范要求正确注册才能被OPC客户访问。OPC服务器按照组件模式可以划分为进程内组件、进程外组件。不同模式的服务器注册的方法稍有差异。 通常OPC服务器要求的标识格式为:Vendor.Drivername.Version 我的温度巡检OPC服务器的标识为:HUA.DA2.1 按照OPC规范要求,我的OPC服务器在系统注册表中建立如下注册表项: 1至6项对于不同模式的服务器均需要: 1. HKEY_CLASSES_ROOT\Vendor.Drivername.Version = A Description of your server 2. HKEY_CLASSES_ROOT\Vendor.Drivername.Version\CLSID = {Your Server's unique CLSID} 3. HKEY_CLASSES_ROOT\Vendor.Drivername.Version\OPC 4. HKEY_CLASSES_ROOT\Vendor.Drivername.Version\OPC\Vendor =Your vendor name 5. HKEY_CLASSES_ROOT\CLSID\{Your Server's unique CLSID} = A Description of your server 6. HKEY_CLASSES_ROOT\CLSID\{Your Server's unique CLSID}\ProgID = Vendor.Drivername.Version 7至8项根据服务器的模式进行选择注册: 7. HKEY_CLASSES_ROOT\CLSID\{Your Server's unique CLSID}\InprocServer32 = Full Path to DLL 8. HKEY_CLASSES_ROOT\CLSID\{YourServer's unique CLSID}\LocalServer32 = Full Path to EXE 9. HKEY_CLASSES_ROOT\CLSID\{YourServer's unique CLSID}\InprocHandler32 = Full Path to DLL 步骤1的代码示例如下: aReg:=nil; try aReg:=TRegistry.Create; aReg.RootKey:=HKEY_CLASSES_ROOT; aReg.OpenKey(HUA.DA2.1,true); aReg.WriteString('','HUA Data Access Server Version 2.0'); finally aReg.CloseKey; aReg.Free; end; 此外,还应根据规范创建组件的分类信息,以便与组件分类管理。对于数据访问服务器规范提供的组件类别为: "OPC Data Access Servers Version 1.0" CATID_OPCDAServer10 = {63D5F430-CFE4-11d1-B2C8-0060083BA1FB}
"OPC Data Access Servers Version 2.0" CATID_OPCDAServer20 = {63D5F432-CFE4-11d1-B2C8-0060083BA1FB} 创建类别的程序代码为: CreateComponentCategory(CATID_OPCDAServer20,'HUA OPC Data Access'); RegisterCLSIDInCategory(CLASS_HUA_DA2,CATID_OPCDAServer20);
实现服务器的反注册就简单了,逐一清除掉以上所建立的注册表项即可: aReg:=nil; try aReg:=TRegistry.Create; aReg.RootKey:=HKEY_CLASSES_ROOT; aReg.DeleteKey(name); finally aReg.CloseKey; aReg.Free; end; 还应删除自己组件类别信息 UnRegisterCLSIDInCategory(CLASS_HUA_DA2,CATID_OPCDAServer20); UnCreateComponentCategory(CATID_OPCDAServer20,'HUA OPC Data Access');
(2) 定时读取现场数据并更新数据缓冲池 OPC服务器本身就是一个可执行程序,该程序以设定的速率不断地同物理设备进行数据交互。服务器内有一个数据缓冲区,其中存有最新的数据值,数据质量戳和时间戳。时间戳表明服务器最近一次从设备读取数据的时间。服务器对设备寄存器的读取是不断进行的,时间戳也在不断更新。即使数据值和质量戳都没有发生变化,时间戳也会进行更新。客户既可从服务器缓冲区读取数据,也可直接从设备读取数据,从设备直接读取数据速度会慢一些,一般只有在故障诊断或极特殊的情况下才会采用。
OPC客户和OPC服务器进行数据交互可以有两种不同方式,即同步方式和异步方式。同步方式实现较为简单,当客户数目较少而且同服务器交互的数据量也比较少的时候可以采用这种方式;异步方式实现较为复杂,需要在客户程序中实现服务器回调函数。然而当有大量客户和大量数据交互时,异步方式能提供高效的性能,尽量避免阻塞客户数据请求,并最大可能地节省CPU和网络资源。
本服务器的数据缓冲池的数据格式是按照OPC数据访问规范中的 OPCITEMSTATE格式设计的。 type TOPCItem=record strID:string;//点的ID编号 bActive:longbool;//点的激活状态 ItemState:OPCITEMSTATE;//点的客户访问项 end;
type OPCITEMSTATE = record hClient: OPCHANDLE;//点的客户句柄 ftTimeStamp: TFileTime;//点的时间戳 wQuality: Word;//点的品质 wReserved: Word;//保留 vDataValue: OleVariant;//点的值 end; 本温度采集项目选用研华的亚当4000系列模块,OPC服务器通过计算机串口与ADAM-4520模块连接,这是一块隔离RS232到RS422/485转换器。现场模块通过485总线与其连在一起。数据的采集方式如下图所示

图表 7 温度巡检系统配置图
对于服务器来说实现现场数据的采集,只需要对串口进行编程即可。串口编程的方法很多,例如:Windows API方式、在程序中嵌入汇编语言操作端口、使用控件等。我选择了使用控件。不同公司开发的串口控件很多,我使用的是微软的MSComm控件。编程时只需要参照研华的相关模块操作命令表,然后使用MSComm控件发出相应的命令进行操作即可。例如根据如下命令表

图表 8 ADAM-4015命令表摘录
对于命令#AAN 意思是读取地址为AA的模块的第N通道数据。如果操作正确则模块响应为>(data)(cr) 如果在程序中发出如下命令: MSComm1.Output :='#021'+#13; //读取2号模块的第一通道数据 2号模块的响应为:>+1.4567(cr)
(3) OPCServer对象的实现 必须严格按照规范要求,实现所要求的所有接口函数。但是这并不意味着所有的接口函数都必须自己编写,我们可以充分利用COM的包容与聚合机制来复用一些常规的接口。在Delphi中有很多现成的类已经实现了众多的接口,我们可以充分利用这一点来简化我们的编程过程。例如在我的 OPCServer服务器对象中IUnknown接口是通过父类TAutoObject 实现的,IConnectionPointContainer 接口是通过创建一个Delphi的TConnectionPoints对象,然后将其接口暴露给客户实现的; OPCServer的Delphi描述如下,限于篇幅具体实现过程从略: type THUA_DA2 = class(TAutoObject,IHUA_DA2, IOPCServer,IConnectionPointContainer,IOPCCommon,IOPCItemProperties, IOPCServerPublicGroups,IOPCBrowseServerAddressSpace,IPersist,IPersistFile) public ClientIUnknown:IUnknown; Grp:TOPCGroup; procedure Initialize; override; procedure ShutdownOnConnect(const Sink: IUnknown; Connecting: Boolean); destructor Destroy;override; private FIConnectionPoints:TConnectionPoints; protected property iFIConnectionPoints:TConnectionPoints read FIConnectionPoints write FIConnectionPoints implements IConnectionPointContainer; //IOPCServer 接口描述省略 //IOPCCommon接口描述省略 //IOPCServerPublicGroups接口描述省略 //IOPCBrowseServerAddressSpace接口描述省略 //IPersistFile接口描述省略 //IOPCItemProperties接口描述省略 end;//end type
(4) OPCGroup对象的实现 OPCGroup对象是由OPCServer对象创建的。当OPC客户程序调用OPCServer对象的接口函数AddGroup时,在AddGroup函数体内生成OPCGroup对象。以后用户依赖与指向这个OPCGroup接口的指针,便可以方便的创建和管理点项目,从而获得现场的实时数据。OPCGroup对象的描述如下:
type TOPCGroup = class(TTypedComObject,IOPCGroup,IOPCItemMgt,IOPCGroupStateMgt, IOPCPublicGroupStateMgt,IOPCSyncIO, IConnectionPointContainer,IOPCAsyncIO2) private FIConnectionPoints:TConnectionPoints; protected property iFIConnectionPoints:TConnectionPoints read FIConnectionPoints write FIConnectionPoints implements IConnectionPointContainer; //IOPCItemMgt 接口描述省略 //IOPCGroupStateMgt接口描述省略 //IOPCPublicGroupStateMgt接口描述省略 //IOPCSyncIO接口描述省略 //IOPCAsyncIO2接口描述省略 end;
(5) 服务器的界面显示 OPC服务器的主要功能在于数据的传递,它常常并不需要过多的人机交互过程。项目中的OPC服务器只是为了调试的方便,在界面中通过两个按钮向用户提供了服务器的注册与反注册功能。在服务器中还显示了所连接的OPC客户数和OPC客户创建的OPC组数目,以及数据缓冲池中的数据更新情况。
7. 结语 总的来说,进行OPC服务器程序的开发就是严格按照OPC规范编写各种接口函数来向客户提供服务,在定时更新缓冲池中的数据的同时,还应对缓冲池中的数据提供较快的查询服务从而减少系统延迟。
|