OPC客户程序(C篇——OPC1.0,2.0规范)

OPC技术论坛 http://www.opc-china.com OPC服务器,客户程序技术讨论
OPC CLIENT程序(C语言篇,OPC1.0,2.0规范)

本程序为以前个人学习时,在国外网站上下载,本来想自己重新写一篇,因为各方面的原因,没有写,所以现在把下载的这个程序帖出来供大家学习。此程序个人觉得值得一看,虽然看起来程序有些长。

本程序一共包括三个文件:opc.idl opccomn.idl opctest.cpp

opc.idl opccomn.idlOPC规范的IDL定义,opctest.cpp为主程序文件。

opctest.cpp文件如下:

// 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 that

// 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 async 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 simply ignore transaction ID's and

// use the client handle returned as validation.

//

// 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 solely to assist in understanding

//  the OPC Data Access Specification in relation to a SST OPC server.

//  This code is provided as-is and without warranty or support of any sort.

//

// This code may be freely re-used 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 <stdio.h>

#include <time.h>

#include <windows.h>

#include <conio.h>

#include <atlbase.h>

//You may derive a class from CComModule and use it if you want to override

//something, but do not change the name of _Module

CComModule _Module;

#include <atlcom.h>

#include <atlctl.h>

 

// 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 values or async 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规范

IOPCGroupStateMgt *g_pIOPCGroupStateMgt = NULL;

IOPCAsyncIO *g_pIOPCAsyncIO = NULL;                         //OPC1.0规范

IOPCSyncIO *g_pIOPCSyncIO = NULL;

IOPCItemMgt *g_pIOPCItemMgt = NULL;

IOPCAsyncIO2 *g_pIOPCAsyncIO2 = NULL;

IOPCCommon *g_pIOPCCommon = NULL;

IUnknown *g_pIGroupUnknown = NULL;

IOPCBrowseServerAddressSpace *g_pIOPCBrowse = NULL;

 

// critical section stuff

CComAutoCriticalSection g_Readcs;

CComAutoCriticalSection g_Writecs;

class CLock

{

public:

       CComAutoCriticalSection* 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_Writecs);

 

class ATL_NO_VTABLE CTestAdviseSink;

class ATL_NO_VTABLE COPCCallback;

typedef CComObject<CTestAdviseSink> CComCTestAdviseSink;

typedef CComObject<COPCCallback> CComCOPCCallback;

 

UINT g_nOpcFormatData = ::RegisterClipboardFormat("OPCSTMFORMATDATA");

UINT g_nOpcFormatDatatime = ::RegisterClipboardFormat("OPCSTMFORMATDATATIME");

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 ShowError(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 hServer[MAX_ITEMS];

       VARIANT Val[MAX_ITEMS];

       VARIANT vCount;

       for(DWORD dw = 0; dw < g_dwNumItems; 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())

       {

              // read from the 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 < g_dwNumItems; 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 < g_dwNumItems; dw++)

                     {

                            if(FAILED(pErrors[dw]))

                            {

                                   char sz[100];

                                   sprintf(sz,"SyncIO->Read(%ls) returned", TestItem[dw].wszName);

                                   ShowError(pErrors[dw], sz);

                            }

                     }

              }

              else

              {

                     ShowError(hr,"Sync Read");

              }

              if(g_bWriteEnable) // quick write enable hack

              {

                     // pump out data sync to items

                     for(dw = 0; dw < g_dwNumItems; 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((V_VT(&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 < g_dwNumItems; dw++)

                            {

                                   if(FAILED(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 < g_dwNumItems; 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) OnDataChange(LPFORMATETC pFE, 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<const LPBYTE>(::GlobalLock(pSTM->hGlobal));

                     if(pBuffer == NULL) return;

                     const OPCGROUPHEADERWRITE *pHeader = reinterpret_cast<const OPCGROUPHEADERWRITE*>(pBuffer);

                     if(FAILED(pHeader->hrStatus))

                     {

                            ShowError(pHeader->hrStatus,"General Async Write");

                     }

                     if(g_dwWriteTransID != pHeader->dwTransactionID)

                     {

                            ShowError(S_OK,"Async Write callback, TransactionID's do not match");

                     }

                     DWORD dwSize = sizeof(OPCGROUPHEADERWRITE);

                     for(DWORD dw=0; dw < pHeader->dwItemCount; dw++, dwSize += sizeof(OPCITEMHEADERWRITE))

                     {

                            const OPCITEMHEADERWRITE* pItemHeader = reinterpret_cast<const OPCITEMHEADERWRITE*>(&pBuffer[dwSize]);

                            if(FAILED(pItemHeader->dwError))

                            {

                                   ShowError(pItemHeader->dwError,"Async Write request");

                            }

                     }

                     g_bWriteComplete = true;

                     ::GlobalUnlock(pSTM->hGlobal);

                     return;

              }

              else if(pFE->cfFormat != g_nOpcFormatDatatime) return;

 

              const LPBYTE pBuffer = reinterpret_cast<const LPBYTE>(::GlobalLock(pSTM->hGlobal));

              if(pBuffer == NULL) return;

 

              const OPCGROUPHEADER *pHeader = reinterpret_cast<const OPCGROUPHEADER*>(pBuffer);

              if(FAILED(pHeader->hrStatus))

              {

                     ShowError(pHeader->hrStatus,"General Async Read");

              }

              if(g_bPoll)

              {

                     // if we are polling, ignore async updates

                     if(pHeader->dwTransactionID == 0)

                     {

                            return;

                     }

                     READ_LOCK;

                     if(pHeader->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 < pHeader->dwItemCount; dw++, dwSize += sizeof(OPCITEMHEADER1))

              {

                     const OPCITEMHEADER1* pItemHeader = reinterpret_cast<const OPCITEMHEADER1*>(&pBuffer[dwSize]);

                     if(pItemHeader->wQuality == OPC_QUALITY_GOOD)

                     {

                            VARIANT *pValue = reinterpret_cast<VARIANT*>(&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(SUCCEEDED(::VariantChangeType(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_nOpcFormatDatatime;

       // need to fill the rest of the struct or the proxy make puke

       formatetc.ptd = NULL;

       formatetc.dwAspect = DVASPECT_CONTENT;

       formatetc.lindex = -1;

       formatetc.tymed = TYMED_HGLOBAL;

       CComCTestAdviseSink *pSink = NULL;

 

       ATLTRY(pSink = new CComCTestAdviseSink);

       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, &dwWriteConnection);

              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 hServer[MAX_ITEMS];

       VARIANT Val[MAX_ITEMS];

       VARIANT vCount;

       DWORD dw = 0;

       if(g_bWriteEnable)

       {

              for(dw = 0; dw < g_dwNumItems; 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 < g_dwNumItems; 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((V_VT(&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(dwWriteConnection, g_dwNumItems, hServer, Val, &g_dwWriteTransID, &pErrorsWrite);

                     g_Writecs.Unlock();

                     if(FAILED(hr))

                     {

                            ShowError(hr,"AsyncIO->Write()");

                     }

                     else if(hr == S_FALSE)

                     {

                            for(dw = 0; dw < g_dwNumItems; dw++)

                            {

                                   if(FAILED(pErrorsWrite[dw]))

                                   {

                                          ShowError(pErrorsWrite[dw],"AsyncIO->Write() item returned");

                                   }

                            }

                            ::CoTaskMemFree(pErrorsWrite);

                     }

                     else // S_OK

                     {

                            ::CoTaskMemFree(pErrorsWrite);

                     }

              }

       }

       if(g_bWriteEnable)

       {

              for(dw = 0; dw < g_dwNumItems; 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 dwWriteConnection = 0;

       g_bPoll = true; // we are polling for values

       formatetc.cfFormat = g_nOpcFormatDatatime;

       // need to fill the rest of the struct or the proxy make puke

       formatetc.ptd = NULL;

       formatetc.dwAspect = DVASPECT_CONTENT;

       formatetc.lindex = -1;

       formatetc.tymed = TYMED_HGLOBAL;

       CTestAdviseSink *pSink = NULL;

 

       ATLTRY(pSink = new CComCTestAdviseSink);

       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, &dwWriteConnection);

              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 hServer[MAX_ITEMS];

       VARIANT Val[MAX_ITEMS];

       VARIANT vCount;

       DWORD dw = 0;

       if(g_bWriteEnable)

       {

              for(dw = 0; dw < g_dwNumItems; 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 < g_dwNumItems; 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((V_VT(&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(dwWriteConnection, g_dwNumItems, hServer, Val, &g_dwWriteTransID, &pErrorsWrite);

                     g_Writecs.Unlock();

                     if(FAILED(hr))

                     {

                            ShowError(hr,"AsyncIO->Write()");

                     }

                     else if(hr == S_FALSE)

                     {

                            for(dw = 0; dw < g_dwNumItems; dw++)

                            {

                                   if(FAILED(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 < g_dwNumItems; 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 *pvValues,

    /* [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 < dwCount; 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(::VariantChangeType(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 *pvValues,

    /* [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 < dwCount; 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(::VariantChangeType(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

              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 < dwCount; dw++)

              {

                     if(FAILED(pErrors[dw]))

                     {

                            ShowError(pErrors[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 CComCOPCCallback);

       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 hServer[MAX_ITEMS];

       VARIANT Val[MAX_ITEMS];

       VARIANT vCount;

       DWORD dw = 0;

       if(g_bWriteEnable)

       {

              for(dw = 0; dw < g_dwNumItems; 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 < g_dwNumItems; 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((V_VT(&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 < g_dwNumItems; dw++)

                            {

                                   if(FAILED(pErrorsWrite[dw]))

                                   {

                                          ShowError(pErrorsWrite[dw],"AsyncIO2->Write() item returned");

                                   }

                            }

                            ::CoTaskMemFree(pErrorsWrite);

                     }

                     else // S_OK

                     {

                            ::CoTaskMemFree(pErrorsWrite);

                     }

              }

       }

       if(g_bWriteEnable)

       {

              for(dw = 0; dw < g_dwNumItems; dw++)

              {

                     ::VariantClear(&Val[dw]);

              }

       }

       ::VariantClear(&vCount);

 

       // release interfaces

       hr = pCP->Unadvise(dwCookie);

       if(FAILED(hr))

       {

              ShowError(hr, "Unadvise()");

       }

       pCP->Release();

       pCPC->Release();

       while(pSink->Release()) ;

       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 CComCOPCCallback);

       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;

       }

 

       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 hServer[MAX_ITEMS];

       VARIANT Val[MAX_ITEMS];

       VARIANT vCount;

 

       for(DWORD dw = 0; dw < g_dwNumItems; 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 < g_dwNumItems; 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((V_VT(&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 < g_dwNumItems; dw++)

                            {

                                   if(FAILED(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, &pErrorsRead);

                     if(FAILED(hr))

                     {

                            ShowError(hr,"AsyncIO2->Read()");

                     }

                     else if(hr == S_FALSE)

                     {

                            for(dw = 0; dw < g_dwNumItems; dw++)

                            {

                                   if(FAILED(pErrorsRead[dw]))

                                   {

                                          ShowError(pErrorsRead[dw],"AsyncIO2->Read() item returned");

                                   }

                            }

                            ::CoTaskMemFree(pErrorsRead);

                     }

                     else // S_OK

                     {

                            ::CoTaskMemFree(pErrorsRead);

                     }

              }

              ::Sleep(g_dwUpdateRate);

       }

       for(dw = 0; dw < g_dwNumItems; dw++)

       {

              ::VariantClear(&Val[dw]);

       }

       ::VariantClear(&vCount);

 

       // release interfaces

       hr = pCP->Unadvise(dwCookie);

       if(FAILED(hr))

       {

              ShowError(hr, "Unadvise()");

       }

       pCP->Release();

       pCPC->Release();

       while(pSink->Release()) ;

       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((wcslen(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, &clsid );

       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 allow 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_IOPCBrowseServerAddressSpace, (void**)&g_pIOPCBrowse);

       if(FAILED(hr))

       {

              ShowError(hr,"QueryInterface(IID_IOPCBrowseServerAddressSpace");

       }

 

       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(IID_IDataObject)");

       }

       hr = g_pIGroupUnknown->QueryInterface(IID_IOPCGroupStateMgt, (void**)&g_pIOPCGroupStateMgt);

       if(FAILED(hr))

       {

              ShowError(hr,"QueryInterface(IID_IOPCGroupStateMgt)");

       }

       hr = g_pIGroupUnknown->QueryInterface(IID_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(IID_IOPCSyncIO)");

       }

       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(IID_IOPCCommon)");

              }

              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_pIOPCServer->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 there 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"))) break;

              }

              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(sz2,"VT_I2")) TestItem[nTestItem].vt = VT_I2;

              else if(!stricmp(sz2,"VT_I4")) TestItem[nTestItem].vt = VT_I4;

              else if(!stricmp(sz2,"VT_EMPTY")) TestItem[nTestItem].vt = VT_EMPTY;

              else if(!stricmp(sz2,"VT_R4")) TestItem[nTestItem].vt = VT_R4;

              else if(!stricmp(sz2,"VT_R8")) TestItem[nTestItem].vt = VT_R8;

              else if(!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, &pErrors);

              if(FAILED(hr))

              {

                     ShowError(hr,"AddItem()");

                     continue;

              }

              hr = S_OK;

 

              if(FAILED(pErrors[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_IEnumOPCItemAttributes,  reinterpret_cast<LPUNKNOWN*>(&pEnumOPCItems));

       if(SUCCEEDED(hr))

       {

              printf("IOPCItemMgt::CreateEnumerator()/n");

              pEnumOPCItems->Reset();

              // NOTE: 3rd param must not be a NULL or the proxy will puke

              hr = pEnumOPCItems->Next(static_cast<ULONG>(nTestItem), &pItemAttr, &dwFetched);

              if(SUCCEEDED(hr))

              {

                     if((dwFetched != static_cast<ULONG>(nTestItem)) || (hr == S_FALSE))

                     {

                            printf("Error: pEnumOPCItems->Next() - fetched != requested/n");

                     }

                     for(ULONG i = 0; i < dwFetched; 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 (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 values 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;

}

 

 

 

IDL文件如下:

OPC.idl文件,即为OPC2.0规范的OPCDA.IDL文件

OPCCOMN.idl文件,也是OPC规范的IDL文件。

两个文件的内容都可以在我的BLOG上复制。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值