OPC client (C article --OPC1.0, 2.0 specification)

xiaoxiao2021-03-06  41

OPC Technology Forum http://www.opc-china.com OPC server, customer program technology discussion OPC Client Program (C language articles, OPC1.0, 2.0 specification)

When this program is formerly personal learning, downloaded on foreign websites, I originally wanted to rewrite an article, because of all the reasons, did not write, so now put the downloaded procedure for everyone to learn. This program feels worth seeing, although it looks a bit long.

This program includes three files: opc.idl opccomn.idl opctest.cpp

Opc.idl opccomn.idl is the IDL definition of the OPC specification, opctest.cpp is the main program.

The opctest.cpp file is as follows:

// SST Win32 Console OPC Client EXAMPLE

// Copyright? 1998-1999 SST, A DIVISION OF WOODHEAD CANADA LIMITED

// www.sstech.on.ca

//

// Created by Richard Illes

// May 21, 1998

// async Updates Added June 10, 1998

// Simple Write Added June 24, 1998

// async reads added july 20, 1998

// logging added July 27, 1998

// Cache / Device Added August 8, 1998

// Version 2.0 Support Added August 18, 1998

//

// this is a sample console win32 client this

// does sync / async reads at 100ms Intervals

// with Up to 10 items

//

// critical sections are used for async calls to keep track of the

// Transaction ID. This Slows The Response Rate Down, But Ensures All

// Calls Are Completed. An Alternative, The Client CAN Place Transaction ID's

// INTO a Que from onDatachange () And after a askYNC CALL IS Completed. THEN A

// Watchdog Thread After a set timeout period can check Both Ques to See iF the

// Transaction completed. or the client can size ignore transaction ID's and

// Use the client handle returned as value.

//

// OPC Version 2.0 Negates The NEED for Critical Sections, Since The Client

// generates the transaction ID Before The Read / Write IS Called.

//

// disclaimer:

// this sample code is provided by sst solilaly to assist in understanding

// The OPC Data Access Specification In Relation to a SST OPC Server.

// this code is provided as-is and welcome warranty or support of any sort.//

// this code May be Freely Re-use long as credit is Openly Given

// TO SST.

//

//

#define strict

#define vc_extralean

#ifndef _win32_dcom

#define_win32_dcom // Winnt 4.0 or Win95 W / DCOM

#ENDIF

#define _atl_free_threaded

#include

#include

#include

#include

#include

// You May Derive a class from ccommodule and use it ing you want to override

// Something, But Do Not Change The Name of_Module

CCOMMODULE _MODULE;

#include

#include

// Check for Visual C 5 W / SP3

#if _atl_ver <0x0202

#Error Minimum Requirements: Visual C 5 W / SP3

#ENDIF

#include "opc_i.c"

#include "opc.h"

#include "opccomn_i.c"

#include "opccomn.h"

#define max_Keylen 256

#define max_Items 10

DWORD g_dwupdaterate = 100;

DWORD g_dwclienthandle = 1;

DWORD G_DWNUMITEMS = 0;

BOOL g_bwriteenable = false;

BOOL G_BWRITECOMPLETE = TRUE;

BOOL G_BREADCOMPLETE = TRUE;

Bool g_bpoll = false; // poll for valuees or askNC Updates

BOOL G_BVER2 = false; // version 2.0 Flag

Opchandle g_hclientgroup = 0;

IOPCSERVER * G_PIOPCSERVER = NULL;

DWORD g_dwupdateTransid = 1;

DWORD g_dwcancelid = 1;

DWORD g_dwreadtransid = 1;

DWORD g_dwwritetransid = 2;

FILE * g_stream = null; // file log handle

// Group Interfaces

IdataObject * g_pidataobject = null; //opc1.0 specification

IopcgroupStatemgt * g_piopcgroupstatemgt = null;

Iopcasyncio * g_piopcasyncio = NULL; //opc1.0 specification

IOPCSYNCIO * g_piopcsyncio = null; IOPCITEMMGT * g_piopcitemmgt = NULL;

Iopcasyncio2 * g_piopcasyncio2 = NULL;

IOPCCMMON * g_piopccommon = NULL;

IUNKNOWN * G_PIGROUNKNOWN = NULL;

IopcbrowServeraddresSpace * g_piopcbrowse = null;

// critical section stuff

CCOMAUTOCRITICATION G_READCS;

Ccomautocriticalsection g_writecs;

Class Clock

{

PUBLIC:

CCOMAUTOCRITICATION * M_PCS;

Clock (ccomautocriticalsection * pcs) {m_pcs = pcs; pcs-> lock ();}

~ Clock () {m_pcs-> unlock ();

}

#define read_lock clock gl (& g_readcs);

#define Write_lock Clock GL (& g_write);

Class ATL_NO_VTABLE CTESTADVISESINK;

Class ATL_NO_VTABLE COPCCALLBACK;

TypedEf CComObject ccomcteathage

TypedEf CComObject ccomcopcCallback;

Uint g_nopcformatdata = :: registerclipboardFormat ("opcstmformatdata);

Uint g_nopcformatdata = :: registerclipboardformat ("opcstmformatdataatime);

Uint g_nopcformatwrite = :: registerclipboardformat ("opcstmformatwritecomplete);

// Prototypes

INT opcstart ();

INT opcstop ();

INT GetStatus (Word * PWMAV, Word * PWMIV, Word * PWB, LPWSTR * PSWZV);

Int additems ();

Void syncread (bool bflag);

Int asyncread (bool bflag);

Int asyncupdate ();

Void ShrowError (HRESULT HR, LPCSTR PSZERROR);

Void Starterrorlog ();

Void enderrorlog ();

LPCSTR getDatetime ();

BOOL VERSION2 ();

INT async2read (bool bflag);

Int async2update ();

// struct's and classes

Struct structItem

{

Wchar wszname [100];

VARTYPE VT;

DWORD HCLIENT;

DWORD HSERVER;

} TestItem [10];

void main () // main

{

Printf ("SST Win32 Console OPC Client Example./nversion: 1999.04.06 / N / N"); starterrorlog ();

INT nret = opcstart (); // connect to a server

IF (NRET) exit (nret);

NRET = additems (); // add some items

IF (NRET) exit (nret);

Char szbuffer [50];

IF (! g_bver2)

{

PRINTF ("/ NPERFORM SYNC READS, Async Reads or Async Updates (S / A / U)?");

}

Else // Version 2.0 HAS More Options

{

Printf ("/ n1) SYNC READS / N2) Async Reads / N3) Async Updates / N4) Async2 Reads / N5) ConnectionPoint Updates / N");

PRINTF ("SELECT (1 - 5)?");

}

_flushall ();

Gets (szbuffer);

IF ((* szbuffer == 'a') || (* szbuffer == 'a') || (* szbuffer == '2')))

{

PRINTF ("Read from Cache or Device (C / D)?");

Gets (szbuffer);

IF (* szbuffer == 'c') || (* szbuffer == 'c')))

AsyncRead (True);

Else

AsyncRead (false);

}

ELSE IF (* szbuffer == 's') || (* szbuffer == 's') || (* szbuffer == '1')))

{

PRINTF ("Read from Cache or Device (C / D)?");

Gets (szbuffer);

IF (* szbuffer == 'c') || (* szbuffer == 'c')))

SyncRead (TRUE);

Else

SyncRead (False);

}

ELSE IF (* szbuffer == '4')

{

PRINTF ("Read from Cache or Device (C / D)?");

Gets (szbuffer);

IF (* szbuffer == 'c') || (* szbuffer == 'c')))

Async2read (true);

Else

Async2read (false);

}

ELSE IF (* szbuffer == '5')

{

Async2update ();

}

Else

{

Asyncupdate ();

}

NRET = opcstop (); // done with server

Enderrorlog ();

Exit (nret);

// HEAP ERROR ON EXIT?

}

Void SyncRead (Bool Bflag)

{

OpcitemState * pitemstate = null;

HRESULT * PERRORS = NULL;

HRESULT HR = 0; // Check for Dupes

INT dupBool = 0;

INT DUPI2 = 0;

Long dupi4 = 0;

FLOAT DUPR4 = 0.0f;

Double Dupr8 = 0.0;

IF (g_bwriteenable)

Printf ("Performing Sync Reads / Write ... Press a key to exit./N");

Else

Printf ("Performing Sync Reads ... Press A Key to EXIT./N");

Opchandle Hserve [MAX_ITEMS];

Variant Val [MAX_ITEMS];

Variant vcount;

For (DWORD DW = 0; DW

{

HSERVER [DW] = TestItem [DW] .hserver;

:: VariantInit (& Val [dw]);

}

:: VariantInit (& vcount);

V_vt (& vcount) = vt_i2;

V_i2 (& vcount) = 0;

HRESULT * PERRORSWRITE = NULL;

// Loop Around Doing Sync Reads UnTil User Hits a KEY

While (! _ kbhit ())

{

// r r t server

HR = g_piopcsyncio-> read (bflag? OPC_DS_CACHE: OPC_DS_DEVICE,

g_dwnumitems,

& hserver [0],

& pitemstate,

& perrors);

IF (hr == s_ok)

{

For (dw = 0; DW

{

Switch (V_VT (& PitemState [dw] .vdatavalue))

{

Case vt_bool:

IF (V_Bool (& PitemState [DW] .vdatavalue)! = dupbool)

Printf ("% D / T", V_Bool (& PitemState [dw] .vdatavalue);

Break;

Case vt_i2:

DEFAULT:

IF (V_i2 (& PitemState [dw] .vdatavalue)! = DUPI2)

Printf ("% D / T", V_i2 (& PitemState [DW] .vdataValue);

Break;

Case vt_i4:

IF (V_i4 (& PitemState [dw] .vdatavalue)! = DUPI4)

Printf ("% ld / t", v_i4 (& PitemState [dw] .vdatavalue);

Break;

Case VT_R4:

IF (V_R4 (& PitemState [dw] .vdatavalue)! = DUPR4)

Printf ("% f / t", V_R4 (& PitemState [dw] .vdatavalue);

Break;

Case vt_r8:

IF (V_R8 (& PitemState [dw] .vdatavalue)! = DUPR8)

Printf ("% lf / t", v_r8 (& PitemState [dw] .vdatavalue);

Break;

Case VT_BSTR:

Printf ("% ls / t", v_bstr (& PitemState [dw] .vdatavalue);

Break;

}

}

Printf ("/ r");

:: cotaskmemfree (pitemstate);

:: cotaskmemfree (perrors);

}

Else IF (hr == s_false)

{

For (dw = 0; DW

{

IF (failed (PERRORS [DW]))

{

Char SZ [100];

Sprintf (SZ, "Syncio-> Read (% ls) returned", TestItem [DW] .wszname);

ShowError (PerroS [dw], sz);

}

}

}

Else

{

ShowError (HR, "Sync Read");

}

IF (g_bwriteenable) // Quick Write enable Hack

{

// pump out data sync to items

For (dw = 0; DW

{

V_vt (& val [dw]) = vt_i2;

:: VariantCopy (& Val [dw], & vcount);

:: VariantChangeType (& Val [DW], & Val [DW], 0, V_VT (& TestItem [DW]));

}

V_i2 (& vcount) ;

IF ((& TestItem [0]) == vt_bool) && (v_i2 (& vcount)> 1)))

{

V_i2 (& vcount) = 0; // allow bool to Toggle ON / OFF

}

HR = g_piopcsyncio-> Write (g_dwnumitems, hserver, val, & perrorswrite);

IF (Failed (HR))

{

ShowError (HR, "Syncio-> Write ()");

}

Else IF (hr == s_false)

{

For (dw = 0; DW

{

IF (Failed (PerrorD (PERRORSWRITE [DW])))

{

ShowError (PerrorSwrite [dw], "syncio-> write () item returned");

}

}

:: cotaskmemfree (perrorswrite);

}

ELSE // S_OK

{

:: cotaskmemfree (perrorswrite);

}

}

:: Sleep (g_dwupdaterate); // Sleep BetWeen Updates

}

For (dw = 0; DW

{

:: VariantClear (& Val [dw]);

}

}

// Advisesink Class Derived from IadviseSink

// used with async updates

Class ATL_NO_VTABLE CTESTADVISESINK:

Public CComObjectRoot,

Public IadviseSink

{

PUBLIC:

Begin_COM_MAP (CTestAdViseSink)

COM_INTERFACE_ENTRY (IADVISESINK) END_COM_MAP ()

STDMETHODIMP_ (VOID) ONVIEWCHANGE (DWORD, long) {};

STDMETHODIMP_ (VOID) OnRename (LPMoniker) {};

STDMETHODIMP_ (VOID) ONSAVE (Void) {};

STDMETHODIMP_ (VOID) OnClose (Void) {};

STDMETHODIMP_ (VOID) OONDATACHANGE (LPSTGMEDIUM PSTM)

{

// Verify the Format Follows the OPC Spec

IF (TYMED_HGLOBAL! = PFE-> TYMED) RETURN;

IF (PSTM-> hglobal == 0) return;

IF (PFE-> cfformat == g_nopcformatwrite)

{

WRITE_LOCK;

Const lpbyte pBuffer = reinterpret_cast (:: Globalock (PSTM-> Hglobal);

IF (pBuffer == null) return;

Const opcgroupheaderwrite * pheader = reinterpret_cast (PBUFFER);

IF (Failed (Phet-> HRSTATUS))

{

ShowError (Phet-> Hrstatus, "General Async Write");

}

IF (g_dwwritetransid! = pheter-> dwtransaction)

{

ShowError (S_ok, "Async Write Callback, TransactionId's Do Not Match");

}

DWORD DWSIZE = SizeOf (OpcGroupHeaderwrite);

For (DWORD DW = 0; DW DwItemcount; dw , dwsize = sizeof (opcitemheaderwrite))

{

Const opcitemheaderwrite * pitemheader = reinterpret_cast (& PBuffer [dwsize]);

IF (Failed (Pitemheader-> dwerror))

{

ShowError (Pitemheader-> Dwerror, "async write request");

}

}

g_bwritecomplete = true;

:: GlobalUnlock (PSTM-> Hglobal);

Return;

}

Else IF (pfe-> cfformat! = g_nopcformatdataure) return;

Const lpbyte pBuffer = reinterpret_cast (:: Globalock (PSTM-> Hglobal);

IF (pBuffer == null) return;

Const opcgroupheader * phet = reinterpret_cast (PBuffer);

IF (Faader-> HRSTATUS) {

ShowError (Phet-> Hrstatus, "General Async Read");

}

IF (g_bpoll)

{

// if we are polling, ignore async updates

IF (Phet-> DWTRANSACTIONID == 0)

{

Return;

}

Read_lock;

IF (Pheter-> DWTransactionID! = g_dwreadtransid)

{

ShowError (S_ok, "Async Read Callback, TransactionId's Do Not Match");

Return;

}

IF (! g_breadcomplete) g_breadcomplete = true;

}

DWORD DWSIZE = SizeOf (OpcGroupHeader);

For (DWORD DW = 0; DW dwItemcount; dw , dwsize = sizeof (opcitemheader1))

{

Const opcitemheader1 * pitemhead = reinterpret_cast (& PBuffer [dwsize]);

IF (PitemHeader-> wquality == opc_quality_good)

{

Variant * pvalue = reinterpret_cast (& PBuffer [Pitemheader-> DWValueOffset]);

Switch (V_VT (PVALUE))

{

Case vt_bool:

Printf ("% d / t", v_bool (pValue));

Break;

Case vt_i2:

Printf ("% d / t", v_i2 (pValue);

Break;

Case vt_i4:

Printf ("% ld / t", v_i4 (pValue);

Break;

Case VT_R4:

Printf ("% f / t", v_r4 (pValue);

Break;

Case vt_r8:

Printf ("% LF / T", V_R8 (PVALUE));

Break;

Case VT_BSTR:

Printf ("% ls / t", v_bstr (pValue));

Break;

DEFAULT:

IF (succeededed (:: VariantchanGType (PVALUE, PVALUE, 0, VT_I4))))

Printf ("% ld / t", v_i4 (pValue);

Else

Printf ("*** / t");

Break;

}

}

Else

{

Switch (Pitemheader-> wquality)

{

CASE OPC_QUALITY_BAD:

DEFAULT:

ShowError (S_OK, "Quality Bad");

Break;

Case opc_quality_uncertain:

ShowError (S_OK, "Quality Uncertain);

Break;

Case opc_quality_config_error:

ShowError (S_OK, "config error");

Break;

CASE OPC_QUALITY_NOT_CONNECTED:

ShowError (S_OK, "Not Connected"); Break;

CASE OPC_QUALITY_DEVICE_FAILURE:

ShowError (S_OK, "Device Failure");

Break;

CASE OPC_QUALITY_OUT_OF_SERVICE:

ShowError (S_OK, "Out of Service");

Break;

}

}

}

Printf ("/ r");

:: GlobalUnlock (PSTM-> Hglobal);

}

}

Int asyncupdate ()

{

HRESULT HR = 0;

Formatetc formatetc;

DWORD dwupdateConnection = 0;

DWORD dwwriteconnection = 0;

Formatetc.cfformat = g_nopcformatdata;

// NEED TO FILL THE REST OF THE STRUCT or THE Proxy Make Puke

Formatetc.pt = NULL;

Formatetc.dwaspect = dvaspect_content;

Formatetc.lindex = -1;

Formatetc.Tymed = TYMED_HGLOBAL;

CComcTestAdvisesink * psink = null;

Atltry (psink = new ccomcteestadvises);

IF (psink == null)

{

ShowError (E_OUTOFMEMORY, "New CtestadViseSink);

Return 1;

}

HR = g_pidataObject-> DADVISE (& Formatetc, 0, Psink, & DwupdateConnection);

IF (Failed (HR))

{

ShowError (HR, "DADVISE (DataTime));

Return 1;

}

IF (g_bwriteenable)

{

Formatetc.cfformat = g_nopcformatwrite;

HR = g_pidataObject-> DADVISE (& Formatetc, 0, Psink, & DWRITECONNECTION);

IF (Failed (HR))

{

ShowError (HR, "DADVISE (WRITE)");

Return 1;

}

Printf ("Performing Async Updates / Write ... Press A Key To EXIT./N");

}

Else

Printf ("Performing Async Updates ... Press A Key To EXIT./N");

Opchandle Hserve [MAX_ITEMS];

Variant Val [MAX_ITEMS];

Variant vcount;

DWORD DW = 0;

IF (g_bwriteenable)

{

For (dw = 0; DW

{

HSERVER [DW] = TestItem [DW] .hserver;

:: VariantInit (& Val [dw]);

}

}

:: VariantInit (& vcount);

V_vt (& vcount) = vt_i2;

V_i2 (& vcount) = 0;

HRESULT * PERRORSWRITE = NULL;

// Nap While Server Does ITS Callback

While (! _ kbhit ())

{

:: Sleep (0);

IF (g_bwriteenable && g_bwritecomplete)

{

// pump out data async to items

For (dw = 0; DW

{

V_vt (& val [dw]) = vt_i2;

:: VariantCopy (& Val [dw], & vcount);

:: VariantChangeType (& Val [DW], & Val [DW], 0, V_VT (& TestItem [DW]));

}

V_i2 (& vcount) ;

IF ((& TestItem [0]) == vt_bool) && (v_i2 (& vcount)> 1)))

{

V_i2 (& vcount) = 0; // allow bool to Toggle ON / OFF

}

g_bwritecomplete = false;

g_writecs.lock (); // Lock Callbacks Until We get transid

HR = g_piopcasyncio-> Write (DWRITECONNECTION, G_DWNUMITEMS, HSERVER, VAL, & G_DWWWRITETRANSID, & PERRORSWRITE);

g_writecs.unlock ();

IF (Failed (HR))

{

ShowError (HR, "asyncio-> write ()");

}

Else IF (hr == s_false)

{

For (dw = 0; DW

{

IF (Failed (PerrorD (PERRORSWRITE [DW])))

{

ShowError (PerrorSwrite [dw], "asyncio-> write () item returned");

}

}

:: cotaskmemfree (perrorswrite);

}

ELSE // S_OK

{

:: cotaskmemfree (perrorswrite);

}

}

}

IF (g_bwriteenable)

{

For (dw = 0; DW

{

:: VariantClear (& Val [dw]);

}

}

:: VariantClear (& vcount);

HR = g_pidataObject-> Dunadvise (dwupdateConnection);

IF (Failed (HR))

{

ShowError (HR, "Dunadvise (DataTime));

}

IF (g_bwriteenable)

{

HR = g_pidataObject-> Dunadvise (dwwriteconnection);

IF (Failed (HR))

{

ShowError (HR, "Dunadvise (Write");

}

}

Return 0;

}

Int asyncread (Bool Bflag)

{

HRESULT HR = 0;

Formatetc formatetc;

DWORD DWREADCONNECTION = 0;

DWORD dwriteconnection = 0; g_bpoll = true; // We are polling for value

Formatetc.cfformat = g_nopcformatdata;

// NEED TO FILL THE REST OF THE STRUCT or THE Proxy Make Puke

Formatetc.pt = NULL;

Formatetc.dwaspect = dvaspect_content;

Formatetc.lindex = -1;

Formatetc.Tymed = TYMED_HGLOBAL;

CTestAdVisesink * psink = null;

Atltry (psink = new ccomcteestadvises);

IF (psink == null)

{

ShowError (E_OUTOFMEMORY, "New CtestadViseSink);

Return 1;

}

HR = g_pidataobject-> DADVISE (& Formatetc, 0, Psink, & dwreadConnection);

IF (Failed (HR))

{

ShowError (HR, "DADVISE (DataTime));

Return 1;

}

IF (g_bwriteenable)

{

Formatetc.cfformat = g_nopcformatwrite;

HR = g_pidataObject-> DADVISE (& Formatetc, 0, Psink, & DWRITECONNECTION);

IF (Failed (HR))

{

ShowError (HR, "DADVISE (WRITE)");

Return 1;

}

Printf ("Performing Async Reads / Write ... Press A Key to EXIT./N");

}

Else

Printf ("Performing Async Reads ... Press A Key To EXIT./N);

Opchandle Hserve [MAX_ITEMS];

Variant Val [MAX_ITEMS];

Variant vcount;

DWORD DW = 0;

IF (g_bwriteenable)

{

For (dw = 0; DW

{

HSERVER [DW] = TestItem [DW] .hserver;

:: VariantInit (& Val [dw]);

}

}

:: VariantInit (& vcount);

V_vt (& vcount) = vt_i2;

V_i2 (& vcount) = 0;

HRESULT * PERRORSWRITE = NULL;

// Nap While Server Does ITS Callback

While (! _ kbhit ())

{

IF (g_bwriteenable && g_bwritecomplete)

{

// pump out data async to items

For (dw = 0; DW

{

V_vt (& val [dw]) = vt_i2;

:: VariantCopy (& Val [dw], & vcount);

:: VariantchanGType (& Val [DW], & Val [DW], 0, V_VT (& TestItem [DW]);}

V_i2 (& vcount) ;

IF ((& TestItem [0]) == vt_bool) && (v_i2 (& vcount)> 1)))

{

V_i2 (& vcount) = 0; // allow bool to Toggle ON / OFF

}

g_bwritecomplete = false;

g_writecs.lock (); // Lock Callbacks Until We get transid

// Write to One Item

HR = g_piopcasyncio-> Write (DWRITECONNECTION, G_DWNUMITEMS, HSERVER, VAL, & G_DWWWRITETRANSID, & PERRORSWRITE);

g_writecs.unlock ();

IF (Failed (HR))

{

ShowError (HR, "asyncio-> write ()");

}

Else IF (hr == s_false)

{

For (dw = 0; DW

{

IF (Failed (PerrorD (PERRORSWRITE [DW])))

{

ShowError (PerrorSwrite [dw], "asyncio-> write () item returned");

}

}

:: cotaskmemfree (perrorswrite);

}

ELSE // S_OK

{

:: cotaskmemfree (perrorswrite);

}

}

IF (g_breadcomplete)

{

g_breadcomplete = false;

g_readcs.lock (); // Lock Callbacks Until We get Transid

// read all Items in group

HR = g_piopcasyncio-> refresh (dwreadConnection,

BFLAG? OPC_DS_CACHE: OPC_DS_DEVICE,

& g_dwreadtransid;

g_readcs.unlock ();

IF (Failed (HR))

{

ShowError (HR, "asyncio-> refresh ()");

}

}

:: Sleep (g_dwupdaterate);

}

IF (g_bwriteenable)

{

For (dw = 0; DW

{

:: VariantClear (& Val [dw]);

}

}

:: VariantClear (& vcount);

HR = g_pidataObject-> Dunadvise (dwreadConnection);

IF (Failed (HR))

{

ShowError (HR, "Dunadvise (DataTime));

}

IF (g_bwriteenable)

{

HR = g_pidataObject-> Dunadvise (dwwriteconnection);

IF (Failed (HR))

{

ShowError (HR, "Dunadvise (Write");

}

}

Return 0;

}

// AdviseSink Class Derived from IopcDatacAllback

// used with async updates

Class ATL_NO_VTABLE COPCCALLBACK:

Public CComObjectRoot,

Public IopcDatacAllback

{

PUBLIC:

Begin_COM_MAP (CopcCallback)

COM_Interface_entry (IOPCDataCallback)

END_COM_MAP ()

STDMETHODIMP ONDATACHANGE

/ * [in] * / dword dwtransid,

/ * [in] * / opchandle hgroup,

/ * [in] * / hResult HrmasterQuality,

/ * [in] * / HRESULT HRMASTERERROR,

/ * [in] * / DWORD DWCOUNT,

/ * [size_is] [in] * / opchandle __rpc_far * PhclientItems,

/ * [size_is] [in] * / variant __rpc_far * pvrough,

/ * [size_is] [in] * / word __rpc_far * pwqualities,

/ * [size_is] [in] * / filetime __rpc_far * pfttimestamps,

/ * [size_is] [in] * / hResult __rpc_far * perrors)

{

IF (Failed (Hrmastererror))

{

ShowError (Hrmastererror, "General ConnectionPoint Update);

}

For (DWORD DW = 0; DW

{

IF ((PWQualities [DW] == OPC_QUALITY_GOOD) && succeeded (PERRORS [DW]))

{

Variant * pvalue = & pvvalues ​​[dw];

Switch (V_VT (PVALUE))

{

Case vt_bool:

Printf ("% d / t", v_bool (pValue));

Break;

Case vt_i2:

Printf ("% d / t", v_i2 (pValue);

Break;

Case vt_i4:

Printf ("% ld / t", v_i4 (pValue);

Break;

Case VT_R4:

Printf ("% f / t", v_r4 (pValue);

Break;

Case vt_r8:

Printf ("% LF / T", V_R8 (PVALUE));

Break;

Case VT_BSTR:

Printf ("% ls / t", v_bstr (pValue));

Break;

DEFAULT:

IF (succeededed (:: VariantchanGType (PVALUE, PVALUE, 0, VT_I4))))

Printf ("% ld / t", v_i4 (pValue);

Else

Printf ("*** / t");

Break;

}

}

ELSE // Else IF

{

Switch (PWQualities [DW])

{

Case opc_quality_good:

ShowError (S_OK, "Quality Good");

Break;

CASE OPC_QUALITY_BAD:

DEFAULT:

ShowError (S_OK, "Quality Bad");

Break;

Case opc_quality_uncertain:

ShowError (S_OK, "Quality Uncertain); Break;

Case opc_quality_config_error:

ShowError (S_OK, "config error");

Break;

CASE OPC_QUALITY_NOT_CONNECTED:

ShowError (S_OK, "Not Connected");

Break;

CASE OPC_QUALITY_DEVICE_FAILURE:

ShowError (S_OK, "Device Failure");

Break;

CASE OPC_QUALITY_OUT_OF_SERVICE:

ShowError (S_OK, "Out of Service");

Break;

}

} // Endif

} // end for

Printf ("/ r");

Return S_OK;

}

STDMETHODIMP OnReadcomplete

/ * [in] * / dword dwtransid,

/ * [in] * / opchandle hgroup,

/ * [in] * / hResult HrmasterQuality,

/ * [in] * / HRESULT HRMASTERERROR,

/ * [in] * / DWORD DWCOUNT,

/ * [size_is] [in] * / opchandle __rpc_far * PhclientItems,

/ * [size_is] [in] * / variant __rpc_far * pvrough,

/ * [size_is] [in] * / word __rpc_far * pwqualities,

/ * [size_is] [in] * / filetime __rpc_far * pfttimestamps,

/ * [size_is] [in] * / hResult __rpc_far * perrors)

{

IF (Failed (Hrmastererror))

{

ShowError (Hrmastererror, "General Async2 Read");

}

IF (dwtransid! = g_dwreadtransid)

{

ShowError (S_OK, "Async2 Read Callback, TransactionId's Do Not Match");

Return S_FALSE;

}

For (DWORD DW = 0; DW

{

IF ((PWQualities [DW] == OPC_QUALITY_GOOD) && succeeded (PERRORS [DW]))

{

Variant * pvalue = & pvvalues ​​[dw];

Switch (V_VT (PVALUE))

{

Case vt_bool:

Printf ("% d / t", v_bool (pValue));

Break;

Case vt_i2:

Printf ("% d / t", v_i2 (pValue);

Break;

Case vt_i4:

Printf ("% ld / t", v_i4 (pValue);

Break;

Case VT_R4:

Printf ("% f / t", v_r4 (pValue);

Break;

Case vt_r8:

Printf ("% LF / T", V_R8 (PVALUE));

Break;

Case VT_BSTR:

Printf ("% ls / t", v_bstr (pValue));

Break;

DEFAULT:

IF (succeeded (::4))) Printf ("% ld / t", v_i4);

Else

Printf ("*** / t");

Break;

}

}

ELSE // Else IF

{

Switch (PWQualities [DW])

{

Case opc_quality_good:

ShowError (S_OK, "Quality Good");

Break;

CASE OPC_QUALITY_BAD:

DEFAULT:

ShowError (S_OK, "Quality Bad");

Break;

Case opc_quality_uncertain:

ShowError (S_OK, "Quality Uncertain);

Break;

Case opc_quality_config_error:

ShowError (S_OK, "config error");

Break;

CASE OPC_QUALITY_NOT_CONNECTED:

ShowError (S_OK, "Not Connected");

Break;

CASE OPC_QUALITY_DEVICE_FAILURE:

ShowError (S_OK, "Device Failure");

Break;

CASE OPC_QUALITY_OUT_OF_SERVICE:

ShowError (S_OK, "Out of Service");

Break;

}

} // Endif

} // end for

g_breadcomplete = true;

Printf ("/ r");

Return S_OK;

}

STDMETHODIMP OnWriteComplete

/ * [in] * / dword dwtransid,

/ * [in] * / opchandle hgroup,

/ * [in] * / hResult Hrmastererr,

/ * [in] * / DWORD DWCOUNT,

/ * [size_is] [in] * / opchandle __rpc_far * PClientHandles,

/ * [size_is] [in] * / hResult __rpc_far * perrors)

{

IF (Failed (Hrmastererr))

{

ShowError (Hrmastererr, "General Async2 Write);

}

IF (g_dwwritetransid! = dwtransid)

{

Showerror (S_OK, "Async2 Write Callback, Transactionid's Do Not Match");

}

For (DWORD DW = 0; DW

{

IF (failed (PERRORS [DW]))

{

ShowError (PerroS [DW], "Async2 Write Request");

}

}

g_bwritecomplete = true;

Return S_OK;

}

STDMETHODIMP onCancelcomplete

/ * [in] * / dword dwtransid,

/ * [in] * / opchandle hgroup)

{

Return S_OK;

}

}

Int async2update ()

{

IF (g_piopcasyncio2 == null) Return 1; // not supported

IconnectionPointContainer * pcpc = null; iconnectionPoint * PCP = NULL;

DWORD DWCOOKIE = 0;

HRESULT HR = S_OK;

// Create the Sink

CCOMCOPCCALLBACK * PSINK = NULL;

ATLTRY (psink = new ccomcopcallback);

IF (psink == null)

{

ShowError (E_OUTOFMEMORY, "New CopcCallback");

Return 1;

}

// Obtain Connection Points

HR = g_pigroupunknown-> queryinterface (IID_ICONNECTIONPOINTCONTAINER, (void **) & pcpc);

IF (Failed (HR))

{

ShowError (HR, "Queryinterface (IID_ICONNECTIONPOINTCONTAINER);

Return 1;

}

HR = PCPC-> FindConnectionPoint (IID_IOPCDataCallback, & PCP);

IF (Failed (HR))

{

ShowError (HR, "FindConnectionPoint (IID_IOPCDatacAllback)");

Return 1;

}

HR = PCP-> Advise (psink, & dwcookie);

IF (Failed (HR))

{

ShowError (HR, "Advise ()");

Return 1;

}

IF (g_bwriteenable)

Printf ("Performing C.p. Updates / Async2 Write ... Press A Key to EXIT. /N");

Else

Printf ("Performing ConnectionPoint Updates ... Press A Key to EXIT./N");

Opchandle Hserve [MAX_ITEMS];

Variant Val [MAX_ITEMS];

Variant vcount;

DWORD DW = 0;

IF (g_bwriteenable)

{

For (dw = 0; DW

{

HSERVER [DW] = TestItem [DW] .hserver;

:: VariantInit (& Val [dw]);

}

}

:: VariantInit (& vcount);

V_vt (& vcount) = vt_i2;

V_i2 (& vcount) = 0;

HRESULT * PERRORSWRITE = NULL;

// Nap While Server Does ITS Callback

While (! _ kbhit ())

{

:: Sleep (0);

IF (g_bwriteenable && g_bwritecomplete)

{

// pump out data async to items

For (dw = 0; DW

{

V_vt (& val [dw]) = vt_i2;

:: VariantCopy (& Val [dw], & vcount);

:: VariantchanGType (& Val [DW], & Val [DW], 0, V_VT (& TestItem [DW]);}

V_i2 (& vcount) ;

IF ((& TestItem [0]) == vt_bool) && (v_i2 (& vcount)> 1)))

{

V_i2 (& vcount) = 0; // allow bool to Toggle ON / OFF

}

g_bwritecomplete = false;

HR = g_piopcasyncio2-> Write (g_dwnumitems, hserver, val, g_dwwritetransid, & g_dwcancelid, "); & perrorswrite;

IF (Failed (HR))

{

ShowError (HR, "asyncio2-> write ()");

}

Else IF (hr == s_false)

{

For (dw = 0; DW

{

IF (Failed (PerrorD (PERRORSWRITE [DW])))

{

ShowError (PERRORSWRITE [DW], "asyncio2-> write () item returned");

}

}

:: cotaskmemfree (perrorswrite);

}

ELSE // S_OK

{

:: cotaskmemfree (perrorswrite);

}

}

}

IF (g_bwriteenable)

{

For (dw = 0; DW

{

:: VariantClear (& Val [dw]);

}

}

:: VariantClear (& vcount);

// Release Interfaces

HR = PCP-> Unadvise (dwcookie);

IF (Failed (HR))

{

ShowError (HR, "Unadvise ()");

}

PCP-> Release ();

PCPC-> Release ();

While (psink-> relation ());

Return 0;

}

Int async2read (bool bflag)

{

IF (g_piopcasyncio2 == null) Return 1; // not supported

IConnectionPointContainer * PCPC = NULL;

IConnectionPoint * PCP = NULL;

DWORD DWCOOKIE = 0; // Advise Cookie

HRESULT HR = S_OK;

// Create the Sink

CCOMCOPCCALLBACK * PSINK = NULL;

ATLTRY (psink = new ccomcopcallback);

IF (psink == null)

{

ShowError (E_OUTOFMEMORY, "New CopcCallback");

Return 1;

}

// Obtain Connection Points

HR = g_pigroupunknown-> queryinterface (IID_ICONNECTIONPOINTCONTAINER, (void **) & pcpc);

IF (Failed (HR))

{

ShowError (HR, "Queryinterface"); Return 1;

}

HR = PCPC-> FindConnectionPoint (IID_IOPCDataCallback, & PCP);

IF (Failed (HR))

{

ShowError (HR, "FindConnectionPoint (IID_IOPCDatacAllback)");

Return 1;

}

HR = PCP-> Advise (psink, & dwcookie);

IF (Failed (HR))

{

ShowError (HR, "Advise ()");

Return 1;

}

g_piopcasyncio2-> setENABLE (false); // Turn Off Update Callbacks

IF (g_bwriteenable)

Printf ("Performing Async2 Reads / Writes ... Press A Key to EXIT./N");

Else

Printf ("Performing Async2 Reads ... Press A Key to EXIT./N");

Opchandle Hserve [MAX_ITEMS];

Variant Val [MAX_ITEMS];

Variant vcount;

For (DWORD DW = 0; DW

{

HSERVER [DW] = TestItem [DW] .hserver;

:: VariantInit (& Val [dw]);

}

:: VariantInit (& vcount);

V_vt (& vcount) = vt_i2;

V_i2 (& vcount) = 0;

HRESULT * PERRORSWRITE = NULL;

HRESULT * perrorsread = null;

// Nap While Server Does ITS Callback

While (! _ kbhit ())

{

IF (g_bwriteenable && g_bwritecomplete)

{

// pump out data async to items

For (dw = 0; DW

{

V_vt (& val [dw]) = vt_i2;

:: VariantCopy (& Val [dw], & vcount);

:: VariantChangeType (& Val [DW], & Val [DW], 0, V_VT (& TestItem [DW]));

}

V_i2 (& vcount) ;

IF ((& TestItem [0]) == vt_bool) && (v_i2 (& vcount)> 1)))

{

V_i2 (& vcount) = 0; // allow bool to Toggle ON / OFF

}

g_bwritecomplete = false;

// Write Items

HR = g_piopcasyncio2-> Write (g_dwnumitems, hserver, val, g_dwwritetransid, & g_dwcancelid, "); & perrorswrite;

IF (Failed (HR))

{

Showerror (HR, "asyncio2-> write ()");}

Else IF (hr == s_false)

{

For (dw = 0; DW

{

IF (Failed (PerrorD (PERRORSWRITE [DW])))

{

ShowError (PERRORSWRITE [DW], "asyncio2-> write () item returned");

}

}

:: cotaskmemfree (perrorswrite);

}

ELSE // S_OK

{

:: cotaskmemfree (perrorswrite);

}

}

IF (g_breadcomplete)

{

g_breadcomplete = false;

// read all Items in group

HR = g_piopcasyncio2-> read (g_dwnumitems, hserver, g_dwreadtransid, & g_dwcancelid, ");

IF (Failed (HR))

{

ShowError (HR, "asyncio2-> read ()");

}

Else IF (hr == s_false)

{

For (dw = 0; DW

{

IF (Failed (PERRORSREAD [DW]))

{

ShowError (PerroSread [DW], "asyncio2-> read () item returned");

}

}

:: CotaskMemfree (PerroSread);

}

ELSE // S_OK

{

:: CotaskMemfree (PerroSread);

}

}

:: Sleep (g_dwupdaterate);

}

For (dw = 0; DW

{

:: VariantClear (& Val [dw]);

}

:: VariantClear (& vcount);

// Release Interfaces

HR = PCP-> Unadvise (dwcookie);

IF (Failed (HR))

{

ShowError (HR, "Unadvise ()");

}

PCP-> Release ();

PCPC-> Release ();

While (psink-> relation ());

Return 0;

}

INT opcstart ()

{

// Browse Registry for OPC 1.0A Servers

HKEY HK = HKEY_CLASSES_ROOT;

Tchar szkey [max_keylen];

For (int NINDEX = 0; :: RegenumKey (HK, NINDEX, SZKEY, MAX_KEYLEN) == Error_Success; NINDEX )

{

HKEY HPROGID;

Tchar szdummy [max_keylen];

IF (:: regopenkey (hk, szkey, & hprogid) == Error_Success)

{

Long lsize = max_Keylen;

IF (: RegQueryValue (HProgid, "OPC", SZDummy, & lsize) == Error_Success)

{

Printf ("% s / n", szkey;}

:: regcloseKey (HProgID);

}

}

Wchar WSZServername [100];

TCHAR SZBUFFER [100];

Uses_Conversion;

Printf ("/ NENTER Server Name:);

_flushall ();

Gets (szbuffer);

WCSCPY (WSZServerName, T2W (SZBuffer);

// Enter '.' to Default To Sst Test Server

IF (WSZServerName == 0) || (* WSZServerName == L '.')) WCSCPY (WSZServerName, L "sst.simulatoropcsvr.1);

// enter '=' to Default to SST DHP Server

IF (* wszservername == l '=') WCSCPY (WSZServerName, L "sst.DataHighwayPlusopcsvr.1);

CLSID CLSID;

HRESULT HR = :: CLSIDFROMPROGID (WSZServerName, & Cls);

IF (Failed (HR))

{

ShowError (HR, "CLSIDFROMPROGID ()");

Return 1;

}

Printf ("Server ID Found./N");

HR = :: CoinitializeEx (NULL, COINIT_MULTITHREADED); // setup COM LIB

IF (Failed (HR))

{

ShowError (HR, "CoinitializeEx ()");

Return 1;

}

// Create a Running Object from That Class ID

// (CLSCTX_ALL WILL ALOW IN-Proc, local and remote)

HR = :: COCREATEINSTANCE (CLSID, NULL, CLSCTX_ALL, IID_IOPCSERVER, (Void **) & g_piopcserver;

IF (Failed (HR) || (g_piopcserver == null))

{

IF (FAILED (HR)) Showerror (HR, "CocreateInstance ()");

Printf ("You May Not Have Registered The OPC Proxy DLL! / N");

Return 1;

}

Printf ("Connected to Server./N");

Word WMAJOR, WMINOR, WBUILD

LPWSTR PWSZ = NULL;

IF (! GetStatus (& WMAJOR, & WMINOR, & WBUILD, & PWSZ))

{

Printf ("Version:% D.% D.% D / N", WMAJOR, WMINOR, WBUILD);

Printf ("% ls / n / n", pwsz);

:: cotaskmemfree (pwsz);

}

g_bver2 = version2 ();

IF (g_bver2)

{

Printf ("Server Supports OPC 2.0 Interfaces / N / N");

}

HR = g_piopcserver-> queryinterface (IID_IOPCBROWSESERVERVERADDRESSSSSPACE, (Void **) & g_piopcbrowse; if (Failed (HR))

{

ShowError (HR, "Queryinterface (IID_IOPCBROWSESERVERVERADDRESSSSPACE");

}

Float fTemp = 0.0f;

Long ltimebias = 0;

DWORD dwrevisedupdaterate = 0;

// Create An in-Active Group

// NOTE: 1st Param Must Not Be a null or the proxy will puke

HR = g_piopcserver-> addgroup (l ", // [in] Server name, if Null OPC Server Will Generate a Unique Name

True, // [in] State of Group To Add

g_dwupdaterate, // [in] Requested Update Rate for Group (MS)

1234, // [in] Client Handle To OPC Group

& ltimebias, // [in] TIME

& fTemp, // [in] Percent deadband

0, // [in] localization ID

& g_hclientgroup, // [out] Server Handle to Group

& dwrevisedUpdaterate, // [out] Revised Update Rate

IID_IUNKNOWN, / / ​​[in] Type of Interface Desired

& g_pigroupunknown; // [out] Where to store the interface Pointer

IF (Failed (HR))

{

ShowError (HR, "AddGroup ()");

g_piopcserver-> release ();

Return 1;

}

Printf ("Group Added, Update Rate =% ld. / n", dwrevisedupdaterate);

// Get Pointer to OPC Server Interfaces Required for this Program.

HR = g_pigroupunknown-> queryinterface (IID_IDataObject, (void **) & g_pidataObject);

IF (Failed (HR))

{

ShowError (HR, "Queryinterface");

}

HR = g_pigroupunknown-> queryinterface (IID_IOPCGroupStatemgt, (void **) & g_piopcgroupstatemgt);

IF (Failed (HR))

{

Showerror (HR, "Queryinterface (IID_IOPCGroupStatemgt");

HR = g_pigroupunknown-> queryinterface (IID_ID_IOPCASYNCIO, (Void **) & g_piopcasyncio);

IF (Failed (HR))

{

ShowError (HR, "Queryinterface (IID_IOPCASYNCIO)");

}

HR = g_pigroupunknown-> queryinterface (IID_IOPCITEMMGT, (Void **) & g_piopcitemmgt);

IF (Failed (HR))

{

ShowError (HR, "Queryinterface (IID_IOPCITEMMGT)");

}

HR = g_pigroupunknown-> queryinterface (IID_IOPCSYNCIO, (Void **) & g_piopcsyncio);

IF (Failed (HR))

{

ShowError (HR, "Queryinterface");

}

IF (g_bver2)

{

HR = g_pigroupunknown-> queryinterface (IID_IOPCASYNCIO2, (Void **) & g_piopcasyncio2);

IF (Failed (HR))

{

ShowError (HR, "Queryinterface (IID_IOPCASYNCIO2)")

}

HR = g_piopcserver-> queryinterface (iid_iopccommon, (void **) & g_piopccommon;

IF (Failed (HR))

{

ShowError (HR, "Queryinterface");

}

Else

{

g_piopccommon-> setClientName (L "SST WIN32 Simple Client");

}

}

//

IF (Failed (HR))

{

g_piopcserver-> release ();

Printf ("Error: Secondary Qi Failed / N");

Return 1;

}

IF (dwrevisedUpdaterate! = g_dwupdaterate)

{

g_dwupdaterate = dwrevisedupdaterate;

}

Printf ("Active Group Interface Added./N);

Return 0;

}

int OPCStop ()

{

// Terminate Server and It Will Clean Up Itself

IF (g_piopcserver) While (g_piopcserver-> release ());

:: Couninitialize ();

Printf ("Server and All Group Interfaces Terminated./N");

Return 1;

}

INT GetStatus (Word * Pwmav, Word * PWMIV, Word * PWB, LPWSTR * PSZV)

{

* PWMAV = 0;

* PWMIV = 0;

* PWB = 0;

* pszv = NULL;

OPCSerVersTatus * pstatus = null;

IF (g_piopcserver == null) Return E_POINTER; HRESULT HR = g_piopcser -> getStatus (& PSTATUS);

IF (failed (hr) || (pstatus == null) || (pstatus-> dwserverstate! = opc_status_running))

{

IF (Failed (HR)) Showerror (HR, "GetStatus ()");

IF (PStatus! = null) :: CotaskMemFree (Pstatus);

Return E_FAIL;

}

* PWMAV = PStatus-> wmajorversion;

* PWMIV = PSTATUS-> WMINORVERSION;

* PWB = PStatus-> wbuildnumber;

* pszv = pstatus-> szvendorinfo;

:: cotaskmemfree (pstatus);

Return 0;

}

// Simple Check for Version OPC 2.0 Type Connection Point Containers

BOOL VERSION2 ()

{

IF (g_piopcserver == null) Return False;

IConnectionPointContainer * PCPC = NULL;

IF (Failed (g_piopcserver-> queryinterface (iid_iconnectionpointcontainer, (void **) & pcpc)))

{

Return False;

}

PCPC-> Release ();

Return True;

}

Int additems ()

{

// Loop Until All Items Are Added

Char SZ2 [200];

Tchar Szbuffer [256];

HRESULT HR = 0;

INT NTESTITEM = 0; // How Many Items Twhere Are

Ienumstring * penumstring = null;

INT ncount = 0;

Uses_Conversion;

_flushall ();

HR = g_piopcbrowse-> browseopcitemids (opc_flat, l "" / * null * /, vt_empty, 0, & penumstring);

IF (Failed (HR))

{

Showerror (HR, _T ("Browseopcitemids ()"))

}

IF (hr == s_ok)

{

LpoLESTR pszname = null;

Ulong count = 0;

While ((hr = penumstring-> next (1, & pszname, & count) == S_OK)

{

Printf (_t ("% s / n"), OLE2T (PSZNAME));

:: CotaskMemfree (pszname);

IF (ncount > 22)

{

Printf ("** Press Any Key To Continue ** / N");

Gets (szbuffer);

Ncount = 0;

}

}

Penumstring-> Release ();

}

While (True)

{

_flushall ();

IF (nTestItem) {

PRINTF ("Add AN ITEM (Y / N)?");

Gets (szbuffer);

IF (_TCSICMP (SZBuffer, _T ("y"))))

}

Else

{

Printf ("Add items,");

}

Printf ("Enter Item Name:");

Gets (szbuffer);

WCSCPY (TestItem [NTESTITEM] .wszname, T2W (SZBuffer);

// Enter '.' to select a sample item from sst test server

IF (! WCSCMP (TestItem [NTESTITEM] .wszname, L ")) WCSCPY (TestItem [NTESTITEM] .wszname, l" simulated card.simulated node.random.i4 ");

Printf ("Enter ITEM TYPE (IE: VT_I4):");

Gets (SZ2);

// You can Enter '.' for vt_empty and the server will select the right type

IF (! STRCMP (SZ2, "))) STRCPY (SZ2," VT_EMPTY ");

IF (! Stricmp (Stricmp (SZ2, "VT_I2")) TestItem [NTESTITEM] .vt = vt_i2;

Else if (! Stricmp (Stricmp (SZ2, "VT_I4")) TestItem [NTESTITEM] .vt = vt_i4;

Else if (! Stricmp (Stricmp (SZ2, "VT_EMPTY)) TestItem [NTESTITEM] .vt = vt_empty;

Else if (! Stricsp (Stricmp (SZ2, "VT_R4")) TestItem [NTESTITEM] .vt = vt_r4;

Else if (! Stricmp (Stricmp (SZ2, "VT_R8")) TestItem [NTESTITEM] .vt = vt_r8;

Else if (! Stricmp (Stricmp (SZ2, "Vt_Bool")) TestItem [NTESTITEM] .vt = vt_bool;

Else

{

Printf ("Error: Valid Types: vt_empty, vt_i2, vt_i4, vt_r4, vt_r8, vt_bool / n");

CONTINUE;

}

OpciteMResult * piteMResult = null;

HRESULT * PERRORS = NULL;

Opcitemdef itemdef;

Itemdef.szaccesspath = l "";

Itemdef.szitemid = TestItem [NTESTITEM] .wszname;

Itemdef.bactive = true;

Itemdef.hclient = g_dwclienthandle ;

Itemdef.dwblobsize = 0;

Itemdef.pblob = null;

Itemdef.vtrequestedDataType = TestItem [NTESTITEM] .vt;

Testitem [NTESTITEM] .hclient = itemdef.hclient;

HR = g_piopcitemmgt-> additems (1, & itemdef, & pitemresult, "); if (Failed (HR))

{

ShowError (HR, "AddItem ()");

CONTINUE;

}

HR = S_OK;

IF (Failed (PerroS [0])))

{

Showerror (PERRORS [0], "AddItem () Item");

CONTINUE;

}

// Record Unique Handle for this Item

TestItem [NTESTITEM] .hserver = pitemResult-> hserver;

TestItem [nTestItem] .vt = pitemresult-> vtcanonicalDataType;

NTESTITEM ;

:: cotaskmemfree (pitemResult);

:: cotaskmemfree (perrors);

IF (NTESTITEM> = MAX_ITEMS) BREAK;

}

g_dwnumitems = ntestitem;

// Enumerate Items and Display

OpciteMattributes * pitemattr = null;

Ulong dwfetched = 0;

IEnumopciteMattributes * penumopcitems = null;

HR = g_piopcitemmgt-> createenumerator (IID_IENUMOPCITEMATRIBUTES, ReinterPret_cast );

En (ac))

{

Printf ("IOPCITEMMGT :: CreateEnumerator () / N");

Penumopcitems-> reset ();

// NOTE: 3rd Param Must Not Be a null or the proxy will puke

HR = penumopcitems-> next (static_cast (ntertainment), & pitemattr, & dwfetched;

En (ac))

{

IF ((dwfetched! = static_cast ) || (hr == s_false))

{

Printf ("Error: Penumopcitems-> next () - fetched! = Requested / N");

}

For (ulong i = 0; i

{

Printf ("item:% ls = vt_", pitemattr [i] .szitemid);

Switch (Pitemattr [i] .vtcanonicalDataType)

{

Case vt_i2:

Printf ("I2 (Short));

Break;

Case vt_i4:

DEFAULT:

Printf ("i4 (long));

Break;

Case VT_R4:

Printf ("R4 (FLOAT));

Break;

Case vt_r8:

PRINTF ("r8 (double));

Break;

Case vt_bool:

Printf ("Bool (Bool (Boolean)); Break;

Case VT_EMPTY:

Printf ("EMPTY (Server Defined));

Break;

}

Printf ("/ n");

IF (pitemattr [i] .szitemid)

:: cotaskmemfree (pitemattr [i] .szitemid);

IF (Pitemattr [i] .szaccesspath)

:: cotaskmemfree (pitemattr [i] .szaccesspath);

IF (Pitemattr [i] .dwblobsize)

:: cotaskmemfree (pitemattr [i] .pblob);

}

// Must Release The Memory After We Are Done with IT

:: cotaskmemfree (pitemattr);

}

Else

{

Showerror (HR, "Penumopcitems-> next ()");

}

}

Else

{

ShowError (HR, "Iopcitemmgt :: CreateEnumerator ()");

}

Penumopcitems-> release ();

PRINTF ("Do you wish to write value to each item (y / n)?");

Gets (szbuffer);

IF ((* szbuffer == _t ('y')) || (* szbuffer == _t ('y')))))

{

g_bwriteenable = true;

}

Return 0;

}

Void Showerror (HRESULT HR, LPCSTR PSZERROR)

{

LPWSTR PWSZERROR = NULL;

IF ((g_piopcserver! = null) && succeeded (g_piopcserver-> getErrorString (HR, 0, & pwszerror)))

{

Printf ("Error:% s Failed, / N ->% LS / N", PSZERROR, PWSZERROR);

// dump to log file

IF (g_stream)

{

FPRINTF (G_Stream, "% s error:% s failed, / n ->% ls / n", getdatetime (), pszerror, pwszerror;

Fflush (g_stream); // make sure buffers are flushed

}

:: cotaskmemfree (pwszerror);

}

Else

{

Printf ("Error:% S Failed, / N ->% LX / N", PSZERROR, HR);

// dump to log file

IF (g_stream)

{

FPRINTF (G_Stream, "% s error:% s failed, / n ->% lx / n", getdatetime (), pszerror, hr);

Fflush (g_stream); // make sure buffers are flushed

}

}

}

void starterrorlog ()

{

g_stream = fopen (_t ("sst_client.log"), _t ("w"));

IF (g_stream)

{

FPRINTF (G_Stream, "% ssst client start./n", getdatetime ());

}

}

void enderrorlog ()

{

IF (g_stream)

{

FPRINTF (G_Stream, "% ssst client end./n", getdatetime ());

Fclose (g_stream);

}

}

LPCSTR getDatetime ()

{

Static char SZ [128];

Char SZ2 [128];

_STRDATE (SZ);

STRCAT (SZ, ");

_STRTIME (SZ2);

STRCAT (SZ, SZ2);

STRCAT (SZ, "|");

Return SZ;

}

The IDL file is as follows:

Opc.idl file, which is OPCDA.IDL file for OPC2.0 specification.

OPCComn.idl file, is also an IDL file of the OPC specification.

The content of the two files can be copied on my blog.

转载请注明原文地址:https://www.9cbs.com/read-71086.html

New Post(0)