Delphi环境下使用定制接口开发OPC数据访问客户程序

来源:互联网 发布:js中flag使用方法 编辑:程序博客网 时间:2024/04/29 20:15
http://www.kongzhi.net/cases/caseview.php?id=1112
Delphi环境下使用定制接口开发OPC数据访问客户程序


OPC(用于过程控制的OLE)是一个工业标准。它由一些世界上占领先地位的自动化系统和硬件、软件公司与微软公司紧密合作而建立的。这个标准定义了应用Microsoft操作系统在基于PC 的客户机之间交换自动化实时数据的方法。管理这个标准的国际组织是OPC基金会。因为应用程序要和不同的设备,比如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(Distributed Component Object Model,分布式组件对象模型)。如图1所示。

2  OPC数据访问服务器接口方式
    OPC数据访问规范提供了两套接口方案,即定制接口(Custom Interface)和自动化接口(Automation Interface)。其中自动化接口是对定制接口的进一步封装。定制接口效率高,采用它能够发挥OPC服务器的最佳性能。用C++编写访问OPC服务器的程序一般采用定制接口方案;对于VB、VBA、EXCEL等编程软件或工具,不能直接访问通用接口,而要通过自动化接口,因此使用VB或VBA等语言的客户程序一般使用自动化接口。

3  OPC数据访问服务器的数据访问方法
(1)  同步数据访问处理
    OPC 服务器把按照OPC 客户应用程序的要求得到的数据访问结果,作为方法的参数返回给OPC 客户程序。OPC 客户应用程序在结果被返回之前必须处于等待状态。
(2)  异步数据访问处理
    OPC 服务器接到 OPC 应用程序的要求后,几乎立即将方法返回。OPC 客户应用程序随后可以进行其他处理。当OPC 完成数据访问时,触发OPC 客户应用程序的一部访问事件,将数据访问结果传送给OPC 应用程序。OPC 应用程序在事件处理程序中接受OPC 服务器传送来的数据。
(3)  订阅方式数据采集
    此种方式是OPC 客户应用程序不需要向OPC 服务器程序提出数据请求,而是自动接收OPC 服务器送来的变化通知信号的订阅方式数据采集。服务器按照一定的更新周期更新OPC 服务器的数据缓冲器的数值,如果发现数值有变化时,就会以数据变化事件通知OPC 客户应用程序。OPC 服务器支持不敏感带宽 (Dead Band),当OPC标签的数据类型是模拟量时,只有当前值与上一次值的偏差的绝对值超过一定的限度时,才更新缓冲器的数据并通知OPC 客户应用程序。这样就可以忽略模拟量的微小变化,从而减轻OPC 服务器和OPC 客户的负担。
(4)  刷新方式
    刷新是一种特殊形式的订阅,它强制更新活动组中的所有活动项数据,而不管这些数据与以前相比是否有了变化


1  引言
 
图1  OPC应用架构




    OPC(用于过程控制的OLE)是一个工业标准。它由一些世界上占领先地位的自动化系统和硬件、软件公司与微软公司紧密合作而建立的。这个标准定义了应用Microsoft操作系统在基于PC 的客户机之间交换自动化实时数据的方法。管理这个标准的国际组织是OPC基金会。因为应用程序要和不同的设备,比如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(Distributed Component Object Model,分布式组件对象模型)。如图1所示。

2  OPC数据访问服务器接口方式
    OPC数据访问规范提供了两套接口方案,即定制接口(Custom Interface)和自动化接口(Automation Interface)。其中自动化接口是对定制接口的进一步封装。定制接口效率高,采用它能够发挥OPC服务器的最佳性能。用C++编写访问OPC服务器的程序一般采用定制接口方案;对于VB、VBA、EXCEL等编程软件或工具,不能直接访问通用接口,而要通过自动化接口,因此使用VB或VBA等语言的客户程序一般使用自动化接口。
 
图2  OPC数据访问方式
3  OPC数据访问服务器的数据访问方法


(1)  同步数据访问处理
    OPC 服务器把按照OPC 客户应用程序的要求得到的数据访问结果,作为方法的参数返回给OPC 客户程序。OPC 客户应用程序在结果被返回之前必须处于等待状态。
(2)  异步数据访问处理
    OPC 服务器接到 OPC 应用程序的要求后,几乎立即将方法返回。OPC 客户应用程序随后可以进行其他处理。当OPC 完成数据访问时,触发OPC 客户应用程序的一部访问事件,将数据访问结果传送给OPC 应用程序。OPC 应用程序在事件处理程序中接受OPC 服务器传送来的数据。
(3)  订阅方式数据采集
    此种方式是OPC 客户应用程序不需要向OPC 服务器程序提出数据请求,而是自动接收OPC 服务器送来的变化通知信号的订阅方式数据采集。服务器按照一定的更新周期更新OPC 服务器的数据缓冲器的数值,如果发现数值有变化时,就会以数据变化事件通知OPC 客户应用程序。OPC 服务器支持不敏感带宽 (Dead Band),当OPC标签的数据类型是模拟量时,只有当前值与上一次值的偏差的绝对值超过一定的限度时,才更新缓冲器的数据并通知OPC 客户应用程序。这样就可以忽略模拟量的微小变化,从而减轻OPC 服务器和OPC 客户的负担。
(4)  刷新方式
    刷新是一种特殊形式的订阅,它强制更新活动组中的所有活动项数据,而不管这些数据与以前相比是否有了变化。


4  使用Delphi进行COM程序开发的优点


    Delphi是功能强大的应用程序开发工具。它具有功能强大、运行速度快、易于学习、使用和开发效率高等特点。它是可视化应用编程环境,可重用性面向对象编程语言,快速编译器和数据库的完美组合。编写OPC定制接口的客户程序的本质就是编写COM客户程序,而使用Delphi进行COM开发时,人们会发现Object Pascal为COM提供了强大的语言支持。主要有以下几点:
(1)  Variant 和 OleVariant支持
    直接使用C和C++处理变体需要调用VariantInit(),VariantCopy(),VariantClear()等函数。而Object Pascal对变体的支持,使得在使用Variant和OleVariant类型的情况下,编译器能够自动生成对API的变体支持程序的调用。
(2)  可变数组支持
    在Delphi中一旦一个Variant包含了一个可变数组,就可以用标准数组的下标来访问数组元素。与C和C++中手工生成的安全数组相比,Object Pascal的封装功能要简洁的多,且不易出错。
(3)  后期绑定Automation支持
    Object Pascal对于Variant和OleVariant的支持使得编写后期绑定的自动化(Automation)客户程序成为可能。因此使用Delphi来开发基于自动化接口的OPC客户程序也是很方便的。
(4)  宽字符串支持
    Delphi提供的宽字符串(WideString)类型是一个和COM BSTR字符串兼容的字符串。而传统方式下,使用标准的诸如SysStringLen()等API函数与BSTR一起工作是一件相当麻烦的事。
(5)  接口支持
    Object Pascal为合乎COM规范的接口提供了完全自包容的实现代码,接口的实现不需要任何的COM API函数。程序员不需要考虑在传统COM编程中的引用计数和接口查询等底层细节。
(6)  Dispinterface接口支持
    Object Pascal对于Dispinterface的支持使得编写支持双接口的COM程序非常容易。


5  Delphi环境下使用定制接口开发OPC数据访问客户程序


    OPC规范中规定OPC服务器必须提供定制接口,而自动化接口则可以有选择地提供。因此编写使用定制接口的OPC客户程序更具有一般意义,而且使用定制接口的效率要远远高于使用自动化接口的效率。本文限于篇幅只讨论针对OPC数据访问服务器的客户应用程序。
5.1  OPC数据访问服务器简介
    OPCDataAccess服务器是最基本的OPC服务器,它包括OPCServer、OPCGroup和OPCItem三类典型的对象。OPC服务器对象维护有关服务器的信息并用作OPC组对象的容器,而OPC组对象维护组的信息,提供包容OPC项的机制,并管理OPC项。OPC组提供了客户程序组织数据的手段。有两种类型的组:公共(Public)组和局部(Local)组。公共组可以被多个客户共享,而局部组只能被一个客户使用。每个组中都可以定义一个或多个OPC项。OPC项代表了与服务器中的数据的连接。客户程序对OPC项的操作都是通过包容此项的OPC组来进行的,而不是直接把OPC项作为一个对象来操作。每个OPC项都有值(Value)、品质(Quality)和时间戳(Time Stamp)三个属性。

图3  OPC数据访问服务器的数据组织方式
    人们要的就是上面的item,这就是点,人们所谓的点,就是PLC的I/O点、仪表的数值等。编写客户端的程序的过程实际上就是对在OPC服务器中的数据项目(Item)进行操作。这就要求人们要了解OPC数据访问服务器不同对象的接口的功能:
(1)  OPCServer对象接口
    OPCServer对象是OPC中的首要对象,它提供了如下接口:
    IUnknown接口是COM的标准接口;
   IOPCServer接口可对OPCGroup对象进行有关操作;
   IOPCServerPublicGroups接口为客户和服务器提供了管理公共组的功能;
   IOPCBrowseServerAddressSpace接口提供了客户浏览服务器数据项的功能;
   IOPCItemProperties接口让客户能够浏览与ItemID相关的可访问属性;
   IOPCCommon接口提供了设置和询问LocaleID的功能;
   IPersistFile接口允许客户装载和保存服务器的配置信息;
   IConnectionPointContainer接口允许用户探查发现连接点。
(2)  OPCGroup对象接口
   OPCGroup对象是管理数据项集合的对象,它提供的接口如下:
   IUnknown接口是COM的标准接口;
   IOPCItemMgt接口为客户提供了添加,删除和控制组中数据项的功能;
   IOPCGroupStateMgt接口允许客户管理组中的所有状态信息;
   IOPCPublicGroupStateMgt接口用来将私有组转换为公共组;
   IOPCSyncIO接口允许用户对服务器执行同步读写操作;
   IOPCAsyncIO接口允许客户对服务器执行异步读写操作;
   IOPCAsyncIO2接口用来替代IOPCAsyncIO接口;
   IConnectionPointContainer接口允许用户探查发现连接点;
   IDataObject接口允许客户和使用OPC数据流格式的组之间产生连接。
5.2  同步读写方式编程
   编程部分限于篇幅只列出核心代码,关于一些类型定义,接口描述均省略了。
(1)  COM库的初始化
   在调用任何COM或OLE API函数之前,必须要用CoIntialize()函数来对COM库进行初始化,为了关闭COM库在最后一次调用COM库后,要调用CoUnitialize()函数。使用Delphi开发时,事情就简化了,人们只需要在程序中包括ComObj单元即可,这样应用程序在调用Application.Initialize()时会自动调用CoInitialize()函数,而ComObj单元的finalization部分会自动调用CoUnitialize()函数。只需要一句代码:
Uses ComObj;
(2)  创建服务器对象
   Const ServerProgID = 'hua.da2.1';//OPC服务器的注册名称
   Var 
   ServerIf: IOPCServer;//声明服务器对象接口
   HR: HResult;//用来保存函数返回值
   ServerIf := CreateComObject(ProgIDToClassID(ServerProgID)) as IOPCServer;
   / /本函数用来获取服务器对象的IOPCServer接口,这是一个COM库函数
(3)  添加组对象
   Var  
   GroupIf: IOPCItemMgt; 
   GroupHandle: OPCHANDLE;
   HR:=ServerAddGroup(ServerIf, 'MyGroup', True, 500, 0, GroupIf, GroupHandle);
   //本函数用来对IOPCServer.AddGroup方法进行包装,在服务器对象中添加一个名为MyGroup的组对象,激活状态为true,更新率为500毫秒。如果组对象添加成功,则组对象接口保存在GroupIf中,组对象句柄保存在GroupHandle中。其实现过程如下:
   function ServerAddGroup(ServerIf: IOPCServer; Name: string; Active: BOOL;
        UpdateRate: DWORD; ClientHandle: OPCHANDLE; var GroupIf: IOPCItemMgt;
        var ServerHandle: OPCHANDLE): HResult;
var
  PercentDeadBand: Single;
  RevisedUpdateRate: DWORD;
begin
  Result := E_FAIL;
  if ServerIf <> nil then
  begin
    PercentDeadBand := 0.0;
    Result := ServerIf.AddGroup(PWideChar(WideString(Name)), Active, UpdateRate,
                            ClientHandle, nil, @PercentDeadBand, 0,
                            ServerHandle, RevisedUpdateRate, IOPCItemMgt,
                            IUnknown(GroupIf));
  end;
  if Failed(Result) then
  begin
    GroupIf := nil;
  end;
end;
(4)  添加项目
const  Item0Name = 'item.hua.bstr';//要添加的项目名称
Var  
ItemType: TVarType; 
Item0Handle: OPCHANDLE;
HR := GroupAddItem(GroupIf, Item0Name, 0, VT_EMPTY, Item0Handle,ItemType);
//本函数用来对IOPCItemMgt.AddItems进行包装,在组对象中添加一个类型为VT_EMPTY,名称为item.hua.bstr的项目,如果添加成功,则项目句柄保存在Item0Handle中,实际项目类型保存在ItemType中。其实现过程如下:
function GroupAddItem(GroupIf: IOPCItemMgt; ItemID: string;
          ClientHandle: OPCHANDLE; DataType: TVarType;
          var ServerHandle: OPCHANDLE; var CanonicalType: TVarType): HResult;
var
  ItemDef: OPCITEMDEF;
  Results: POPCITEMRESULTARRAY;
  Errors: PResultList;
begin
  if GroupIf = nil then
  begin
    Result := E_FAIL;
    Exit;
  end;
  with ItemDef do
  begin
    szAccessPath := '';
    szItemID := PWideChar(WideString(ItemID));
    bActive := True;
    hClient := ClientHandle;
    dwBlobSize := 0;
    pBlob := nil;
    vtRequestedDataType := DataType;
  end;
  Result := GroupIf.AddItems(1, @ItemDef, Results, Errors);
  if Succeeded(Result) then
  begin
    Result := Errors[0];
    try
      if Succeeded(Result) then
      begin
        ServerHandle := Results[0].hServer;
        CanonicalType := Results[0].vtCanonicalDataType;
      end;
    finally
      CoTaskMemFree(Results[0].pBlob);
      CoTaskMemFree(Results);
      CoTaskMemFree(Errors);
    end;
  end;
end;
(5)  同步读
Var
ItemValue: string;
ItemQuality: Word;
HR := ReadOPCGroupItemValue(GroupIf, Item0Handle, ItemValue, ItemQuality);
//本函数用来同步读取组中的项目值,如果读取成功,则项目值保存在ItemValue中,项目质量保存在ItemQuality中,其实现过程如下:
function ReadOPCGroupItemValue(GroupIf: IUnknown; ItemServerHandle: OPCHANDLE;
          var ItemValue: string; var ItemQuality: Word): HResult;
var
  SyncIOIf: IOPCSyncIO;
  Errors: PResultList;
  ItemValues: POPCITEMSTATEARRAY;
begin
  Result := E_FAIL;
  try
    SyncIOIf := GroupIf as IOPCSyncIO;
  except
    SyncIOIf := nil;
  end;
  if SyncIOIf <> nil then
  begin
    Result := SyncIOIf.Read(OPC_DS_CACHE, 1, @ItemServerHandle, ItemValues,
                            Errors);
    if Succeeded(Result) then
    begin
      Result := Errors[0];
      CoTaskMemFree(Errors);
      ItemValue := VarToStr(ItemValues[0].vDataValue);
      ItemQuality := ItemValues[0].wQuality;
      VariantClear(ItemValues[0].vDataValue);
      CoTaskMemFree(ItemValues);
    end;
  end;
end;
(6)  同步写
ItemValue:='hello,the operation is sync-write';
HR := WriteOPCGroupItemValue(GroupIf, Item0Handle, ItemValue);
//本函数用来将ItemValue同步写入Item0Handle所代表的项目中,其实现过程如下:
function WriteOPCGroupItemValue(GroupIf: IUnknown; ItemServerHandle: OPCHANDLE;
          ItemValue: OleVariant): HResult;
var
  SyncIOIf: IOPCSyncIO;
  Errors: PResultList;
begin
  Result := E_FAIL;
  try
    SyncIOIf := GroupIf as IOPCSyncIO;
  except
    SyncIOIf := nil;
  end;
  if SyncIOIf <> nil then
  begin
    Result := SyncIOIf.Write(1, @ItemServerHandle, @ItemValue, Errors);
    if Succeeded(Result) then
    begin
      Result := Errors[0];
      CoTaskMemFree(Errors);
    end;
  end;
end;
(7)  断开服务器连接
   HR := ServerIf.RemoveGroup(GroupHandle, False);
   //本函数用来将在服务器中创建的组GroupHandle删除,服务器对象在程序结束时会自动销毁。
5.3  异步读写方式编程
    使用异步方式进行读写编程,需要客户提供IOPCDataCallback接口,还需要使用到COM的连接点容器,连接点和接收器等相关知识。以下关于异步读写方式编程中和同步方式相同的部分均未给出其实现部分。下面给出异步读写方式编程的主要步骤:
   (1)  COM库的初始化   //实现过程同上
   (2)  创建服务器对象 //实现过程同上
   (3)  添加组对象 //实现过程同上
   (4)  添加项目  //实现过程同上
   (5)  实现IOPCDataCallback接口
   为了使用连接点,客户必须创建同时支持IUnknown和IOPCDataCallback接口的对象。下面的TOPCDataCallback从TInterfacedObject继承所以支持IUnknown接口,它实现了IOPCDataCallback接口。
type
   // 本类用来接收 IConnectionPointContainer 的数据变化回调
  TOPCDataCallback = class(TInterfacedObject, IOPCDataCallback)
  public
    function OnDataChange(dwTransid: DWORD; hGroup: OPCHANDLE;
      hrMasterquality: HResult; hrMastererror: HResult; dwCount: DWORD;
      phClientItems: POPCHANDLEARRAY; pvValues: POleVariantArray;
      pwQualities: PWordArray; pftTimeStamps: PFileTimeArray;
      pErrors: PResultList): HResult; stdcall;
    function OnReadComplete(dwTransid: DWORD; hGroup: OPCHANDLE;
      hrMasterquality: HResult; hrMastererror: HResult; dwCount: DWORD;
      phClientItems: POPCHANDLEARRAY; pvValues: POleVariantArray;
      pwQualities: PWordArray; pftTimeStamps: PFileTimeArray;
      pErrors: PResultList): HResult; stdcall;
    function OnWriteComplete(dwTransid: DWORD; hGroup: OPCHANDLE;
      hrMastererr: HResult; dwCount: DWORD; pClienthandles: POPCHANDLEARRAY;
      pErrors: PResultList): HResult; stdcall;
    function OnCancelComplete(dwTransid: DWORD; hGroup: OPCHANDLE):
      HResult; stdcall;
  end;
(6)  连接IOPCDataCallback接口
   客户程序除了实现接收器对象外,还必须建立接收器与连接点对象之间的连接关系
var  
  AsyncConnection: Longint;
  OPCDataCallback: IOPCDataCallback;
  OPCDataCallback := TOPCDataCallback.Create;//创建接收器对象
  HR := GroupAdvise2(GroupIf, OPCDataCallback, AsyncConnection);
//本函数实现了接收器对象与连接点对象之间建立关系的过程,如果连接成功则用AsyncConnection来标识这个连接,其具体实现如下:
function GroupAdvise2(GroupIf: IUnknown; OPCDataCallback: IOPCDataCallback;
          var AsyncConnection: Longint): HResult;
var
  ConnectionPointContainer: IConnectionPointContainer;
  ConnectionPoint: IConnectionPoint;
begin
  Result := E_FAIL;
  try
    ConnectionPointContainer := GroupIf as IConnectionPointContainer;
  except
    ConnectionPointContainer := nil;
  end;
  if ConnectionPointContainer <> nil then
  begin
    Result := ConnectionPointContainer.FindConnectionPoint(IID_IOPCDataCallback,
      ConnectionPoint);
    if Succeeded(Result) and (ConnectionPoint <> nil) then
    begin
      Result := ConnectionPoint.Advise(OPCDataCallback as IUnknown,
        AsyncConnection);
    end;
  end;
end;
(7)  异步读写
   客户进行异步读写操作只需要简单的调用IOPCAsyncIO2接口的Read,Write等方法,这个过程不需要客户程序等待,函数的返回值中并不包括操作的结果,而操作的具体结果会由服务器通过调用客户的IOPCDataCallback接口中的OnDataChange,OnReadComplete,OnWriteComplete,OnCancelComplete等方法来返回给客户。下面给出IOPCAsyncIO2接口的Delphi描述:
IOPCAsyncIO2 = interface(IUnknown)
    ['{39C13A71-011E-11D0-9675-0020AFD8ADB3}']
    function Read(
            dwCount:                    DWORD;
            phServer:                   POPCHANDLEARRAY;
            dwTransactionID:            DWORD;
      out   pdwCancelID:                DWORD;
      out   ppErrors:                   PResultList): HResult; stdcall;
    function Write(
            dwCount:                    DWORD;
            phServer:                   POPCHANDLEARRAY;
            pItemValues:                POleVariantArray;
            dwTransactionID:            DWORD;
      out   pdwCancelID:                DWORD;
      out   ppErrors:                   PResultList): HResult; stdcall;
    function Refresh2(
            dwSource:                   OPCDATASOURCE;
            dwTransactionID:            DWORD;
      out   pdwCancelID:                DWORD): HResult; stdcall;
    function Cancel2(
            dwCancelID:                 DWORD): HResult; stdcall;
    function SetEnable(
            bEnable:                    BOOL): HResult; stdcall;
    function GetEnable(
      out   pbEnable:                   BOOL): HResult; stdcall;
  end;
   当服务器中的数据发生变化时,服务器会调用客户的IOPCDataCallback接口中OnDataChange函数,当服务器异步读完成后会调用客户的IOPCDataCallback接口中OnReadComplete函数,当服务器异步写完成后会调用客户的IOPCDataCallback接口中OnWriteComplete函数,当服务器撤销操作完成后会调用客户的IOPCDataCallback接口中OnCancelComplete函数。限于篇幅,这里只给出客户的IOPCDataCallback接口中OnDataChange函数实现,其他函数的实现过程类似。
function TOPCDataCallback.OnDataChange(dwTransid: DWORD; hGroup: OPCHANDLE;
  hrMasterquality: HResult; hrMastererror: HResult; dwCount: DWORD;
  phClientItems: POPCHANDLEARRAY; pvValues: POleVariantArray;
  pwQualities: PWordArray; pftTimeStamps: PFileTimeArray;
  pErrors: PResultList): HResult;
var
  ClientItems: POPCHANDLEARRAY;
  Values: POleVariantArray;
  Qualities: PWORDARRAY;
  I: Integer;
  NewValue: string;
begin
  Result := S_OK;
  ClientItems := POPCHANDLEARRAY(phClientItems);
  Values := POleVariantArray(pvValues);
  Qualities := PWORDARRAY(pwQualities);
  for I := 0 to dwCount - 1 do
  begin
    if Qualities[I] = OPC_QUALITY_GOOD then
    begin
      NewValue := VarToStr(Values[I]);
      Form1.LblValue.Caption :=NewValue;
    end
    else begin
      ShowMessage('Callback received for item , but quality not good');
    end;
  end;
end;
(8)  断开IOPCDataCallback接口
GroupUnadvise2(GroupIf, AsyncConnection);
//断开客户与组对象GroupIf之间用AsyncConnection标识的IOPCDataCallback接口连接,其实现过程如下:
function GroupUnadvise2(GroupIf: IUnknown; var AsyncConnection: Longint): HResult;
var
  ConnectionPointContainer: IConnectionPointContainer;
  ConnectionPoint: IConnectionPoint;
begin
  Result := E_FAIL;
  try
    ConnectionPointContainer := GroupIf as IConnectionPointContainer;
  except
    ConnectionPointContainer := nil;
  end;
  if ConnectionPointContainer <> nil then
  begin
    Result := ConnectionPointContainer.FindConnectionPoint(IID_IOPCDataCallback,
      ConnectionPoint);
    if Succeeded(Result) and (ConnectionPoint <> nil) then
    begin
      Result := ConnectionPoint.Unadvise(AsyncConnection);
    end;
  end;
end;
(9)  断开服务器连接 //实现过程同上


6  结语


    使用OPC的定制接口进行客户程序的编程,需要一些COM知识的理解以及对于以下开发工具的熟练使用,因此相对于自动化接口的编程来说时比较困难的,但是使用定制接口的效率和灵活性却是使用其他方式所无法比拟的。本文只是粗浅的介绍了在Delphi环境下使用定制接口进行OPC客户程序的开发,希望能达到抛砖引玉的效果。
原创粉丝点击