-
Notifications
You must be signed in to change notification settings - Fork 1
/
SimpleOPCFunctions.cpp
306 lines (265 loc) · 9.09 KB
/
SimpleOPCFunctions.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
#include "SimpleOPCFunctions.h"
//Global Variables
// The OPC DA Spec requires that some constants be registered in order to use
// them. The one below refers to the OPC DA 1.0 IDataObject interface.
UINT OPC_DATA_TIME = RegisterClipboardFormat (_T("OPCSTMFORMATDATATIME"));
////////////////////////////////////////////////////////////////////
// Instantiate the IOPCServer interface of the OPCServer
// having the name ServerName. Return a pointer to this interface
//
IOPCServer* InstantiateServer(wchar_t ServerName[])
{
CLSID CLSID_OPCServer;
HRESULT hr;
// get the CLSID from the OPC Server Name:
hr = CLSIDFromString(ServerName, &CLSID_OPCServer);
_ASSERT(!FAILED(hr));
//queue of the class instances to create
LONG cmq = 1; // nbr of class instance to create.
MULTI_QI queue[1] =
{{&IID_IOPCServer,
NULL,
0}};
//Server info:
//COSERVERINFO CoServerInfo =
//{
// /*dwReserved1*/ 0,
// /*pwszName*/ REMOTE_SERVER_NAME,
// /*COAUTHINFO*/ NULL,
// /*dwReserved2*/ 0
//};
// create an instance of the IOPCServer
hr = CoCreateInstanceEx(CLSID_OPCServer, NULL, CLSCTX_SERVER,
/*&CoServerInfo*/NULL, cmq, queue);
_ASSERT(!hr);
// return a pointer to the IOPCServer interface:
return(IOPCServer*) queue[0].pItf;
}
/////////////////////////////////////////////////////////////////////
// Add group "Group1" to the Server whose IOPCServer interface
// is pointed by pIOPCServer.
// Returns a pointer to the IOPCItemMgt interface of the added group
// and a server opc handle to the added group.
//
void AddTheGroup(IOPCServer* pIOPCServer, IOPCItemMgt* &pIOPCItemMgt,
OPCHANDLE& hServerGroup)
{
DWORD dwUpdateRate = 0;
OPCHANDLE hClientGroup = 0;
// Add an OPC group and get a pointer to the IUnknown I/F:
HRESULT hr = pIOPCServer->AddGroup(/*szName*/ L"Group1",
/*bActive*/ FALSE,
/*dwRequestedUpdateRate*/ 1000,
/*hClientGroup*/ hClientGroup,
/*pTimeBias*/ 0,
/*pPercentDeadband*/ 0,
/*dwLCID*/0,
/*phServerGroup*/&hServerGroup,
&dwUpdateRate,
/*riid*/ IID_IOPCItemMgt,
/*ppUnk*/ (IUnknown**) &pIOPCItemMgt);
_ASSERT(!FAILED(hr));
}
//////////////////////////////////////////////////////////////////
// Add the Item ITEM_ID to the group whose IOPCItemMgt interface
// is pointed by pIOPCItemMgt pointer. Return a server opc handle
// to the item.
void AddTheItem(IOPCItemMgt* pIOPCItemMgt, OPCHANDLE& hServerItem,wchar_t *item_id, OPCHANDLE hClientItem)
{
HRESULT hr;
// Array of items to add:
OPCITEMDEF ItemArray[1] =
{{
/*szAccessPath*/ L"",
/*szItemID*/ item_id,
/*bActive*/ TRUE,
/*hClient*/ hClientItem,
/*dwBlobSize*/ 0,
/*pBlob*/ NULL,
/*vtRequestedDataType*/ VT,
/*wReserved*/0
}};
//Add Result:
OPCITEMRESULT* pAddResult=NULL;
HRESULT* pErrors = NULL;
// Add an Item to the previous Group:
hr = pIOPCItemMgt->AddItems(1, ItemArray, &pAddResult, &pErrors);
if (hr != S_OK){
printf("Failed call to AddItems function. Error code = %x\n", hr);
exit(0);
}
// Server handle for the added item:
hServerItem = pAddResult[0].hServer;
// release memory allocated by the server:
CoTaskMemFree(pAddResult->pBlob);
CoTaskMemFree(pAddResult);
pAddResult = NULL;
CoTaskMemFree(pErrors);
pErrors = NULL;
}
///////////////////////////////////////////////////////////////////////////////
// Read from device the value of the item having the "hServerItem" server
// handle and belonging to the group whose one interface is pointed by
// pGroupIUnknown. The value is put in varValue.
//
void ReadItem(IUnknown* pGroupIUnknown, OPCHANDLE hServerItem, VARIANT& varValue)
{
// value of the item:
OPCITEMSTATE* pValue = NULL;
//get a pointer to the IOPCSyncIOInterface:
IOPCSyncIO* pIOPCSyncIO;
pGroupIUnknown->QueryInterface(__uuidof(pIOPCSyncIO), (void**) &pIOPCSyncIO);
// read the item value from the device:
HRESULT* pErrors = NULL; //to store error code(s)
HRESULT hr = pIOPCSyncIO->Read(OPC_DS_DEVICE, 1, &hServerItem, &pValue, &pErrors);
_ASSERT(!hr);
_ASSERT(pValue!=NULL);
varValue = pValue[0].vDataValue;
//Release memeory allocated by the OPC server:
CoTaskMemFree(pErrors);
pErrors = NULL;
CoTaskMemFree(pValue);
pValue = NULL;
// release the reference to the IOPCSyncIO interface:
pIOPCSyncIO->Release();
}
///////////////////////////////////////////////////////////////////////////
// Remove the item whose server handle is hServerItem from the group
// whose IOPCItemMgt interface is pointed by pIOPCItemMgt
//
void RemoveItem(IOPCItemMgt* pIOPCItemMgt, OPCHANDLE hServerItem)
{
// server handle of items to remove:
OPCHANDLE hServerArray[1];
hServerArray[0] = hServerItem;
//Remove the item:
HRESULT* pErrors; // to store error code(s)
HRESULT hr = pIOPCItemMgt->RemoveItems(1, hServerArray, &pErrors);
_ASSERT(!hr);
//release memory allocated by the server:
CoTaskMemFree(pErrors);
pErrors = NULL;
}
////////////////////////////////////////////////////////////////////////
// Remove the Group whose server handle is hServerGroup from the server
// whose IOPCServer interface is pointed by pIOPCServer
//
void RemoveGroup (IOPCServer* pIOPCServer, OPCHANDLE hServerGroup)
{
// Remove the group:
HRESULT hr = pIOPCServer->RemoveGroup(hServerGroup, FALSE);
if (hr != S_OK){
if (hr == OPC_S_INUSE)
printf ("Failed to remove OPC group: object still has references to it.\n");
else printf ("Failed to remove OPC group. Error code = %x\n", hr);
exit(0);
}
}
///////////////////////////////////////////////////////////////////////////////
// Set up an asynchronous connection with the server by means of the OPC DA
// 2.0 IConnectionPointContainer
//
void SetDataCallback(
IUnknown* pGroupIUnknown,
IOPCDataCallback* pSOCDataCallback,
IConnectionPoint* &pIConnectionPoint,
DWORD *pdwCookie)
{
HRESULT hr;
IConnectionPointContainer* pIConnPtCont = NULL; //pointer to IConnectionPointContainer
//interface
//Get a pointer to the IConnectionPointContainer interface:
hr = pGroupIUnknown->QueryInterface(__uuidof(pIConnPtCont), (void**) &pIConnPtCont);
if (hr != S_OK){
printf ("Could not obtain a pointer to IConnectionPointContainer. Error = %x\n",
hr);
return;
}
// Call the IConnectionPointContainer::FindConnectionPoint method on the
// group object to obtain a Connection Point
hr = pIConnPtCont->FindConnectionPoint(IID_IOPCDataCallback, &pIConnectionPoint);
if (hr != S_OK) {
printf ("Failed call to FindConnectionPoint. Error = %x\n", hr);
//*ptkAsyncConnection = 0;
return;
}
// Now set up the Connection Point.
// TO BE DONE: in Kepware´s code the IOPCDataCallback object is instantiated
// here, as a consequence of the FindConnectionPoint success. It makes sense,
// for if not we would have instantiated it unnecessarly.
hr = pIConnectionPoint->Advise(pSOCDataCallback, pdwCookie);
if (hr != S_OK) {
printf ("Failed call to IConnectionPoint::Advise. Error = %x\n", hr);
*pdwCookie = 0;
}
// From this point on we do not need anymore the pointer to the
// IConnectionPointContainer interface, so release it
pIConnPtCont->Release();
return;
}
/////////////////////////////////////////////////////////////////////////
// Function to mimic the Delphi VARTOSTR procedure in which a VARIANT
// is converted to a string. Only a few VARIANT types are supported in
// this version.
//
// Luiz T. S. Mendes - 07/09/2011
bool VarToStr (VARIANT pvar, char *buffer)
{
bool vReturn = true;
switch (pvar.vt & ~VT_ARRAY)
{
case VT_BOOL:
case VT_I1:
sprintf_s(buffer,16, "%d", pvar.iVal); break;
case VT_I2:
sprintf_s(buffer,16, "%d", pvar.intVal); break;
case VT_I4:
sprintf_s(buffer,16, "%ld", pvar.intVal); break;
case VT_UI1:
sprintf_s(buffer,16, "%u", pvar.uiVal); break;
case VT_UI2:
sprintf_s(buffer,16, "%u", pvar.ulVal); break;
case VT_UI4:
sprintf_s(buffer,16, "%lu", pvar.ulVal); break;
case VT_R4:
sprintf_s(buffer,16, "%6.2f", pvar.fltVal); break;
case VT_R8:
sprintf_s(buffer,16, "%lu", pvar.dblVal); break;
case VT_BSTR:
sprintf_s(buffer,16, "%s", pvar.bstrVal); break;
default:
sprintf_s(buffer,16, "%s", NULL);
vReturn = false;
break;
}
return(vReturn);
}
///////////////////////////////////////////////////////////////////////////////////////
// Setup a VARIANT type variable with the data sent and the type specified
bool GenerateVar (VARIANT* pvar, VARTYPE var_type, void* var_value)
{
bool vReturn = true;
pvar->vt = var_type; //Assign var_type
//Typecast from void* to the specified type*, and them dereferenciate.
switch (var_type & ~VT_ARRAY)
{
case VT_BOOL:
case VT_I1:
pvar->iVal = *static_cast<char*>(var_value); break;
case VT_I2:
pvar->intVal = *static_cast<short*>(var_value); break;
case VT_I4:
pvar->intVal = *static_cast<long*>(var_value); break;
case VT_UI1:
pvar->uiVal = *static_cast<unsigned char*>(var_value); break;
case VT_UI2:
pvar->ulVal = *static_cast<unsigned short*>(var_value); break;
case VT_UI4:
pvar->ulVal = *static_cast<unsigned long*>(var_value); break;
case VT_R4:
pvar->fltVal = *static_cast<float*>(var_value); break;
case VT_R8:
pvar->dblVal = *static_cast<double*>(var_value); break;
}
return(vReturn);
}