1 引言
圖1 OPC應(yīng)用架構(gòu)
OPC(用于過程控制的OLE)是一個工業(yè)標準。它由一些世界上占領(lǐng)先地位的自動化系統(tǒng)和硬件、軟件公司與微軟公司緊密合作而建立的。這個標準定義了應(yīng)用Microsoft操作系統(tǒng)在基于PC 的客戶機之間交換自動化實時數(shù)據(jù)的方法。管理這個標準的國際組織是OPC基金會。因為應(yīng)用程序要和不同的設(shè)備,比如PLC、變頻器、現(xiàn)場總線的儀表等通訊,如果不同的設(shè)備廠家都遵守一個相同的程序接口標準的話,那么程序和不同設(shè)備的溝通將變得非常容易。OPC 就是這樣一種工業(yè)標準,它是OLE for Process Control 的英文縮寫。OPC 是基于微軟的COM(Component Object Model,組件對象模型)和OLE(Object Linking and Embedding,對象鏈接與嵌入)技術(shù)之上的。和以前不同的是,現(xiàn)在設(shè)備廠家提供不同的OPC Server。OPC Server負責從設(shè)備中取數(shù)據(jù)和寫數(shù)據(jù)。人們所要做的就是利用統(tǒng)一的COM 規(guī)范編寫OPC Client。客戶程序和OPC Server 打交道。OPC Server是一座在客戶和硬件設(shè)備之間的橋梁,通過它,人們可以很容易的取得現(xiàn)場的溫度、壓力、流量、位置等信號,以及控制現(xiàn)場的閥門開度、電機轉(zhuǎn)速等。注意客戶程序和服務(wù)器程序可以在同一臺計算機上,也可以在不同的計算機上,區(qū)別是使用COM 還是使用DCOM(Distributed Component Object Model,分布式組件對象模型)。如圖1所示。
2 OPC數(shù)據(jù)訪問服務(wù)器接口方式
OPC數(shù)據(jù)訪問規(guī)范提供了兩套接口方案,即定制接口(Custom Interface)和自動化接口(Automation Interface)。其中自動化接口是對定制接口的進一步封裝。定制接口效率高,采用它能夠發(fā)揮OPC服務(wù)器的最佳性能。用C++編寫訪問OPC服務(wù)器的程序一般采用定制接口方案;對于VB、VBA、EXCEL等編程軟件或工具,不能直接訪問通用接口,而要通過自動化接口,因此使用VB或VBA等語言的客戶程序一般使用自動化接口。
圖2 OPC數(shù)據(jù)訪問方式
3 OPC數(shù)據(jù)訪問服務(wù)器的數(shù)據(jù)訪問方法
(1) 同步數(shù)據(jù)訪問處理
OPC 服務(wù)器把按照OPC 客戶應(yīng)用程序的要求得到的數(shù)據(jù)訪問結(jié)果,作為方法的參數(shù)返回給OPC 客戶程序。OPC 客戶應(yīng)用程序在結(jié)果被返回之前必須處于等待狀態(tài)。
(2) 異步數(shù)據(jù)訪問處理
OPC 服務(wù)器接到 OPC 應(yīng)用程序的要求后,幾乎立即將方法返回。OPC 客戶應(yīng)用程序隨后可以進行其他處理。當OPC 完成數(shù)據(jù)訪問時,觸發(fā)OPC 客戶應(yīng)用程序的一部訪問事件,將數(shù)據(jù)訪問結(jié)果傳送給OPC 應(yīng)用程序。OPC 應(yīng)用程序在事件處理程序中接受OPC 服務(wù)器傳送來的數(shù)據(jù)。
(3) 訂閱方式數(shù)據(jù)采集
此種方式是OPC 客戶應(yīng)用程序不需要向OPC 服務(wù)器程序提出數(shù)據(jù)請求,而是自動接收OPC 服務(wù)器送來的變化通知信號的訂閱方式數(shù)據(jù)采集。服務(wù)器按照一定的更新周期更新OPC 服務(wù)器的數(shù)據(jù)緩沖器的數(shù)值,如果發(fā)現(xiàn)數(shù)值有變化時,就會以數(shù)據(jù)變化事件通知OPC 客戶應(yīng)用程序。OPC 服務(wù)器支持不敏感帶寬 (Dead Band),當OPC標簽的數(shù)據(jù)類型是模擬量時,只有當前值與上一次值的偏差的絕對值超過一定的限度時,才更新緩沖器的數(shù)據(jù)并通知OPC 客戶應(yīng)用程序。這樣就可以忽略模擬量的微小變化,從而減輕OPC 服務(wù)器和OPC 客戶的負擔。
(4) 刷新方式
刷新是一種特殊形式的訂閱,它強制更新活動組中的所有活動項數(shù)據(jù),而不管這些數(shù)據(jù)與以前相比是否有了變化。
4 使用Delphi進行COM程序開發(fā)的優(yōu)點
Delphi是功能強大的應(yīng)用程序開發(fā)工具。它具有功能強大、運行速度快、易于學習、使用和開發(fā)效率高等特點。它是可視化應(yīng)用編程環(huán)境,可重用性面向?qū)ο缶幊陶Z言,快速編譯器和數(shù)據(jù)庫的完美組合。編寫OPC定制接口的客戶程序的本質(zhì)就是編寫COM客戶程序,而使用Delphi進行COM開發(fā)時,人們會發(fā)現(xiàn)Object Pascal為COM提供了強大的語言支持。主要有以下幾點:
(1) Variant 和 OleVariant支持
直接使用C和C++處理變體需要調(diào)用VariantInit(),VariantCopy(),VariantClear()等函數(shù)。而Object Pascal對變體的支持,使得在使用Variant和OleVariant類型的情況下,編譯器能夠自動生成對API的變體支持程序的調(diào)用。
(2) 可變數(shù)組支持
在Delphi中一旦一個Variant包含了一個可變數(shù)組,就可以用標準數(shù)組的下標來訪問數(shù)組元素。與C和C++中手工生成的安全數(shù)組相比,Object Pascal的封裝功能要簡潔的多,且不易出錯。
(3) 后期綁定Automation支持
Object Pascal對于Variant和OleVariant的支持使得編寫后期綁定的自動化(Automation)客戶程序成為可能。因此使用Delphi來開發(fā)基于自動化接口的OPC客戶程序也是很方便的。
(4) 寬字符串支持
Delphi提供的寬字符串(WideString)類型是一個和COM BSTR字符串兼容的字符串。而傳統(tǒng)方式下,使用標準的諸如SysStringLen()等API函數(shù)與BSTR一起工作是一件相當麻煩的事。
(5) 接口支持
Object Pascal為合乎COM規(guī)范的接口提供了完全自包容的實現(xiàn)代碼,接口的實現(xiàn)不需要任何的COM API函數(shù)。程序員不需要考慮在傳統(tǒng)COM編程中的引用計數(shù)和接口查詢等底層細節(jié)。
(6) Dispinterface接口支持
Object Pascal對于Dispinterface的支持使得編寫支持雙接口的COM程序非常容易。
5 Delphi環(huán)境下使用定制接口開發(fā)OPC數(shù)據(jù)訪問客戶程序
OPC規(guī)范中規(guī)定OPC服務(wù)器必須提供定制接口,而自動化接口則可以有選擇地提供。因此編寫使用定制接口的OPC客戶程序更具有一般意義,而且使用定制接口的效率要遠遠高于使用自動化接口的效率。本文限于篇幅只討論針對OPC數(shù)據(jù)訪問服務(wù)器的客戶應(yīng)用程序。
5.1 OPC數(shù)據(jù)訪問服務(wù)器簡介
OPCDataAccess服務(wù)器是最基本的OPC服務(wù)器,它包括OPCServer、OPCGroup和OPCItem三類典型的對象。OPC服務(wù)器對象維護有關(guān)服務(wù)器的信息并用作OPC組對象的容器,而OPC組對象維護組的信息,提供包容OPC項的機制,并管理OPC項。OPC組提供了客戶程序組織數(shù)據(jù)的手段。有兩種類型的組:公共(Public)組和局部(Local)組。公共組可以被多個客戶共享,而局部組只能被一個客戶使用。每個組中都可以定義一個或多個OPC項。OPC項代表了與服務(wù)器中的數(shù)據(jù)的連接。客戶程序?qū)PC項的操作都是通過包容此項的OPC組來進行的,而不是直接把OPC項作為一個對象來操作。每個OPC項都有值(Value)、品質(zhì)(Quality)和時間戳(Time Stamp)三個屬性。
圖3 OPC數(shù)據(jù)訪問服務(wù)器的數(shù)據(jù)組織方式
人們要的就是上面的item,這就是點,人們所謂的點,就是PLC的I/O點、儀表的數(shù)值等。編寫客戶端的程序的過程實際上就是對在OPC服務(wù)器中的數(shù)據(jù)項目(Item)進行操作。這就要求人們要了解OPC數(shù)據(jù)訪問服務(wù)器不同對象的接口的功能:
(1) OPCServer對象接口
OPCServer對象是OPC中的首要對象,它提供了如下接口:
IUnknown接口是COM的標準接口;
IOPCServer接口可對OPCGroup對象進行有關(guān)操作;
IOPCServerPublicGroups接口為客戶和服務(wù)器提供了管理公共組的功能;
IOPCBrowseServerAddressSpace接口提供了客戶瀏覽服務(wù)器數(shù)據(jù)項的功能;
IOPCItemProperties接口讓客戶能夠瀏覽與ItemID相關(guān)的可訪問屬性;
IOPCCommon接口提供了設(shè)置和詢問LocaleID的功能;
IPersistFile接口允許客戶裝載和保存服務(wù)器的配置信息;
IConnectionPointContainer接口允許用戶探查發(fā)現(xiàn)連接點。
(2) OPCGroup對象接口
OPCGroup對象是管理數(shù)據(jù)項集合的對象,它提供的接口如下:
IUnknown接口是COM的標準接口;
IOPCItemMgt接口為客戶提供了添加,刪除和控制組中數(shù)據(jù)項的功能;
IOPCGroupStateMgt接口允許客戶管理組中的所有狀態(tài)信息;
IOPCPublicGroupStateMgt接口用來將私有組轉(zhuǎn)換為公共組;
IOPCSyncIO接口允許用戶對服務(wù)器執(zhí)行同步讀寫操作;
IOPCAsyncIO接口允許客戶對服務(wù)器執(zhí)行異步讀寫操作;
IOPCAsyncIO2接口用來替代IOPCAsyncIO接口;
IConnectionPointContainer接口允許用戶探查發(fā)現(xiàn)連接點;
IDataObject接口允許客戶和使用OPC數(shù)據(jù)流格式的組之間產(chǎn)生連接。
5.2 同步讀寫方式編程
編程部分限于篇幅只列出核心代碼,關(guān)于一些類型定義,接口描述均省略了。
(1) COM庫的初始化
在調(diào)用任何COM或OLE API函數(shù)之前,必須要用CoIntialize()函數(shù)來對COM庫進行初始化,為了關(guān)閉COM庫在最后一次調(diào)用COM庫后,要調(diào)用CoUnitialize()函數(shù)。使用Delphi開發(fā)時,事情就簡化了,人們只需要在程序中包括ComObj單元即可,這樣應(yīng)用程序在調(diào)用Application.Initialize()時會自動調(diào)用CoInitialize()函數(shù),而ComObj單元的finalization部分會自動調(diào)用CoUnitialize()函數(shù)。只需要一句代碼:
Uses ComObj;
(2) 創(chuàng)建服務(wù)器對象
Const ServerProgID = 'hua.da2.1';//OPC服務(wù)器的注冊名稱
Var
ServerIf: IOPCServer;//聲明服務(wù)器對象接口
HR: HResult;//用來保存函數(shù)返回值
ServerIf := CreateComObject(ProgIDToClassID(ServerProgID)) as IOPCServer;
/ /本函數(shù)用來獲取服務(wù)器對象的IOPCServer接口,這是一個COM庫函數(shù)
(3) 添加組對象
Var
GroupIf: IOPCItemMgt;
GroupHandle: OPCHANDLE;
HR:=ServerAddGroup(ServerIf, 'MyGroup', True, 500, 0, GroupIf, GroupHandle);
//本函數(shù)用來對IOPCServer.AddGroup方法進行包裝,在服務(wù)器對象中添加一個名為MyGroup的組對象,激活狀態(tài)為true,更新率為500毫秒。如果組對象添加成功,則組對象接口保存在GroupIf中,組對象句柄保存在GroupHandle中。其實現(xiàn)過程如下:
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);
//本函數(shù)用來對IOPCItemMgt.AddItems進行包裝,在組對象中添加一個類型為VT_EMPTY,名稱為item.hua.bstr的項目,如果添加成功,則項目句柄保存在Item0Handle中,實際項目類型保存在ItemType中。其實現(xiàn)過程如下:
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);
//本函數(shù)用來同步讀取組中的項目值,如果讀取成功,則項目值保存在ItemValue中,項目質(zhì)量保存在ItemQuality中,其實現(xiàn)過程如下:
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);
//本函數(shù)用來將ItemValue同步寫入Item0Handle所代表的項目中,其實現(xiàn)過程如下:
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) 斷開服務(wù)器連接
HR := ServerIf.RemoveGroup(GroupHandle, False);
//本函數(shù)用來將在服務(wù)器中創(chuàng)建的組GroupHandle刪除,服務(wù)器對象在程序結(jié)束時會自動銷毀。
5.3 異步讀寫方式編程
使用異步方式進行讀寫編程,需要客戶提供IOPCDataCallback接口,還需要使用到COM的連接點容器,連接點和接收器等相關(guān)知識。以下關(guān)于異步讀寫方式編程中和同步方式相同的部分均未給出其實現(xiàn)部分。下面給出異步讀寫方式編程的主要步驟:
(1) COM庫的初始化 //實現(xiàn)過程同上
(2) 創(chuàng)建服務(wù)器對象 //實現(xiàn)過程同上
(3) 添加組對象 //實現(xiàn)過程同上
(4) 添加項目 //實現(xiàn)過程同上
(5) 實現(xiàn)IOPCDataCallback接口
為了使用連接點,客戶必須創(chuàng)建同時支持IUnknown和IOPCDataCallback接口的對象。下面的TOPCDataCallback從TInterfacedObject繼承所以支持IUnknown接口,它實現(xiàn)了IOPCDataCallback接口。
type
// 本類用來接收 IConnectionPointContainer 的數(shù)據(jù)變化回調(diào)
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接口
客戶程序除了實現(xiàn)接收器對象外,還必須建立接收器與連接點對象之間的連接關(guān)系
var
AsyncConnection: Longint;
OPCDataCallback: IOPCDataCallback;
OPCDataCallback := TOPCDataCallback.Create;//創(chuàng)建接收器對象
HR := GroupAdvise2(GroupIf, OPCDataCallback, AsyncConnection);
//本函數(shù)實現(xiàn)了接收器對象與連接點對象之間建立關(guān)系的過程,如果連接成功則用AsyncConnection來標識這個連接,其具體實現(xiàn)如下:
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) 異步讀寫
客戶進行異步讀寫操作只需要簡單的調(diào)用IOPCAsyncIO2接口的Read,Write等方法,這個過程不需要客戶程序等待,函數(shù)的返回值中并不包括操作的結(jié)果,而操作的具體結(jié)果會由服務(wù)器通過調(diào)用客戶的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;
當服務(wù)器中的數(shù)據(jù)發(fā)生變化時,服務(wù)器會調(diào)用客戶的IOPCDataCallback接口中OnDataChange函數(shù),當服務(wù)器異步讀完成后會調(diào)用客戶的IOPCDataCallback接口中OnReadComplete函數(shù),當服務(wù)器異步寫完成后會調(diào)用客戶的IOPCDataCallback接口中OnWriteComplete函數(shù),當服務(wù)器撤銷操作完成后會調(diào)用客戶的IOPCDataCallback接口中OnCancelComplete函數(shù)。限于篇幅,這里只給出客戶的IOPCDataCallback接口中OnDataChange函數(shù)實現(xiàn),其他函數(shù)的實現(xiàn)過程類似。
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接口連接,其實現(xiàn)過程如下:
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) 斷開服務(wù)器連接 //實現(xiàn)過程同上
6 結(jié)語
使用OPC的定制接口進行客戶程序的編程,需要一些COM知識的理解以及對于以下開發(fā)工具的熟練使用,因此相對于自動化接口的編程來說時比較困難的,但是使用定制接口的效率和靈活性卻是使用其他方式所無法比擬的。本文只是粗淺的介紹了在Delphi環(huán)境下使用定制接口進行OPC客戶程序的開發(fā),希望能達到拋磚引玉的效果。