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.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)

OPC客户程序(VB篇——同步)

《OPC服务器与客户程序开发指南》

如何将文件从 OPC UA 客户端传输到服务器

OPC协议解析-OPC客户端与服务器通讯解析

C# OPC 客户端无法获取服务器列表

C# OPC 应用程序代码相同,但工作方式不同