LibOPC.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581
  1. #include "stdAfx.h"
  2. #include "LibApp.h"
  3. #include "LibOPC.h"
  4. #include "opcda_i.h"
  5. #include <comdef.h>
  6. #include ".\stoneu_opcc.h"
  7. inline void EnableMemLeakCheck()
  8. {
  9. _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
  10. }
  11. //#ifdef _DEBUG
  12. //#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
  13. //#endif
  14. const int _OPC_DUPLICATE_GROUP_NAME = -101 ;
  15. const int _OPC_INVALID_OBJECT = -102 ;
  16. const int _OPC_TOO_MANY_ITEMS = -103 ;
  17. const int _OPC_INVALID_GROUP_NAME = -104 ;
  18. const int _OPC_SERVER_NOT_CONNECTED = -105 ;
  19. // The OPC data formats
  20. UINT OPCSTMFORMATDATA = RegisterClipboardFormat(_T("OPCSTMFORMATDATA"));
  21. UINT OPCSTMFORMATDATATIME = RegisterClipboardFormat(_T("OPCSTMFORMATDATATIME"));
  22. UINT OPCSTMFORMATWRITECOMPLETE = RegisterClipboardFormat(_T("OPCSTMFORMATWRITECOMPLETE"));
  23. ///////////////////////////////////////////////////////////////////////////
  24. ///////////////////////////////////////////////////////////////////////////
  25. CAdviseSink::CAdviseSink()
  26. {
  27. m_cRef=0;
  28. return;
  29. }
  30. CAdviseSink::~CAdviseSink(void)
  31. {
  32. return;
  33. }
  34. /*
  35. * CAdviseSink::QueryInterface
  36. * CAdviseSink::AddRef
  37. * CAdviseSink::Release
  38. *
  39. * Purpose:
  40. * IUnknown members for CAdviseSink object.
  41. */
  42. STDMETHODIMP CAdviseSink::QueryInterface(REFIID riid, void** ppv)
  43. {
  44. *ppv=NULL;
  45. if (IID_IUnknown==riid || IID_IAdviseSink==riid)
  46. *ppv=this;
  47. if (NULL!=*ppv)
  48. {
  49. ((LPUNKNOWN)*ppv)->AddRef();
  50. return NOERROR;
  51. }
  52. return ResultFromScode(E_NOINTERFACE);
  53. }
  54. STDMETHODIMP_(ULONG) CAdviseSink::AddRef(void)
  55. {
  56. return ++m_cRef;
  57. }
  58. STDMETHODIMP_(ULONG) CAdviseSink::Release(void)
  59. {
  60. if (0!=--m_cRef)
  61. return m_cRef;
  62. delete this;
  63. return 0;
  64. }
  65. /*
  66. * CAdviseSink::OnDataChange
  67. *
  68. * Purpose:
  69. * Notifes the advise sink that data changed in a data object.
  70. * On this message you may request a new data rendering and update
  71. * your displays as necessary. Any data sent to this function is
  72. * owned by the caller, not by this advise sink.
  73. *
  74. * All Advise Sink methods are asynchronous and therefore we
  75. * should attempt no synchronous calls from within them to an EXE
  76. * object. If we do, we'll get RPC_E_CALLREJECTED.
  77. *
  78. * Parameters:
  79. * pFEIn LPFORMATETC describing format that changed
  80. * pSTM LPSTGMEDIUM providing the medium in which the
  81. * data is provided.
  82. *
  83. * Return Value:
  84. * None
  85. */
  86. STDMETHODIMP_(void) CAdviseSink::OnDataChange(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
  87. {
  88. int iMode=0;
  89. // Verify the format follows the OPC spec
  90. if( TYMED_HGLOBAL != pFE->tymed )
  91. {
  92. return;
  93. }
  94. if( pSTM->hGlobal == 0 )
  95. {
  96. return;
  97. }
  98. if( OPCSTMFORMATWRITECOMPLETE != pFE->cfFormat ) // ignore write status notification
  99. {
  100. #ifdef DATATIMEFORMAT
  101. if( OPCSTMFORMATDATATIME != pFE->cfFormat ) // ignore write status notification
  102. #else
  103. if( OPCSTMFORMATDATA != pFE->cfFormat ) // ignore write status notification
  104. #endif // DATATIMEFORMAT
  105. {
  106. return;
  107. }
  108. } else
  109. {
  110. iMode=OPCSTMFORMATWRITECOMPLETE;
  111. }
  112. // It must be a data advise
  113. const BYTE* buffer = (BYTE*)GlobalLock( pSTM->hGlobal );
  114. if( !buffer )
  115. {
  116. return;
  117. }
  118. const OPCGROUPHEADER* pHeader = (OPCGROUPHEADER*)buffer;
  119. // check pHeader->hClientGroup
  120. // pHeader->dwTransactionID
  121. // pHeader->hrStatus
  122. int offset = sizeof(OPCGROUPHEADER);
  123. // for each item in the data stream, get the value and
  124. if (iMode==(int)OPCSTMFORMATWRITECOMPLETE)
  125. {
  126. if (!pHeader->dwTransactionID)
  127. {
  128. //error
  129. }
  130. if (pHeader->dwItemCount>200)
  131. {
  132. //error
  133. }
  134. for( DWORD index=0; index<pHeader->dwItemCount; index++, offset += sizeof(OPCITEMHEADERWRITE) )
  135. {
  136. const OPCITEMHEADERWRITE* pItemHeader = (OPCITEMHEADERWRITE*)&buffer[offset];
  137. if ( pItemHeader->dwError != S_OK)
  138. {
  139. Item* pItem = (Item*)pItemHeader->hClient;
  140. if (pItem)
  141. {
  142. pItem->dwLastError = pItemHeader->dwError;
  143. pItem->boActual = TRUE;
  144. } else
  145. {
  146. // DEBUG BREAK
  147. pItem=pItem;
  148. }
  149. }// else
  150. // pItem->boActual = TRUE;
  151. }
  152. } else
  153. {
  154. #ifdef DATATIMEFORMAT
  155. for( DWORD index=0; index<pHeader->dwItemCount; index++, offset += sizeof(OPCITEMHEADER1) )
  156. {
  157. const OPCITEMHEADER1* pItemHeader = (OPCITEMHEADER1*)&buffer[offset];
  158. #else
  159. for( DWORD index=0; index<pHeader->dwItemCount; index++, offset += sizeof(OPCITEMHEADER2) )
  160. {
  161. const OPCITEMHEADER2* pItemHeader = (OPCITEMHEADER2*)&buffer[offset];
  162. #endif // DATATIMEFORMAT
  163. VARIANT* pValue = (VARIANT*)&buffer[pItemHeader->dwValueOffset];
  164. // Strings and arrays are packed in the stream
  165. // requiring unpacking
  166. if( pValue->vt == VT_BSTR )
  167. {
  168. pValue->bstrVal = (BSTR)((BYTE*)pValue + sizeof(VARIANT) + sizeof(DWORD));
  169. }
  170. else if( (pValue->vt & VT_ARRAY) == VT_ARRAY )
  171. {
  172. pValue->parray = (SAFEARRAY*)((BYTE*)pValue + sizeof(VARIANT));
  173. pValue->parray->pvData = ((BYTE*)pValue->parray + sizeof(SAFEARRAY));
  174. }
  175. Item* pItem = (Item*)pItemHeader->hClient;
  176. pItem->value = pValue;
  177. pItem->quality = pItemHeader->wQuality;
  178. pItem->boActual = TRUE;
  179. }
  180. }
  181. GlobalUnlock( pSTM->hGlobal );
  182. return;
  183. }
  184. /*
  185. * CAdviseSink::OnViewChange
  186. * CAdviseSink::OnRename
  187. * CAdviseSink::OnSave
  188. * CAdviseSink::OnClose
  189. *
  190. * Unimplemented members
  191. */
  192. STDMETHODIMP_(void) CAdviseSink::OnViewChange(DWORD dwAspect
  193. , LONG lindex)
  194. {
  195. return;
  196. }
  197. STDMETHODIMP_(void) CAdviseSink::OnRename(LPMONIKER pmk)
  198. {
  199. return;
  200. }
  201. STDMETHODIMP_(void) CAdviseSink::OnSave(void)
  202. {
  203. return;
  204. }
  205. STDMETHODIMP_(void) CAdviseSink::OnClose(void)
  206. {
  207. return;
  208. }
  209. CAppServer::CAppServer() :
  210. m_nGroupNo(-1),
  211. m_nTotalGroups(0),
  212. m_GroupHandle (0)
  213. {
  214. for (int i=0; i<_OPC_MAX_GROUP; i++)
  215. {
  216. GroupNumber[i].m_szGrpName[0] = L'\0' ;
  217. GroupNumber[i].m_GroupHandle = 0 ;
  218. GroupNumber[i].m_NoOfItems = 0 ;
  219. GroupNumber[i].m_nGroupNo = -1 ;
  220. }
  221. _CrtSetBreakAlloc(65);
  222. pcSink = new CAdviseSink(); // create the advise sink for notifications
  223. pcSink->AddRef();
  224. m_nTagCount = 0;
  225. m_pcItem=new Item[_OPC_MAX_HANDLE];
  226. }
  227. ///////////////////////////////////////////////////////////////////////////
  228. ///////////////////////////////////////////////////////////////////////////
  229. CAppServer::~CAppServer()
  230. {
  231. m_sTagNameAll.RemoveAll();
  232. pcSink->Release(); // OLE should clean this up, but may not have time!
  233. }
  234. ///////////////////////////////////////////////////////////////////////////
  235. ///////////////////////////////////////////////////////////////////////////
  236. CAppServer::CAppServer(wchar_t* svrName, wchar_t* svrType) :
  237. m_nGroupNo(-1),
  238. m_nTotalGroups(0),
  239. m_GroupHandle (0)
  240. {
  241. wcscpy (m_wstrServerName, svrName) ;
  242. wcscpy (m_wstrServerType, svrType) ;
  243. for (int i=0; i<_OPC_MAX_GROUP; i++)
  244. {
  245. GroupNumber[i].m_szGrpName[0] = L'\0' ;
  246. GroupNumber[i].m_GroupHandle = 0 ;
  247. GroupNumber[i].m_NoOfItems = 0 ;
  248. GroupNumber[i].m_nGroupNo = -1 ;
  249. }
  250. }
  251. ///////////////////////////////////////////////////////////////////////////
  252. ///////////////////////////////////////////////////////////////////////////
  253. CAppServer::CAppServer (const CAppServer &rhs) :
  254. m_nGroupNo (rhs.m_nGroupNo),
  255. m_nTotalGroups(rhs.m_nTotalGroups),
  256. m_GroupHandle (rhs.m_GroupHandle),
  257. m_pIOPCServer (rhs.m_pIOPCServer),
  258. m_pIOPCSyncIO (rhs.m_pIOPCSyncIO)
  259. {
  260. wcscpy (m_wstrServerName, rhs.m_wstrServerName) ;
  261. wcscpy (m_wstrServerType, rhs.m_wstrServerType) ;
  262. for (int i=0; i<_OPC_MAX_GROUP; i++)
  263. {
  264. GroupNumber[i] = rhs.GroupNumber[i] ;
  265. m_pIOPCGroupStateMgt[i] = rhs.m_pIOPCGroupStateMgt[i] ;
  266. }
  267. }
  268. ///////////////////////////////////////////////////////////////////////////
  269. ///////////////////////////////////////////////////////////////////////////
  270. CAppServer& CAppServer::operator=(const CAppServer &rhs)
  271. {
  272. m_nGroupNo = rhs.m_nGroupNo ;
  273. m_nTotalGroups = rhs.m_nTotalGroups ;
  274. m_GroupHandle = rhs.m_GroupHandle ;
  275. m_pIOPCServer = rhs.m_pIOPCServer ;
  276. m_pIOPCSyncIO = rhs.m_pIOPCSyncIO ;
  277. wcscpy(this->m_wstrServerName, rhs.m_wstrServerName) ;
  278. wcscpy(this->m_wstrServerType, rhs.m_wstrServerType) ;
  279. for (int i=0; i<_OPC_MAX_GROUP; i++)
  280. {
  281. GroupNumber[i] = rhs.GroupNumber[i] ;
  282. m_pIOPCGroupStateMgt[i] = rhs.m_pIOPCGroupStateMgt[i] ;
  283. }
  284. return *this ;
  285. }
  286. ///////////////////////////////////////////////////////////////////////////
  287. ///////////////////////////////////////////////////////////////////////////
  288. HRESULT CAppServer::OPCConnect(wchar_t* svrName, wchar_t* svrType)
  289. {
  290. #if 1
  291. CLSID clsid ;
  292. COSERVERINFO csvrinfo;
  293. MULTI_QI queryInterface;
  294. LPUNKNOWN pUnkn = NULL;
  295. HRESULT hr ;
  296. //size_t len = strlen(svrName) + 1;
  297. //size_t converted = 0;
  298. //wchar_t *WStr;
  299. //WStr=(wchar_t*)malloc(len*sizeof(wchar_t));
  300. //mbstowcs_s(&converted, m_wstrServerName, len, svrName, _TRUNCATE);
  301. wcscpy (m_wstrServerName, svrName) ;
  302. wcscpy (m_wstrServerType, svrType) ;
  303. // Get the Class ID from the Program ID
  304. // Take note that the server name and type must be wide-character
  305. hr = CLSIDFromProgID(m_wstrServerType, &clsid );
  306. if ( FAILED (hr))
  307. return hr ;
  308. // Assign GUID of IOPCServer Interface to the MULTI_QI structure
  309. queryInterface.pIID = &IID_IOPCServer ;
  310. queryInterface.pItf = NULL ;
  311. queryInterface.hr = 0 ;
  312. memset(&csvrinfo, 0, sizeof(COSERVERINFO));
  313. csvrinfo.pAuthInfo = NULL ;
  314. // If the server name is blank, assume the server is
  315. // the Local Server, and set it to NULL
  316. if (!wcslen(m_wstrServerName))
  317. csvrinfo.pwszName = NULL ;
  318. else
  319. csvrinfo.pwszName = m_wstrServerName ;
  320. // Create an instance and retrieve the IOPCServer Interface
  321. hr = ::CoCreateInstanceEx(clsid, NULL, CLSCTX_SERVER, &csvrinfo, 1, &queryInterface);
  322. m_pIOPCServer = queryInterface.pItf ;
  323. if( FAILED(hr) || m_pIOPCServer == NULL)
  324. return hr ;
  325. queryInterface.pItf->Release() ;
  326. return hr ;
  327. #endif
  328. return 0;
  329. }
  330. HRESULT CAppServer::OPCConnect(CString svrName, CString svrType)
  331. {
  332. CLSID clsid ;
  333. COSERVERINFO csvrinfo;
  334. MULTI_QI queryInterface;
  335. LPUNKNOWN pUnkn = NULL;
  336. HRESULT hr ;
  337. //size_t len = strlen(svrName) + 1;
  338. //size_t converted = 0;
  339. //wchar_t *WStr;
  340. //WStr=(wchar_t*)malloc(len*sizeof(wchar_t));
  341. //mbstowcs_s(&converted, m_wstrServerName, len, svrName, _TRUNCATE);
  342. //m_wstrServerName = svrName.AllocSysString();
  343. MultiByteToWideChar(CP_ACP,0,svrName,-1,m_wstrServerName,256);
  344. MultiByteToWideChar(CP_ACP,0,svrType,-1,m_wstrServerType,256);
  345. //wcscpy (m_wstrServerName, svrName) ;
  346. //wcscpy (m_wstrServerType, svrType) ;
  347. // Get the Class ID from the Program ID
  348. // Take note that the server name and type must be wide-character
  349. hr = CLSIDFromProgID(m_wstrServerType, &clsid );
  350. if ( FAILED (hr))
  351. return hr ;
  352. // Assign GUID of IOPCServer Interface to the MULTI_QI structure
  353. memset(&queryInterface, 0, sizeof(queryInterface));
  354. queryInterface.pIID = &IID_IUnknown;//&IID_IOPCServer ;
  355. //queryInterface.pItf = NULL ;
  356. //queryInterface.hr = 0 ;
  357. memset(&csvrinfo, 0, sizeof(COSERVERINFO));
  358. csvrinfo.pAuthInfo = NULL ;
  359. // If the server name is blank, assume the server is
  360. // the Local Server, and set it to NULL
  361. if (!wcslen(m_wstrServerName))
  362. csvrinfo.pwszName = NULL ;
  363. else
  364. csvrinfo.pwszName = m_wstrServerName ;
  365. // Create an instance and retrieve the IOPCServer Interface
  366. hr = ::CoCreateInstanceEx(clsid, NULL, CLSCTX_REMOTE_SERVER, &csvrinfo, 1, &queryInterface);
  367. //HRESULT hRet=CoCreateInstanceEx(OPCCLSID,NULL,CLSCTX_REMOTE_SERVER,&ServerInfo,1,qi);
  368. //hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IUnknown, (LPVOID *)&pUnkn);
  369. m_pIOPCServer = queryInterface.pItf ;
  370. if( FAILED(hr) || m_pIOPCServer == NULL)
  371. return hr ;
  372. queryInterface.pItf->Release() ;
  373. return hr ;
  374. }
  375. ///////////////////////////////////////////////////////////////////////////
  376. ///////////////////////////////////////////////////////////////////////////
  377. HRESULT CAppServer::OPCConnect()
  378. {
  379. CLSID clsid ;
  380. COSERVERINFO csvrinfo;
  381. MULTI_QI queryInterface;
  382. LPUNKNOWN pUnkn = NULL;
  383. HRESULT hr ;
  384. // Get the Class ID from the Program ID
  385. // Take note that the server name and type must be wide-character
  386. hr = CLSIDFromProgID(m_wstrServerType, &clsid );
  387. if ( FAILED (hr))
  388. return hr ;
  389. // Assign GUID of IOPCServer Interface to the MULTI_QI structure
  390. queryInterface.pIID = &IID_IOPCServer ;
  391. queryInterface.pItf = NULL ;
  392. queryInterface.hr = 0 ;
  393. memset(&csvrinfo, 0, sizeof(COSERVERINFO));
  394. csvrinfo.pAuthInfo = NULL ;
  395. // If the server name is blank, assume the server is
  396. // the Local Server, and set it to NULL
  397. if (!wcslen(m_wstrServerName))
  398. csvrinfo.pwszName = NULL ;
  399. else
  400. csvrinfo.pwszName = m_wstrServerName ;
  401. // Create an instance and retrieve the IOPCServer Interface
  402. hr = ::CoCreateInstanceEx(clsid, NULL, CLSCTX_SERVER, &csvrinfo, 1, &queryInterface);
  403. m_pIOPCServer = queryInterface.pItf ;
  404. if( FAILED(hr) || m_pIOPCServer == NULL)
  405. return hr ;
  406. queryInterface.pItf->Release() ;
  407. return hr ;
  408. }
  409. ///////////////////////////////////////////////////////////////////////////
  410. // This Method adds a group to the OPC Server
  411. // Note that when a group is added, the group becomes the
  412. // current default group. This means that all subsequent operation
  413. // i.e. addition of tags, reading or writing of tags will use this group.
  414. // If operation on a different group is to be carried out, then the group
  415. // must be changed first by using the method, OPCChangeGroup().
  416. ///////////////////////////////////////////////////////////////////////////
  417. HRESULT CAppServer::OPCAddGroup(LPWSTR szGrpName)
  418. {
  419. HRESULT hr ;
  420. bool bActive = TRUE ;
  421. OPCHANDLE hClientGroupHandle = 1000 ;
  422. DWORD dwLCID = 0 ;
  423. DWORD dwRtnUpdateRate ;
  424. long *pTimeBias = NULL ;
  425. DWORD dwReqUpdateRate = 1000 ;
  426. float pPercentDeadband = 0.0;
  427. CGroup GrpNum ;
  428. // _wcsupr(szGrpName) ; // Convert all Uppercase for easy comparison
  429. if (AddGroupName (szGrpName, GrpNum))
  430. return _OPC_DUPLICATE_GROUP_NAME ;
  431. m_nGroupNo = GrpNum.m_nGroupNo ;
  432. // Add a group
  433. hr = m_pIOPCServer->AddGroup( szGrpName, TRUE, 1000, //szGrpName, TRUE, 1000,
  434. 1324, NULL, &pPercentDeadband, //hClientGroupHandle, NULL, NULL,
  435. dwLCID, &m_GroupHandle, &dwRtnUpdateRate,
  436. IID_IOPCGroupStateMgt,
  437. reinterpret_cast<IUnknown**> (&m_pIOPCGroupStateMgt[m_nGroupNo]));
  438. if (FAILED (hr))
  439. {
  440. GroupNumber[m_nGroupNo].m_szGrpName[0] = L'\0' ;
  441. m_pIOPCGroupStateMgt[m_nGroupNo].Release() ;
  442. return hr ;
  443. }
  444. m_pIOPCGroupStateMgt[m_nGroupNo]->QueryInterface(IID_IDataObject,(LPVOID*)&m_pIOPCDataObject);
  445. // data advise format
  446. FORMATETC formatEtc ;
  447. #ifdef DATATIMEFORMAT
  448. formatEtc.cfFormat = OPCSTMFORMATDATATIME ;
  449. #else
  450. formatEtc.cfFormat = OPCSTMFORMATDATA ;
  451. #endif // DATATIMEFORMAT
  452. formatEtc.tymed = TYMED_HGLOBAL;
  453. formatEtc.ptd = NULL;
  454. formatEtc.dwAspect = DVASPECT_CONTENT;
  455. formatEtc.lindex = -1;
  456. hr = m_pIOPCDataObject->DAdvise(&formatEtc,
  457. 0, // ADVF flag
  458. pcSink,
  459. &dwConnection);
  460. pcSink->Release();
  461. GroupNumber[m_nGroupNo].m_GroupHandle = m_GroupHandle ;
  462. m_nTotalGroups++ ;
  463. return hr ;
  464. }
  465. ///////////////////////////////////////////////////////////////////////////
  466. // This Method adds tags to the current group. If tags are to be added
  467. // to a different group, then the group must be changed first by using
  468. // OPCChangeGroup() method.
  469. // ItemIDs is an array of tagnames.
  470. // ClientHandles is an array of arbitarily assigned integers by the caller.
  471. // ServerHandles is an array corresponding to the ClientHandles array returned
  472. // by OPC Server.Use the ServerHandles for all reading or writing of items to
  473. // the OPC Server. The OPC Server knows only the ServerHandles.
  474. ///////////////////////////////////////////////////////////////////////////
  475. HRESULT CAppServer::OPCAddTags (CString * ItemIDs, DWORD NoOfItems, OPCHANDLE ServerHandles[], CResult* pResult)
  476. {
  477. HRESULT hr=S_OK ;
  478. DWORD index ;
  479. OPCITEMDEF* idef ;
  480. OPCITEMRESULT* pResults ;
  481. HRESULT* pErrors ;
  482. // Assign the variable pItemMgt of type IOPCItemMgt Interface using the
  483. // smart pointer, CComQIPtr. CComQIPtr will add and release references automatically
  484. CComQIPtr <IOPCItemMgt, &IID_IOPCItemMgt> pItemMgt (m_pIOPCGroupStateMgt[m_nGroupNo]) ;
  485. if (!m_pIOPCServer || !m_pIOPCGroupStateMgt[m_nGroupNo] || NoOfItems <= 0)
  486. return _OPC_INVALID_OBJECT ;
  487. if (NoOfItems > _OPC_MAX_HANDLE)
  488. return _OPC_TOO_MANY_ITEMS ;
  489. // for (DWORD idx=0; idx<NoOfItems; idx++)
  490. // _wcsupr(ItemIDs[idx]) ; // OPC requuires items to be Uppercase. May not be necessary now.
  491. GroupNumber[m_nGroupNo].m_NoOfItems += NoOfItems ;
  492. // create a new OPC Item Definition pointer
  493. idef = new OPCITEMDEF [NoOfItems] ;
  494. //Item* pcItem;
  495. //pcItem = new Item;
  496. for ( index=0; index<NoOfItems ; index++ )
  497. {
  498. m_pcItem[m_nTagCount].quality = 0;
  499. m_pcItem[m_nTagCount].name = ItemIDs[index];
  500. idef[index].szItemID = ItemIDs[index].AllocSysString();
  501. idef[index].bActive = TRUE;
  502. idef[index].dwBlobSize = 0;
  503. idef[index].pBlob = NULL;
  504. idef[index].szAccessPath = NULL;
  505. idef[index].hClient = (OPCHANDLE)&m_pcItem[m_nTagCount]; // pointer to item is its "handle"
  506. idef[index].vtRequestedDataType = VT_EMPTY;
  507. pResult->hClient = idef[index].hClient;
  508. //delete pcItem;
  509. }
  510. //delete pcItem;
  511. // Add items using the IOPCItemMgt pointer
  512. hr = pItemMgt->AddItems (NoOfItems, idef, &pResults, &pErrors) ;
  513. delete []idef;
  514. if (hr==S_FALSE)
  515. {
  516. return hr ;
  517. }
  518. else
  519. {
  520. pResult->vtCanonicalDataType = pResults->vtCanonicalDataType;
  521. }
  522. DWORD setNum = 0 ;
  523. int nErr = 0 ;
  524. for (index=0; index<NoOfItems; index++)
  525. {
  526. if ( SUCCEEDED(pErrors[index]) )
  527. {
  528. ServerHandles[m_nTagCount] = pResults[index].hServer ;
  529. m_sAddTagName.Add( ItemIDs[index] );
  530. m_nTagCount++;
  531. }
  532. else
  533. nErr++ ;
  534. pResult[index].pErrors = pErrors[index] ;
  535. }
  536. CoTaskMemFree( pResults );
  537. CoTaskMemFree( pErrors );
  538. return hr ;
  539. }
  540. ///////////////////////////////////////////////////////////////////////////
  541. // This Method Read Tags.
  542. // pValues is a structure of type CItem. See CItem in the header file
  543. // ServerHandles is the array originally used in the OPCAddTags().
  544. ///////////////////////////////////////////////////////////////////////////
  545. HRESULT CAppServer::OPCReadTags (CItem* pValues, DWORD NoOfItems, OPCHANDLE ServerHandles[],int &nErr)
  546. {
  547. // Test to see if the Interface is still connected to the server
  548. if (!CoIsHandlerConnected (m_pIOPCGroupStateMgt[m_nGroupNo]))
  549. {
  550. m_pIOPCGroupStateMgt[m_nGroupNo].Release() ;
  551. }
  552. OPCITEMSTATE* pItemState;
  553. HRESULT hr,*pErrors;
  554. // Assign the variable opcSyncIO of type IOPCSyncIO Interface using the
  555. // smart pointer, CComQIPtr. CComQIPtr will add and release references automatically
  556. CComQIPtr<IOPCSyncIO, &IID_IOPCSyncIO> opcSyncIO (m_pIOPCGroupStateMgt[m_nGroupNo]) ;
  557. if (NoOfItems <= 0 || NoOfItems > _OPC_MAX_HANDLE)
  558. return _OPC_TOO_MANY_ITEMS ;
  559. // for (DWORD idx=0; idx<NoOfItems; idx++)
  560. // _wcsupr(pValues[idx].cName) ;
  561. // Read the items using the IOPCSyncIO Interface pointer.
  562. hr = opcSyncIO->Read(OPC_DS_CACHE, NoOfItems,ServerHandles, &pItemState, &pErrors);//ServerHandles
  563. if( SUCCEEDED(hr) )
  564. {
  565. nErr = 0 ;
  566. for(DWORD index=0; index < NoOfItems; index++) //DWORD index=0;
  567. {
  568. //if ( FAILED(pErrors[index]) )
  569. // nErr++ ;
  570. //pValues[index].cName = ServerHandles[index].sName;
  571. //MultiByteToWideChar(CP_ACP,0,ServerHandles[index].sName,-1,pValues[index].cName,256);
  572. pValues[index].sName = m_sAddTagName[index];
  573. pValues[index].quality = pItemState[index].wQuality ;
  574. pValues[index].timestamp = pItemState[index].ftTimeStamp ;
  575. pValues[index].hClient = pItemState[index].hClient ;
  576. pValues[index].type = pItemState[index].vDataValue.vt ;
  577. pValues[index].value.vt = pItemState[index].vDataValue.vt ;
  578. pValues[index].nResult = pErrors[index] ;
  579. ReadDataValues(pValues, pItemState, index) ;
  580. }
  581. CoTaskMemFree( pItemState );
  582. CoTaskMemFree( pErrors );
  583. }
  584. else
  585. {
  586. CoTaskMemFree( pItemState );
  587. CoTaskMemFree( pErrors );
  588. }
  589. // if hr = S_OK, all the data is guarantee to be good.
  590. // if hr = S_FALSE, there are good and bad data.
  591. return hr ;
  592. }
  593. ///////////////////////////////////////////////////////////////////////////
  594. // This Method Write Tags.
  595. // pValues is a structure of type CItem. See CItem in the header file
  596. // ServerHandles is the array originally used in the OPCAddTags().
  597. ///////////////////////////////////////////////////////////////////////////
  598. HRESULT CAppServer::OPCWriteTags(CItem* pValues, DWORD NoOfItems, OPCHANDLE ServerHandles[],int &nErr)
  599. {
  600. // Test for connectivity
  601. if (!CoIsHandlerConnected (m_pIOPCGroupStateMgt[m_nGroupNo]))
  602. m_pIOPCGroupStateMgt[m_nGroupNo].Release() ;
  603. HRESULT hr, *pErrors = NULL;
  604. VARIANT vartype[_OPC_MAX_HANDLE];
  605. CComQIPtr<IOPCSyncIO, &IID_IOPCSyncIO> opcSyncIO (m_pIOPCGroupStateMgt[m_nGroupNo]) ;
  606. if (NoOfItems <= 0 || NoOfItems > _OPC_MAX_HANDLE)
  607. return _OPC_TOO_MANY_ITEMS ;
  608. for (DWORD idx=0; idx<NoOfItems; idx++)
  609. {
  610. // _wcsupr(pValues[idx].cName) ;
  611. VariantInit(&vartype[idx]);
  612. //if(pValues[idx].value.vt==VT_BSTR)
  613. //{
  614. //vartype[idx].vt=VT_BSTR;
  615. // vartype[idx].vt=VT_LPSTR;
  616. // _tcscpy(ss,pValues[idx].value.bstrVal);
  617. //vartype[idx].bstrVal=pValues[idx].value.bstrVal;
  618. // vartype[idx].pbstrVal=&ss;
  619. // }else
  620. // {
  621. VariantClear(&vartype[idx]);
  622. if(S_OK!=VariantCopy(&vartype[idx],&(pValues[idx].value)))
  623. {
  624. continue;
  625. }
  626. //MapStringValToArrayVariant( pValues[0].sName,&(pValues[idx].value),&vartype[idx] );
  627. //ReadDataValues( &vartype[idx],&(pValues[idx].value),pValues[idx].type );
  628. // }
  629. //vartype[idx].vt = pValues[idx].value.vt;
  630. //vartype[idx] = pValues[idx].value;
  631. }
  632. // Write the items using the IOPCSyncIO Interface pointer.
  633. hr = opcSyncIO->Write(NoOfItems, ServerHandles, vartype, &pErrors) ;
  634. if( SUCCEEDED(hr) )
  635. {
  636. nErr = 0 ;
  637. for(DWORD index=0; index < NoOfItems; index++)
  638. {
  639. if ( FAILED(pErrors[index]) )
  640. nErr++ ;
  641. pValues[index].nResult = pErrors[index] ;
  642. }
  643. CoTaskMemFree( pErrors );
  644. }
  645. else
  646. {
  647. CoTaskMemFree( pErrors );
  648. }
  649. return hr ;
  650. }
  651. ///////////////////////////////////////////////////////////////////////////
  652. // Extracts the value corresponding to the vt type. Note, if the value is
  653. // of type VT_BSTR, don't forget to release the bstrVal that came from the
  654. // OPC Server. The OPC Server allocates memory for VT_BSTR type. It is the
  655. // responsibility of the client to release the allocated memory
  656. ///////////////////////////////////////////////////////////////////////////
  657. void CAppServer::ReadDataValues(CItem* pValues, OPCITEMSTATE* pItemState, DWORD index)
  658. {
  659. switch (pItemState[index].vDataValue.vt)
  660. {
  661. case VT_UI1:
  662. pValues[index].value.bVal = pItemState[index].vDataValue.bVal ;
  663. break;
  664. case VT_I2:
  665. pValues[index].value.iVal = pItemState[index].vDataValue.iVal ;
  666. break;
  667. case VT_I4:
  668. pValues[index].value.lVal = pItemState[index].vDataValue.lVal ;
  669. break;
  670. case VT_R4:
  671. pValues[index].value.fltVal = pItemState[index].vDataValue.fltVal ;
  672. break;
  673. case VT_R8:
  674. pValues[index].value.dblVal = pItemState[index].vDataValue.dblVal ;
  675. break;
  676. case VT_BOOL:
  677. pValues[index].value.boolVal = pItemState[index].vDataValue.boolVal ;
  678. break;
  679. case VT_BSTR:
  680. //SysFreString
  681. //wcscpy(pValues[index].value.bstrVal, pItemState[index].vDataValue.bstrVal) ;
  682. pValues[index].value.bstrVal=SysAllocString(pItemState[index].vDataValue.bstrVal);
  683. SysFreeString(pItemState[index].vDataValue.bstrVal);
  684. break;
  685. case VT_NULL:
  686. break;
  687. // Note: VT_CY and VT_DATE are not implemented
  688. default:
  689. break;
  690. }
  691. }
  692. void CAppServer::ReadDataValues(VARIANT* pDes, VARIANT* pScr,VARTYPE nType )
  693. {
  694. switch (nType)
  695. {
  696. case VT_UI1:
  697. pDes->bVal = pScr->bVal ;
  698. break;
  699. case VT_I2:
  700. pDes->iVal = pScr->iVal ;
  701. break;
  702. case VT_I4:
  703. pDes->lVal = pScr->lVal ;
  704. break;
  705. case VT_R4:
  706. pDes->fltVal = pScr->fltVal ;
  707. break;
  708. case VT_R8:
  709. pDes->dblVal = pScr->dblVal ;
  710. break;
  711. case VT_BOOL:
  712. pDes->boolVal = pScr->boolVal ;
  713. break;
  714. case VT_BSTR:
  715. //SysFreString
  716. //wcscpy(pDes->.value.bstrVal, pScr->vDataValue.bstrVal) ;
  717. pDes->bstrVal=SysAllocString(pScr->bstrVal);
  718. SysFreeString(pScr->bstrVal);
  719. break;
  720. case VT_NULL:
  721. break;
  722. // Note: VT_CY and VT_DATE are not implemented
  723. default:
  724. break;
  725. }
  726. }
  727. bool CAppServer::MapStringValToArrayVariant (CString &strValue, VARIANT *pvtSrc, VARIANT *pvtDst)
  728. {
  729. // Source variant must not be empty:
  730. if (pvtSrc->vt == VT_EMPTY)
  731. return (false);
  732. ASSERT (pvtSrc != NULL);
  733. ASSERT (pvtDst != NULL);
  734. ASSERT (pvtSrc->vt & VT_ARRAY);
  735. // Source variant must contain array data:
  736. if (!pvtSrc->parray)
  737. return (false);
  738. int cnRows = 0;
  739. int cnCols = 0;
  740. TCHAR szValue [260];
  741. // Create a local copy of input string:
  742. lstrcpyn (szValue, strValue, (sizeof (szValue) / sizeof (TCHAR)) - sizeof (TCHAR));
  743. // Set number of rows and columns for this array:
  744. // 1-D array:
  745. if (pvtSrc->parray->cDims == 1)
  746. {
  747. cnRows = 1;
  748. cnCols = pvtSrc->parray->rgsabound [0].cElements;
  749. }
  750. // 2-D array:
  751. else if (pvtSrc->parray->cDims == 2)
  752. {
  753. cnRows = pvtSrc->parray->rgsabound [0].cElements;
  754. cnCols = pvtSrc->parray->rgsabound [1].cElements;
  755. }
  756. // Do not support more than 2 dimensions:
  757. else
  758. {
  759. ASSERT (FALSE);
  760. return (false);
  761. }
  762. // Initialize destination array:
  763. VariantInit (pvtDst);
  764. // Set destination array type:
  765. pvtDst->vt = pvtSrc->vt;
  766. // Allocate memory for destination array:
  767. pvtDst->parray = SafeArrayCreate (pvtSrc->vt & ~VT_ARRAY,
  768. pvtSrc->parray->cDims, pvtSrc->parray->rgsabound);
  769. // Copy data to destination array:
  770. VariantCopy (pvtDst, pvtSrc);
  771. // Overwrite fields with data:
  772. int nIndex = 0;
  773. GETARRELEMRET eRet;
  774. BYTE HUGEP *pVal = (BYTE *) pvtDst->parray->pvData;
  775. int cnRowElements = 0;
  776. // Loop over all array elements:
  777. for (int i = 0; i < cnRows * cnCols; i++)
  778. {
  779. // Create a scratch buffer for parsed array element string:
  780. TCHAR szBuffer [260];
  781. // Parse next element in string:
  782. eRet = GetArrayElement (szValue, &nIndex, szBuffer, 260);
  783. // If parse resulted in done code, the break out of loop:
  784. if (eRet == tDone)
  785. break;
  786. // If parse resulted in invalid code, return false:
  787. if (eRet == tInvalid)
  788. {
  789. TRACE (_T("OTC: Invalid character parsing array value\r\n"));
  790. return (false);
  791. }
  792. // If parse reslted in overflow code, return false:
  793. if (eRet == tOverflow)
  794. {
  795. TRACE (_T("OTC: Buffer overflow parsing array value\r\n"));
  796. return (false);
  797. }
  798. // If we make it here, we expect to have element or end of row code:
  799. ASSERT (eRet == tElement || eRet == tEndRow);
  800. // If element is not NULL string, then overwrite field in destination:
  801. if (*szBuffer != _T('\0'))
  802. {
  803. // Cast string to proper type:
  804. switch (pvtDst->vt & ~VT_ARRAY)
  805. {
  806. case VT_UI1 : *pVal = (BYTE) _ttol (szBuffer); break;
  807. case VT_I1 : *pVal = (char) _ttol (szBuffer); break;
  808. case VT_UI2 : *(WORD *)pVal = (WORD) _ttol (szBuffer); break;
  809. case VT_I2 : *(short *)pVal = (short) _ttol (szBuffer); break;
  810. case VT_UI4 : *(DWORD *)pVal = (DWORD) _ttol (szBuffer); break;
  811. case VT_I4 : *(long *)pVal = (long) _ttol (szBuffer); break;
  812. case VT_R4:
  813. // Text to float conversions can throw exceptions, so
  814. // be prepared to handle them:
  815. try
  816. {
  817. #ifdef _UNICODE
  818. *(float *)pVal = (float) wtof (szBuffer);
  819. #else
  820. *(float *)pVal = (float) atof (szBuffer);
  821. #endif
  822. }
  823. catch (...)
  824. {
  825. *(float *)pVal = 0;
  826. }
  827. break;
  828. case VT_R8:
  829. // Text to float conversions can throw exceptions, so
  830. // be prepared to handle them:
  831. try
  832. {
  833. #ifdef _UNICODE
  834. *(double *)pVal = (double) wtof (szBuffer);
  835. #else
  836. *(double *)pVal = (double) atof (szBuffer);
  837. #endif
  838. }
  839. catch (...)
  840. {
  841. *(double *)pVal = 0;
  842. }
  843. break;
  844. // Unexpected type:
  845. default:
  846. ASSERT (FALSE);
  847. break;
  848. }
  849. }
  850. // Increment destination array pointer to accept next element:
  851. // If last element in row:
  852. if (eRet == tEndRow && cnCols > cnRowElements)
  853. {
  854. pVal += (pvtDst->parray->cbElements * (cnCols - cnRowElements));
  855. cnRowElements = 0;
  856. }
  857. // Else next element:
  858. else
  859. {
  860. cnRowElements++;
  861. pVal += pvtDst->parray->cbElements;
  862. }
  863. }
  864. // If we make it here, then return true to indicate success:
  865. return (true);
  866. }
  867. GETARRELEMRET CAppServer::GetArrayElement (LPCTSTR szInBuff, int *pnStart, LPTSTR szOutBuff, int nBuffSize)
  868. {
  869. // Initialize some variables:
  870. TCHAR ch = _T('\r');
  871. BOOL bLeftBracket = FALSE;
  872. int cnChars = 0;
  873. GETARRELEMRET eRet = tElement;
  874. // If we are at the end of the buffer then we're done:
  875. if (!szInBuff [*pnStart])
  876. return (tDone);
  877. // Continue to read characters until we hit the next comma or
  878. // an EOL (End Of Line - "\n") character:
  879. while (TRUE)
  880. {
  881. // Get the next character, and increment start position for next time:
  882. ch = szInBuff [(*pnStart)++];
  883. // If the character is NULL, we are at the end of the record and
  884. // therefore is nothing left to read. Back up the start position
  885. // and break out of the loop.
  886. if (!ch)
  887. {
  888. (*pnStart)--;
  889. break;
  890. }
  891. // Trim leading whitespace, if any. If current character is a space,
  892. // then continue. This will force us to process next character without
  893. // saving current one (a space) in output buffer.
  894. if (!cnChars && _istspace (ch))
  895. continue;
  896. // Brackets and commas delimit the fields:
  897. if (ch == _T('['))
  898. {
  899. if (bLeftBracket)
  900. {
  901. eRet = tInvalid;
  902. break;
  903. }
  904. continue;
  905. }
  906. if (ch == _T(']'))
  907. {
  908. eRet = tEndRow;
  909. break;
  910. }
  911. if (ch == _T(','))
  912. break;
  913. // If we make it here, then the current character is "a keeper".
  914. // Increment the character counter:
  915. ++cnChars;
  916. // Add the current character to the output buffer as long a there
  917. // is room:
  918. if (nBuffSize > 1)
  919. {
  920. // There is room, so add the current character to the output
  921. // buffer, and move output buffer pointer to next position.
  922. *szOutBuff++ = ch;
  923. // Decrement nBuffSize. When it hits zero we know we have
  924. // filled the output buffer to capacity.
  925. --nBuffSize;
  926. }
  927. else
  928. {
  929. // There is no more room in the output buffer. Set the bOverflow
  930. // flag, but don't break out of the loop. We want to keep going
  931. // until we hit a field deleimiter. This will allow us to parse
  932. // the next field, even though this one is too big to deal with.
  933. eRet = tOverflow;
  934. break;
  935. }
  936. }
  937. // Make sure the output string is properly NULL terminated:
  938. *szOutBuff = 0;
  939. // Return return code:
  940. return (eRet);
  941. }
  942. ///////////////////////////////////////////////////////////////////////////
  943. // This Method is used for checking the validity of a group name
  944. // and returns the appropriate group structure if one is found
  945. ///////////////////////////////////////////////////////////////////////////
  946. int CAppServer::CheckGroupName(LPWSTR szGrpName, CGroup &GrpNum)
  947. {
  948. // _wcsupr(szGrpName) ;
  949. for (int indx=0; indx<_OPC_MAX_GROUP; indx++)
  950. if (!wcscmp (szGrpName, GroupNumber[indx].m_szGrpName))
  951. {
  952. GrpNum = GroupNumber[indx] ;
  953. return FALSE ;
  954. }
  955. return TRUE;
  956. }
  957. ///////////////////////////////////////////////////////////////////////////
  958. // This Method adds a new group name to the array. The maximum group
  959. // is defined in _OPC_MAX_GROUP.
  960. ///////////////////////////////////////////////////////////////////////////
  961. int CAppServer::AddGroupName(LPWSTR szGrpName, CGroup &GrpNum)
  962. {
  963. // _wcsupr(szGrpName) ;
  964. for (int indx=0; indx<_OPC_MAX_GROUP; indx++)
  965. if (!GroupNumber[indx].m_szGrpName[0])
  966. {
  967. wcscpy(GroupNumber[indx].m_szGrpName, szGrpName) ;
  968. GroupNumber[indx].m_nGroupNo = indx ;
  969. GrpNum = GroupNumber[indx] ;
  970. break ;
  971. }
  972. else
  973. {
  974. if (!wcscmp(szGrpName, GroupNumber[indx].m_szGrpName))
  975. return TRUE ;
  976. }
  977. if (indx == _OPC_MAX_GROUP) return TRUE ;
  978. return FALSE;
  979. }
  980. ///////////////////////////////////////////////////////////////////////////
  981. // This Method is used to change to another group. The new group
  982. // becomes the default group
  983. ///////////////////////////////////////////////////////////////////////////
  984. int CAppServer::OPCChangeGroup(LPWSTR szGrpName)
  985. {
  986. // _wcsupr(szGrpName) ;
  987. for (int indx=0; indx<_OPC_MAX_GROUP; indx++)
  988. if (!wcscmp(szGrpName, GroupNumber[indx].m_szGrpName))
  989. {
  990. m_nGroupNo = GroupNumber[indx].m_nGroupNo ;
  991. return FALSE ;
  992. }
  993. return TRUE ;
  994. }
  995. ///////////////////////////////////////////////////////////////////////////
  996. // This Method is used to remove Items in a specified Group
  997. ///////////////////////////////////////////////////////////////////////////
  998. HRESULT CAppServer::OPCRemoveItems (LPWSTR szGrpName, DWORD NoOfItems, OPCHANDLE ServerHandles[], HRESULT pErr[])
  999. {
  1000. HRESULT hr ;
  1001. CGroup GrpNum ;
  1002. HRESULT *pErrors = 0;
  1003. if (!CheckGroupName(szGrpName, GrpNum))
  1004. {
  1005. if (!CoIsHandlerConnected(m_pIOPCGroupStateMgt[GrpNum.m_nGroupNo]))
  1006. {
  1007. m_pIOPCGroupStateMgt[GrpNum.m_nGroupNo].Release() ;
  1008. return _OPC_SERVER_NOT_CONNECTED ;
  1009. }
  1010. CComQIPtr <IOPCItemMgt, &IID_IOPCItemMgt> pItemMgt (m_pIOPCGroupStateMgt[GrpNum.m_nGroupNo]) ;
  1011. hr = pItemMgt->RemoveItems(NoOfItems, ServerHandles, &pErrors) ;
  1012. if( SUCCEEDED(hr) )
  1013. CoTaskMemFree( pErrors );
  1014. if (FAILED(hr))
  1015. return hr ;
  1016. m_nTagCount = 0;
  1017. GroupNumber[GrpNum.m_nGroupNo].m_NoOfItems=0;
  1018. pItemMgt.Release() ;
  1019. return hr ;
  1020. }
  1021. else
  1022. return _OPC_INVALID_GROUP_NAME ;
  1023. }
  1024. ///////////////////////////////////////////////////////////////////////////
  1025. // This Method is used to remove a group from the OPC Server
  1026. ///////////////////////////////////////////////////////////////////////////
  1027. HRESULT CAppServer::OPCRemoveGroup (LPWSTR szGrpName)
  1028. {
  1029. HRESULT hr ;
  1030. CGroup GrpNum ;
  1031. if (!CheckGroupName(szGrpName, GrpNum))
  1032. {
  1033. hr = m_pIOPCServer->RemoveGroup(GroupNumber[GrpNum.m_nGroupNo].m_GroupHandle, FALSE) ;
  1034. m_pIOPCGroupStateMgt[GrpNum.m_nGroupNo].Release() ;
  1035. // Reset all the Group Variables to initial value
  1036. GroupNumber[GrpNum.m_nGroupNo].m_szGrpName[0] = L'\0' ;
  1037. GroupNumber[GrpNum.m_nGroupNo].m_GroupHandle = 0 ;
  1038. GroupNumber[GrpNum.m_nGroupNo].m_NoOfItems = 0 ;
  1039. GroupNumber[GrpNum.m_nGroupNo].m_nGroupNo = -1 ;
  1040. m_nGroupNo = -1 ;
  1041. m_nTotalGroups-- ;
  1042. return hr ;
  1043. }
  1044. else
  1045. return _OPC_INVALID_GROUP_NAME ;
  1046. }
  1047. ///////////////////////////////////////////////////////////////////////////
  1048. // This Method is for disconnecting the OPC Server
  1049. ///////////////////////////////////////////////////////////////////////////
  1050. HRESULT CAppServer::OPCDisconnect ()
  1051. {
  1052. // Check all groups in case some are not released
  1053. for (int nGrpNo=0;nGrpNo<m_nTotalGroups;nGrpNo++)
  1054. {
  1055. if (GroupNumber[nGrpNo].m_GroupHandle)
  1056. m_pIOPCGroupStateMgt[nGrpNo].Release() ;
  1057. }
  1058. pcSink->Release(); // OLE should clean this up, but may not have time!
  1059. m_pIOPCServer.Release() ;
  1060. m_sTagNameAll.RemoveAll();
  1061. m_sAddTagName.RemoveAll();
  1062. delete []m_pcItem;
  1063. m_pcItem = NULL;
  1064. return S_OK ;
  1065. }
  1066. ///////////////////////////////////////////////////////////////////////////
  1067. // This Method can be used for testing errors
  1068. ///////////////////////////////////////////////////////////////////////////
  1069. void CAppServer::OPCpErrors (HRESULT* pErrors, int NoOfItems)
  1070. {
  1071. for (int i=0; i<NoOfItems; i++)
  1072. {
  1073. switch (pErrors[i])
  1074. {
  1075. case OPC_E_INVALIDHANDLE:
  1076. wprintf (L"pErrors[i] = OPC_E_INVALIDHANDLE\n") ;
  1077. break ;
  1078. case OPC_E_UNKNOWNITEMID:
  1079. wprintf (L"pErrors[i] = OPC_E_UNKNOWNITEMID\n") ;
  1080. break ;
  1081. case OPC_E_RANGE:
  1082. wprintf (L"pErrors[i] = OPC_E_RANGE\n") ;
  1083. break ;
  1084. case E_FAIL:
  1085. wprintf (L"pErrors[i] = E_FAIL\n") ;
  1086. break ;
  1087. default:
  1088. break ;
  1089. }
  1090. }
  1091. }
  1092. HRESULT CAppServer::getAllTagName( CStringArray *saTags )
  1093. {
  1094. m_sTagNameAll.RemoveAll();
  1095. saTags->RemoveAll();
  1096. VARTYPE m_vt = VT_EMPTY ;
  1097. OPCNAMESPACETYPE nameSpaceType;
  1098. HRESULT hr = 0;
  1099. nameSpaceType = OPC_NS_HIERARCHIAL;
  1100. IEnumString* pEnumString = NULL;
  1101. const unsigned long NEXT_COUNT = 1000 ;
  1102. //LPWSTR pName[NEXT_COUNT];
  1103. CComQIPtr<IOPCBrowseServerAddressSpace, &IID_IOPCBrowseServerAddressSpace> m_pIOPCBrowseServerAddressSpace (m_pIOPCServer);
  1104. if (!m_pIOPCServer || !m_pIOPCBrowseServerAddressSpace)
  1105. return _OPC_INVALID_OBJECT;
  1106. HTREEITEM first=0;
  1107. if( m_pIOPCServer )
  1108. {
  1109. IEnumString* pEnumString = NULL;
  1110. CString csAdd;
  1111. HRESULT hr = m_pIOPCBrowseServerAddressSpace->BrowseOPCItemIDs(OPC_FLAT,
  1112. csAdd.AllocSysString(),
  1113. VT_EMPTY,
  1114. 0,//OPC_READABLE | OPC_WRITEABLE,
  1115. &pEnumString);
  1116. if( hr == S_OK )
  1117. {
  1118. LPWSTR pName = NULL;
  1119. ULONG count = 0;
  1120. while( (hr = pEnumString->Next(1, &pName, &count)) == S_OK )
  1121. {
  1122. // CString does translation from UNICODE to native type
  1123. // (depends on the way the application is built)
  1124. CString name( pName );
  1125. m_sTagNameAll.Add( name );
  1126. saTags->Add( name );
  1127. //pNameDst[cnt++] = pName;
  1128. //InsertChildren( m_pIOPCBrowseServerAddressSpace,pName,pNameDst,cnt );
  1129. CoTaskMemFree( pName );
  1130. }
  1131. pEnumString->Release();
  1132. }
  1133. }
  1134. return S_OK;
  1135. }
  1136. HRESULT CAppServer::BrowseServerAddressSpace(CString *pNameDst, unsigned int& cnt)
  1137. {
  1138. #if 1
  1139. VARTYPE m_vt = VT_EMPTY ;
  1140. OPCNAMESPACETYPE nameSpaceType;
  1141. HRESULT hr = 0;
  1142. nameSpaceType = OPC_NS_HIERARCHIAL;
  1143. IEnumString* pEnumString = NULL;
  1144. const unsigned long NEXT_COUNT = 1000 ;
  1145. //LPWSTR pName[NEXT_COUNT];
  1146. CComQIPtr<IOPCBrowseServerAddressSpace, &IID_IOPCBrowseServerAddressSpace> m_pIOPCBrowseServerAddressSpace (m_pIOPCServer);
  1147. if (!m_pIOPCServer || !m_pIOPCBrowseServerAddressSpace)
  1148. return _OPC_INVALID_OBJECT;
  1149. HTREEITEM first=0;
  1150. if( m_pIOPCServer )
  1151. {
  1152. IEnumString* pEnumString = NULL;
  1153. CString csAdd;
  1154. HRESULT hr = m_pIOPCBrowseServerAddressSpace->BrowseOPCItemIDs(OPC_FLAT,
  1155. csAdd.AllocSysString(),
  1156. VT_EMPTY,
  1157. 0,//OPC_READABLE | OPC_WRITEABLE,
  1158. &pEnumString);
  1159. if( hr == S_OK )
  1160. {
  1161. LPWSTR pName = NULL;
  1162. ULONG count = 0;
  1163. while( (hr = pEnumString->Next(1, &pName, &count)) == S_OK )
  1164. {
  1165. // CString does translation from UNICODE to native type
  1166. // (depends on the way the application is built)
  1167. CString name( pName );
  1168. pNameDst[cnt++]= pName;
  1169. //pNameDst[cnt++] = pName;
  1170. //InsertChildren( m_pIOPCBrowseServerAddressSpace,pName,pNameDst,cnt );
  1171. CoTaskMemFree( pName );
  1172. }
  1173. pEnumString->Release();
  1174. }
  1175. }
  1176. #else
  1177. VARTYPE m_vt = VT_EMPTY ;
  1178. OPCNAMESPACETYPE nameSpaceType;
  1179. HRESULT hr = 0;
  1180. nameSpaceType = OPC_NS_HIERARCHIAL;
  1181. IEnumString* pEnumString = NULL;
  1182. const unsigned long NEXT_COUNT = 1000 ;
  1183. LPWSTR pName[NEXT_COUNT];
  1184. CComQIPtr<IOPCBrowseServerAddressSpace, &IID_IOPCBrowseServerAddressSpace> m_pIOPCBrowseServerAddressSpace (m_pIOPCServer);
  1185. if (!m_pIOPCServer || !m_pIOPCBrowseServerAddressSpace)
  1186. return _OPC_INVALID_OBJECT;
  1187. m_pIOPCBrowseServerAddressSpace->QueryOrganization(&nameSpaceType);
  1188. if( nameSpaceType == OPC_NS_HIERARCHIAL ) // show a tree
  1189. {
  1190. hr = m_pIOPCBrowseServerAddressSpace->BrowseOPCItemIDs(OPC_BRANCH,
  1191. L"*RQE*",
  1192. m_vt,
  1193. 0,
  1194. &pEnumString);
  1195. if( SUCCEEDED(hr) )
  1196. {
  1197. unsigned long count = 0;
  1198. do
  1199. {
  1200. hr = pEnumString->Next(NEXT_COUNT, &pName[0], &count);
  1201. for( ULONG index=0; index<count; index++ )
  1202. {
  1203. //wcscpy (pzName[index], pName[index]) ;
  1204. CoTaskMemFree( pName[index] );
  1205. CString s;
  1206. s = pName[index];
  1207. //AfxMessageBox( s );
  1208. }
  1209. }
  1210. while( hr == S_OK );
  1211. pEnumString->Release();
  1212. *cnt = count ;
  1213. }
  1214. }
  1215. else
  1216. {
  1217. hr = m_pIOPCBrowseServerAddressSpace->BrowseOPCItemIDs(OPC_LEAF,
  1218. L"*RQE*",
  1219. m_vt,
  1220. 0,
  1221. &pEnumString);
  1222. if( SUCCEEDED(hr) )
  1223. {
  1224. unsigned long count = 0;
  1225. do
  1226. {
  1227. hr = pEnumString->Next(NEXT_COUNT, &pName[0], &count);
  1228. for( ULONG index=0; index<count; index++ )
  1229. {
  1230. //wcscpy (pzName[index], pName[index]) ;
  1231. CoTaskMemFree( pName[index] );
  1232. }
  1233. }
  1234. while( hr == S_OK );
  1235. pEnumString->Release();
  1236. }
  1237. }
  1238. #endif
  1239. return hr ;
  1240. }
  1241. HRESULT CAppServer::BrowseServerAddressSpace(CTreeCtrl *pTreeCtrl,CStringArray *saTags)
  1242. {
  1243. VARTYPE m_vt = VT_EMPTY ;
  1244. OPCNAMESPACETYPE nameSpaceType;
  1245. HRESULT hr = 0;
  1246. nameSpaceType = OPC_NS_HIERARCHIAL;
  1247. IEnumString* pEnumString = NULL;
  1248. const unsigned long NEXT_COUNT = 1000 ;
  1249. //LPWSTR pName[NEXT_COUNT];
  1250. CComQIPtr<IOPCBrowseServerAddressSpace, &IID_IOPCBrowseServerAddressSpace> m_pIOPCBrowseServerAddressSpace (m_pIOPCServer);
  1251. if (!m_pIOPCServer || !m_pIOPCBrowseServerAddressSpace)
  1252. return _OPC_INVALID_OBJECT;
  1253. if( pTreeCtrl )
  1254. pTreeCtrl->SetRedraw(FALSE);
  1255. //m_List.SetRedraw(FALSE);
  1256. HTREEITEM first=0;
  1257. //UpdateData();
  1258. if( pTreeCtrl )
  1259. pTreeCtrl->DeleteAllItems();
  1260. if( m_pIOPCServer )
  1261. {
  1262. IEnumString* pEnumString = NULL;
  1263. CString csAdd;
  1264. HRESULT hr = m_pIOPCBrowseServerAddressSpace->BrowseOPCItemIDs(OPC_BRANCH,
  1265. csAdd.AllocSysString(),
  1266. VT_EMPTY,
  1267. OPC_READABLE | OPC_WRITEABLE,
  1268. &pEnumString);
  1269. if( hr == S_OK )
  1270. {
  1271. LPWSTR pName = NULL;
  1272. ULONG count = 0;
  1273. while( (hr = pEnumString->Next(1, &pName, &count)) == S_OK )
  1274. {
  1275. // CString does translation from UNICODE to native type
  1276. // (depends on the way the application is built)
  1277. CString name( pName );
  1278. HTREEITEM item=NULL;
  1279. if( pTreeCtrl )
  1280. item = pTreeCtrl->InsertItem( name );
  1281. if( first == 0 )
  1282. first = item;
  1283. if( pTreeCtrl )
  1284. InsertChildren( m_pIOPCBrowseServerAddressSpace,pName, item,pTreeCtrl );
  1285. CoTaskMemFree( pName );
  1286. }
  1287. pEnumString->Release();
  1288. }
  1289. }
  1290. if( pTreeCtrl )
  1291. pTreeCtrl->SelectItem( first );
  1292. if( pTreeCtrl )
  1293. pTreeCtrl->SetRedraw(TRUE);
  1294. //m_List.SetRedraw(TRUE);
  1295. if( saTags )
  1296. getAllTagName( saTags );
  1297. return hr ;
  1298. }
  1299. void CAppServer::InsertChildren( CComQIPtr<IOPCBrowseServerAddressSpace, &IID_IOPCBrowseServerAddressSpace> pcBrowse,LPWSTR child, HTREEITEM hParent,CTreeCtrl *pTreeCtrl )
  1300. {
  1301. HRESULT hr = pcBrowse->ChangeBrowsePosition( OPC_BROWSE_DOWN, (LPCWSTR)child );
  1302. if( FAILED(hr) )
  1303. return;
  1304. IEnumString* pEnumString = NULL;
  1305. CString csAdd;
  1306. hr = pcBrowse->BrowseOPCItemIDs( OPC_BRANCH,
  1307. csAdd.AllocSysString(),
  1308. VT_EMPTY,
  1309. OPC_READABLE|OPC_WRITEABLE,
  1310. &pEnumString);
  1311. if( hr == S_OK )
  1312. {
  1313. LPWSTR pName = NULL;
  1314. ULONG count = 0;
  1315. while( (hr = pEnumString->Next(1, &pName, &count)) == S_OK )
  1316. {
  1317. CString name( pName ); // translation
  1318. HTREEITEM item = pTreeCtrl->InsertItem( name, hParent );
  1319. InsertChildren( pcBrowse,pName, item,pTreeCtrl );
  1320. CoTaskMemFree( pName );
  1321. }
  1322. pEnumString->Release();
  1323. }
  1324. hr = pcBrowse->ChangeBrowsePosition( OPC_BROWSE_UP, L"" );
  1325. }
  1326. void CAppServer::InsertChildren( CComQIPtr<IOPCBrowseServerAddressSpace, &IID_IOPCBrowseServerAddressSpace> pcBrowse,LPWSTR child,LPWSTR *pNameDst, unsigned int& cnt )
  1327. {
  1328. HRESULT hr = pcBrowse->ChangeBrowsePosition( OPC_BROWSE_DOWN, (LPCWSTR)child );
  1329. if( FAILED(hr) )
  1330. return;
  1331. IEnumString* pEnumString = NULL;
  1332. CString csAdd;
  1333. hr = pcBrowse->BrowseOPCItemIDs( OPC_FLAT,
  1334. csAdd.AllocSysString(),
  1335. VT_EMPTY,
  1336. OPC_READABLE|OPC_WRITEABLE,
  1337. &pEnumString);
  1338. if( hr == S_OK )
  1339. {
  1340. LPWSTR pName = NULL;
  1341. ULONG count = 0;
  1342. while( (hr = pEnumString->Next(1, &pName, &count)) == S_OK )
  1343. {
  1344. CString name( pName ); // translation
  1345. // pNameDst[cnt++] = pName;
  1346. InsertChildren( pcBrowse,pName,pNameDst,cnt );
  1347. CoTaskMemFree( pName );
  1348. }
  1349. pEnumString->Release();
  1350. }
  1351. hr = pcBrowse->ChangeBrowsePosition( OPC_BROWSE_UP, L"" );
  1352. }
  1353. bool CAppServer::IsOK () const { return (m_pIOPCServer != NULL); }