OPC CLIENT程序(C语言篇,OPC1.0,2.0规范)
转自:https://www.xuebuyuan.com/2034211.html 本程序为以前个人学习时,在国外网站上下载,本来想自己重新写一篇,因为各方面的原因,没有写,所以现在把下载的这个程序帖出来供大家学习。此程序个人觉得值得一看,虽然看起来程序有些长。 本程序一共包括三个文件: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 #include #include #include #include //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 #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 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 typedef CComObject 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],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 if(pBuffer == NULL) return; const OPCGROUPHEADERWRITE *pHeader = reinterpret_cast 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 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |