OPC客户程序(C篇——OPC1.0,2.0规范)
Posted sijigang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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.idl为OPC规范的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)
OPC客户程序(VB篇——同步)