ISIS Logo
NINetVar
An EPICS support module to export National Instruments Network Shared Variables as process variables
NINetVarInterface.cpp
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2013 Science and Technology Facilities Council (STFC), GB.
3 * All rights reverved.
4 * This file is distributed subject to a Software License Agreement found
5 * in the file LICENSE.txt that is included with this distribution.
6 \*************************************************************************/
7 
10 
11 #include <stdio.h>
12 
13 //#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
14 #include <windows.h>
15 
16 #include <string>
17 #include <vector>
18 #include <map>
19 #include <list>
20 #include <stdexcept>
21 #include <sstream>
22 #include <fstream>
23 #include <iostream>
24 #include <algorithm>
25 
26 #include <cvirte.h>
27 #include <userint.h>
28 #include <cvinetv.h>
29 
30 #include <atlbase.h>
31 #include <comutil.h>
32 
33 #include <shareLib.h>
34 #include <macLib.h>
35 #include <epicsGuard.h>
36 #include <epicsString.h>
37 #include <errlog.h>
38 
39 #include "asynPortDriver.h"
40 
41 #include "NINetVarInterface.h"
42 #include "cnvconvert.h"
43 
44 #define MAX_PATH_LEN 256
45 
46 static const char *driverName="NINetVarInterface";
47 
49 class NINetVarException : public std::runtime_error
50 {
51 public:
52  explicit NINetVarException(const std::string& message) : std::runtime_error(message) { }
53  explicit NINetVarException(const std::string& function, int code) : std::runtime_error(ni_message(function, code)) { }
54 private:
55  static std::string ni_message(const std::string& function, int code)
56  {
57  return function + ": " + CNVGetErrorDescription(code);
58  }
59 };
60 
61 #define ERROR_CHECK(__func, __code) \
62  if (__code < 0) \
63  { \
64  throw NINetVarException(__func, __code); \
65  }
66 
68 static const char* connectionStatus(CNVConnectionStatus status)
69 {
70  switch (status)
71  {
72  case CNVConnecting:
73  return "Connecting...";
74  break;
75  case CNVConnected:
76  return "Connected";
77  break;
78  case CNVDisconnected:
79  return "Disconnected";
80  break;
81  default:
82  return "UNKNOWN";
83  break;
84  }
85 }
86 
89 {
90  CNVData m_value;
91  public:
92  ScopedCNVData(const CNVData& d) : m_value(d) { }
93  ScopedCNVData() : m_value(0) { }
94  ScopedCNVData(const ScopedCNVData& d) : m_value(d.m_value) { }
95  CNVData* operator&() { return &m_value; }
96  operator CNVData*() { return &m_value; }
97  operator CNVData() { return m_value; }
98  ScopedCNVData& operator=(const ScopedCNVData& d) { m_value = d.m_value; return *this; }
99  ScopedCNVData& operator=(const CNVData& d) { m_value = d; return *this; }
100  void dispose()
101  {
102  int status = 0;
103  if (m_value != 0)
104  {
105  status = CNVDisposeData(m_value);
106  m_value = 0;
107  }
108  ERROR_CHECK("CNVDisposeData", status);
109  }
111 };
112 
114 struct NvItem
115 {
116  enum { Read=0x1, Write=0x2, BufferedRead=0x4, BufferedWrite=0x8 } NvAccessMode;
117  std::string nv_name;
118  std::string type;
119  int field;
120  unsigned access;
121  int id;
122  std::vector<char> array_data;
123  CNVSubscriber subscriber;
124  CNVBufferedSubscriber b_subscriber;
125  CNVWriter writer;
126  CNVBufferedWriter b_writer;
127  NvItem(const char* nv_name_, const char* type_, unsigned access_, int field_) : nv_name(nv_name_), type(type_), access(access_),
128  field(field_), id(-1), subscriber(0), b_subscriber(0), writer(0), b_writer(0)
129  {
130  std::replace(nv_name.begin(), nv_name.end(), '/', '\\'); // we accept / as well as \ in the XML file for path to variable
131  }
133  void report(const std::string& name, FILE* fp)
134  {
135  fprintf(fp, "Report for asyn parameter \"%s\" type \"%s\" network variable \"%s\"\n", name.c_str(), type.c_str(), nv_name.c_str());
136  if (array_data.size() > 0)
137  {
138  fprintf(fp, " Current array size: %d\n", array_data.size());
139  }
140  if (field != -1)
141  {
142  fprintf(fp, " Network variable structure index: %d\n", field);
143  }
144  report(fp, "subscriber", subscriber, false);
145  report(fp, "buffered subscriber", b_subscriber, true);
146  report(fp, "writer", writer, false);
147  report(fp, "buffered writer", b_writer, true);
148  }
149  void report(FILE* fp, const char* conn_type, void* handle, bool buffered)
150  {
151  int error, conn_error;
152  CNVConnectionStatus status;
153  fprintf(fp, " Connection type: %s", conn_type);
154  if (handle == 0)
155  {
156  fprintf(fp, " Status: <not being used>\n");
157  return;
158  }
159  error = CNVGetConnectionAttribute(handle, CNVConnectionStatusAttribute, &status);
160  ERROR_CHECK("CNVGetConnectionAttribute", error);
161  fprintf(fp, " status: %s", connectionStatus(status));
162  error = CNVGetConnectionAttribute(handle, CNVConnectionErrorAttribute, &conn_error);
163  ERROR_CHECK("CNVGetConnectionAttribute", error);
164  if (conn_error < 0)
165  {
166  fprintf(fp, " error present: %s", CNVGetErrorDescription(conn_error));
167  }
168  if (buffered)
169  {
170  int nitems, maxitems;
171  error = CNVGetConnectionAttribute(handle, CNVClientBufferNumberOfItemsAttribute, &nitems);
172  ERROR_CHECK("CNVGetConnectionAttribute", error);
173  error = CNVGetConnectionAttribute(handle, CNVClientBufferMaximumItemsAttribute, &maxitems);
174  ERROR_CHECK("CNVGetConnectionAttribute", error);
175  fprintf(fp, " Client buffer: %d items (buffer size = %d)", nitems, maxitems);
176  }
177  fprintf(fp, "\n");
178  }
179 };
180 
183 {
185  std::string nv_name;
187  CallbackData(NINetVarInterface* intf_, const std::string& nv_name_, int param_index_) : intf(intf_), nv_name(nv_name_), param_index(param_index_) { }
188 };
189 
190 static void CVICALLBACK DataCallback (void * handle, CNVData data, void * callbackData);
191 static void CVICALLBACK StatusCallback (void * handle, CNVConnectionStatus status, int error, void * callbackData);
192 static void CVICALLBACK DataTransferredCallback(void * handle, int error, void * callbackData);
193 
195 {
196  int error, running;
197  CallbackData* cb_data;
198  int waitTime = 3000;
199  int clientBufferMaxItems = 200;
200  error = CNVVariableEngineIsRunning(&running);
201  ERROR_CHECK("CNVVariableEngineIsRunning", error);
202  if (running == 0)
203  {
204  std::cerr << "connectVars: Variable engine is not running" << std::endl;
205  }
206  for(params_t::const_iterator it=m_params.begin(); it != m_params.end(); ++it)
207  {
208  NvItem* item = it->second;
209  cb_data = new CallbackData(this, item->nv_name, item->id);
210 
211  // check variable exists and create if not???
212 
213  // create either reader or buffered reader
214  std::cerr << "connectVars: connecting to \"" << item->nv_name << "\"" << std::endl;
215  if (item->access & NvItem::Read)
216  {
217  error = CNVCreateSubscriber(item->nv_name.c_str(), DataCallback, StatusCallback, cb_data, waitTime, 0, &(item->subscriber));
218  ERROR_CHECK("CNVCreateSubscriber", error);
219  }
220  else if (item->access & NvItem::BufferedRead)
221  {
222  error = CNVCreateBufferedSubscriber(item->nv_name.c_str(), StatusCallback, cb_data, clientBufferMaxItems, waitTime, 0, &(item->b_subscriber));
223  ERROR_CHECK("CNVCreateBufferedSubscriber", error);
224  }
225  // create either writer or buffered writer
226  if (item->access & NvItem::Write)
227  {
228  error = CNVCreateWriter(item->nv_name.c_str(), StatusCallback, cb_data, waitTime, 0, &(item->writer));
229  ERROR_CHECK("CNVCreateWriter", error);
230  }
231  else if (item->access & NvItem::BufferedWrite)
232  {
233  error = CNVCreateBufferedWriter(item->nv_name.c_str(), DataTransferredCallback, StatusCallback, cb_data, clientBufferMaxItems, waitTime, 0, &(item->b_writer));
234  ERROR_CHECK("CNVCreateBufferedWriter", error);
235  }
236  }
237 }
238 
240 static std::string dataQuality(CNVDataQuality quality)
241 {
242  std::string res;
243  char* description = NULL;
244  int error = CNVGetDataQualityDescription(quality, ";", &description);
245  if (error == 0)
246  {
247  res = description;
248  CNVFreeMemory(description);
249  }
250  else
251  {
252  res = std::string("CNVGetDataQualityDescription: ") + CNVGetErrorDescription(error);
253  }
254  return res;
255 }
256 
258 static void CVICALLBACK DataTransferredCallback(void * handle, int error, void * callbackData)
259 {
260  CallbackData* cb_data = (CallbackData*)callbackData;
261  cb_data->intf->dataTransferredCallback(handle, error, cb_data);
262 }
263 
265 void NINetVarInterface::dataTransferredCallback (void * handle, int error, CallbackData* cb_data)
266 {
267  if (error < 0)
268  {
269  std::cerr << "dataTransferredCallback: \"" << cb_data->nv_name << "\": " << CNVGetErrorDescription(error) << std::endl;
270  }
271 // else
272 // {
273 // std::cerr << "dataTransferredCallback: " << cb_data->nv_name << " OK " << std::endl;
274 // }
275 }
276 
278 static void CVICALLBACK DataCallback (void * handle, CNVData data, void * callbackData)
279 {
280  CallbackData* cb_data = (CallbackData*)callbackData;
281  cb_data->intf->dataCallback(handle, data, cb_data);
282  CNVDisposeData (data);
283 }
284 
286 void NINetVarInterface::dataCallback (void * handle, CNVData data, CallbackData* cb_data)
287 {
288 // std::cerr << "dataCallback: index " << cb_data->param_index << std::endl;
289  try
290  {
291  updateParamCNV(cb_data->param_index, data, true);
292  }
293  catch(const std::exception& ex)
294  {
295  std::cerr << "dataCallback: ERROR updating param index " << cb_data->param_index << ": " << ex.what() << std::endl;
296  }
297  catch(...)
298  {
299  std::cerr << "dataCallback: ERROR updating param index " << cb_data->param_index << std::endl;
300  }
301 }
302 
303 template<typename T>
304 void NINetVarInterface::updateParamValue(int param_index, T val, bool do_asyn_param_callbacks)
305 {
306  const char *paramName = NULL;
307  m_driver->getParamName(param_index, &paramName);
308  m_driver->lock();
309  if (m_params[paramName]->type == "float64")
310  {
311  m_driver->setDoubleParam(param_index, convertToScalar<double>(val));
312  }
313  else if (m_params[paramName]->type == "int32" || m_params[paramName]->type == "boolean")
314  {
315  m_driver->setIntegerParam(param_index, convertToScalar<int>(val));
316  }
317  else if (m_params[paramName]->type == "string")
318  {
319  m_driver->setStringParam(param_index, convertToPtr<char>(val));
320  }
321  else
322  {
323  std::cerr << "updateParamValue: unknown type \"" << m_params[paramName]->type << "\" for param \"" << paramName << "\"" << std::endl;
324  }
325  if (do_asyn_param_callbacks)
326  {
327  m_driver->callParamCallbacks();
328  }
329  m_driver->unlock();
330 }
331 
332 template<typename T,typename U>
333 void NINetVarInterface::updateParamArrayValueImpl(int param_index, T* val, size_t nElements)
334 {
335  const char *paramName = NULL;
336  m_driver->getParamName(param_index, &paramName);
337  std::vector<char>& array_data = m_params[paramName]->array_data;
338  U* eval = convertToPtr<U>(val);
339  if (eval != 0)
340  {
341  array_data.resize(nElements * sizeof(T));
342  memcpy(&(array_data[0]), eval, nElements * sizeof(T));
343  (m_driver->*C2CNV<U>::asyn_callback)(reinterpret_cast<U*>(&(array_data[0])), nElements, param_index, 0);
344  }
345  else
346  {
347  std::cerr << "updateParamArrayValue: cannot update param \"" << paramName << "\": shared variable data type incompatible \"" << C2CNV<T>::desc << "\"" << std::endl;
348  }
349 }
350 
351 template<typename T>
352 void NINetVarInterface::updateParamArrayValue(int param_index, T* val, size_t nElements)
353 {
354  const char *paramName = NULL;
355  m_driver->getParamName(param_index, &paramName);
356  m_driver->lock();
357  if (m_params[paramName]->type == "float64array")
358  {
359  updateParamArrayValueImpl<T,epicsFloat64>(param_index, val, nElements);
360  }
361  if (m_params[paramName]->type == "float32array")
362  {
363  updateParamArrayValueImpl<T,epicsFloat32>(param_index, val, nElements);
364  }
365  else if (m_params[paramName]->type == "int32array")
366  {
367  updateParamArrayValueImpl<T,epicsInt32>(param_index, val, nElements);
368  }
369  else if (m_params[paramName]->type == "int16array")
370  {
371  updateParamArrayValueImpl<T,epicsInt16>(param_index, val, nElements);
372  }
373  else if (m_params[paramName]->type == "int8array")
374  {
375  updateParamArrayValueImpl<T,epicsInt8>(param_index, val, nElements);
376  }
377  else
378  {
379  std::cerr << "updateParamArrayValue: unknown type \"" << m_params[paramName]->type << "\" for param \"" << paramName << "\"" << std::endl;
380  }
381  m_driver->unlock();
382 }
383 
384 template <typename T>
385 void NINetVarInterface::readArrayValue(const char* paramName, T* value, size_t nElements, size_t* nIn)
386 {
387  std::vector<char>& array_data = m_params[paramName]->array_data;
388  size_t n = array_data.size() / sizeof(T);
389  if (n > nElements)
390  {
391  n = nElements;
392  }
393  *nIn = n;
394  memcpy(value, &(array_data[0]), n * sizeof(T));
395 }
396 
397 template<CNVDataType cnvType>
398 void NINetVarInterface::updateParamCNVImpl(int param_index, CNVData data, CNVDataType type, unsigned int nDims, bool do_asyn_param_callbacks)
399 {
400  if (nDims == 0)
401  {
403  int status = CNVGetScalarDataValue (data, type, &val);
404  ERROR_CHECK("CNVGetScalarDataValue", status);
405  updateParamValue(param_index, val, do_asyn_param_callbacks);
407  }
408  else
409  {
411  size_t dimensions[10];
412  int status = CNVGetArrayDataDimensions(data, nDims, dimensions);
413  ERROR_CHECK("CNVGetArrayDataDimensions", status);
414  size_t nElements = 1;
415  for(unsigned i=0; i<nDims; ++i)
416  {
417  nElements *= dimensions[i];
418  }
419  val = new CNV2C<cnvType>::ctype[nElements];
420  status = CNVGetArrayDataValue(data, type, val, nElements);
421  ERROR_CHECK("CNVGetArrayDataValue", status);
422  updateParamArrayValue(param_index, val, nElements);
423  delete[] val;
424  }
425 }
426 
427 void NINetVarInterface::updateParamCNV (int param_index, CNVData data, bool do_asyn_param_callbacks)
428 {
429  unsigned int nDims;
430  unsigned int serverError;
431  CNVDataType type;
432  CNVDataQuality quality;
433  unsigned __int64 timestamp;
434  int year, month, day, hour, minute, good;
435  double second;
436  int status;
437  unsigned short numberOfFields = 0;
438  const char *paramName = NULL;
439  m_driver->getParamName(param_index, &paramName);
440  if (data == 0)
441  {
442 // std::cerr << "updateParamCNV: no data for param " << paramName << std::endl;
443  return;
444  }
445  status = CNVGetDataType (data, &type, &nDims);
446  ERROR_CHECK("CNVGetDataType", status);
447  if (type == CNVStruct)
448  {
449  int field = m_params[paramName]->field;
450  status = CNVGetNumberOfStructFields(data, &numberOfFields);
451  ERROR_CHECK("CNVGetNumberOfStructFields", status);
452  if (numberOfFields == 0)
453  {
454  throw std::runtime_error("number of fields");
455  }
456  if (field < 0 || field >= numberOfFields)
457  {
458  throw std::runtime_error("field index");
459  }
460  CNVData* fields = new CNVData[numberOfFields];
461  status = CNVGetStructFields(data, fields, numberOfFields);
462  ERROR_CHECK("CNVGetStructFields", status);
463  updateParamCNV(param_index, fields[field], do_asyn_param_callbacks);
464  delete[] fields;
465  return;
466  }
467  status = CNVGetDataQuality(data, &quality);
468  ERROR_CHECK("CNVGetDataQuality", status);
469  status = CNVCheckDataQuality(quality, &good);
470  ERROR_CHECK("CNVCheckDataQuality", status);
471  status = CNVGetDataUTCTimestamp(data, &timestamp);
472  ERROR_CHECK("CNVGetDataUTCTimestamp", status);
473  status = CNVGetTimestampInfo(timestamp, &year, &month, &day, &hour, &minute, &second);
474  ERROR_CHECK("CNVGetTimestampInfo", status);
475  if (good == 0)
476  {
477  std::cerr << "updateParamCNV: data for param " << paramName << " is not good quality: " << dataQuality(quality) << std::endl;
478  }
479  switch(type)
480  {
481  case CNVEmpty:
482  break;
483 
484  case CNVBool:
485  updateParamCNVImpl<CNVBool>(param_index, data, type, nDims, do_asyn_param_callbacks);
486  break;
487 
488  case CNVString:
489  updateParamCNVImpl<CNVString>(param_index, data, type, nDims, do_asyn_param_callbacks);
490  break;
491 
492  case CNVSingle:
493  updateParamCNVImpl<CNVSingle>(param_index, data, type, nDims, do_asyn_param_callbacks);
494  break;
495 
496  case CNVDouble:
497  updateParamCNVImpl<CNVDouble>(param_index, data, type, nDims, do_asyn_param_callbacks);
498  break;
499 
500  case CNVInt8:
501  updateParamCNVImpl<CNVInt8>(param_index, data, type, nDims, do_asyn_param_callbacks);
502  break;
503 
504  case CNVUInt8:
505  updateParamCNVImpl<CNVUInt8>(param_index, data, type, nDims, do_asyn_param_callbacks);
506  break;
507 
508  case CNVInt16:
509  updateParamCNVImpl<CNVInt16>(param_index, data, type, nDims, do_asyn_param_callbacks);
510  break;
511 
512  case CNVUInt16:
513  updateParamCNVImpl<CNVUInt16>(param_index, data, type, nDims, do_asyn_param_callbacks);
514  break;
515 
516  case CNVInt32:
517  updateParamCNVImpl<CNVInt32>(param_index, data, type, nDims, do_asyn_param_callbacks);
518  break;
519 
520  case CNVUInt32:
521  updateParamCNVImpl<CNVUInt32>(param_index, data, type, nDims, do_asyn_param_callbacks);
522  break;
523 
524  case CNVInt64:
525  updateParamCNVImpl<CNVInt64>(param_index, data, type, nDims, do_asyn_param_callbacks);
526  break;
527 
528  case CNVUInt64:
529  updateParamCNVImpl<CNVUInt64>(param_index, data, type, nDims, do_asyn_param_callbacks);
530  break;
531 
532  default:
533  std::cerr << "updateParamCNV: unknown type " << type << " for param " << paramName << std::endl;
534  break;
535  }
536  status = CNVGetDataServerError(data, &serverError);
537  if (status == 0 && serverError != 0)
538  {
539  std::cerr << "updateParamCNV: Server error: " << serverError << std::endl;
540  }
541  else if (status < 0)
542  {
543  std::cerr << "updateParamCNV: CNVGetDataServerError: " << CNVGetErrorDescription(status) << std::endl;
544  }
545 }
546 
547 
549 static void CVICALLBACK StatusCallback (void * handle, CNVConnectionStatus status, int error, void * callbackData)
550 {
551  CallbackData* cb_data = (CallbackData*)callbackData;
552  cb_data->intf->statusCallback(handle, status, error, cb_data);
553 }
554 
556 void NINetVarInterface::statusCallback (void * handle, CNVConnectionStatus status, int error, CallbackData* cb_data)
557 {
558  if (error < 0)
559  {
560  std::cerr << "StatusCallback: " << cb_data->nv_name << ": " << CNVGetErrorDescription(error) << std::endl;
561  }
562  else
563  {
564  std::cerr << "StatusCallback: " << cb_data->nv_name << " is " << connectionStatus(status) << std::endl;
565  }
566 }
567 
568 static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
569 
570 static void initCV(void*)
571 {
572  char* dummy_argv[2] = { "NINetVarInterface", NULL };
573  if (InitCVIRTE (0, dummy_argv, 0) == 0)
574  throw std::runtime_error("InitCVIRTE");
575 }
576 
578 {
579  m_pxmldom = NULL;
580  CoInitializeEx(NULL, COINIT_MULTITHREADED);
581  HRESULT hr=CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_SERVER, IID_IXMLDOMDocument2, (void**)&m_pxmldom);
582  if (FAILED(hr))
583  {
584  throw std::runtime_error("Cannot load DomFromCom");
585  }
586  if (m_pxmldom != NULL)
587  {
588  m_pxmldom->put_async(VARIANT_FALSE);
589  m_pxmldom->put_validateOnParse(VARIANT_FALSE);
590  m_pxmldom->put_resolveExternals(VARIANT_FALSE);
591  }
592  else
593  {
594  throw std::runtime_error("Cannot load DomFromCom");
595  }
596 }
597 
601 NINetVarInterface::NINetVarInterface(const char *configSection, const char* configFile, int options) :
602  m_configSection(configSection), m_options(options)
603 {
604  epicsThreadOnce(&onceId, initCV, NULL);
605  DomFromCOM();
606  short sResult = FALSE;
607  char* configFile_expanded = macEnvExpand(configFile);
608  m_configFile = configFile_expanded;
609  HRESULT hr = m_pxmldom->load(_variant_t(configFile_expanded), &sResult);
610  free(configFile_expanded);
611  if(FAILED(hr))
612  {
613  throw std::runtime_error("Cannot load XML \"" + m_configFile + "\" (expanded from \"" + std::string(configFile) + "\"): load failure");
614  }
615  if (sResult != VARIANT_TRUE)
616  {
617  throw std::runtime_error("Cannot load XML \"" + m_configFile + "\" (expanded from \"" + std::string(configFile) + "\"): load failure");
618  }
619  std::cerr << "Loaded XML config file \"" << m_configFile << "\" (expanded from \"" << configFile << "\")" << std::endl;
620 // m_extint = doPath("/lvinput/extint/@path").c_str();
621  epicsAtExit(epicsExitFunc, this);
622 }
623 
624 // need to be careful here as might get called at wrong point. May need to check with driver.
626 {
627 // NINetVarInterface* netvarint = static_cast<NINetVarInterface*>(arg);
628 // if (netvarint == NULL)
629 // {
630 // return;
631 // }
632 // if ( netvarint->checkOption(Something) )
633 // {
634 // }
635  CNVFinish();
636 }
637 
639 {
640  long n = 0;
641  char control_name_xpath[MAX_PATH_LEN];
642  _snprintf(control_name_xpath, sizeof(control_name_xpath), "/ninetvar/section[@name='%s']/param", m_configSection.c_str());
643  IXMLDOMNodeList* pXMLDomNodeList = NULL;
644  HRESULT hr = m_pxmldom->selectNodes(_bstr_t(control_name_xpath), &pXMLDomNodeList);
645  if (SUCCEEDED(hr) && pXMLDomNodeList != NULL)
646  {
647  pXMLDomNodeList->get_length(&n);
648  pXMLDomNodeList->Release();
649  }
650  return n;
651 }
652 
653 void NINetVarInterface::createParams(asynPortDriver* driver)
654 {
655  static const char* functionName = "createParams";
656  m_driver = driver;
657  getParams();
658  for(params_t::iterator it=m_params.begin(); it != m_params.end(); ++it)
659  {
660  NvItem* item = it->second;
661  if (item->type == "float64")
662  {
663  m_driver->createParam(it->first.c_str(), asynParamFloat64, &(item->id));
664  }
665  else if (item->type == "int32" || item->type == "boolean")
666  {
667  m_driver->createParam(it->first.c_str(), asynParamInt32, &(item->id));
668  }
669  else if (item->type == "string")
670  {
671  m_driver->createParam(it->first.c_str(), asynParamOctet, &(item->id));
672  }
673  else if (item->type == "float64array")
674  {
675  m_driver->createParam(it->first.c_str(), asynParamFloat64Array, &(item->id));
676  }
677  else if (item->type == "float32array")
678  {
679  m_driver->createParam(it->first.c_str(), asynParamFloat32Array, &(item->id));
680  }
681  else if (item->type == "int32array")
682  {
683  m_driver->createParam(it->first.c_str(), asynParamInt32Array, &(item->id));
684  }
685  else if (item->type == "int16array")
686  {
687  m_driver->createParam(it->first.c_str(), asynParamInt16Array, &(item->id));
688  }
689  else if (item->type == "int8array")
690  {
691  m_driver->createParam(it->first.c_str(), asynParamInt8Array, &(item->id));
692  }
693  else
694  {
695  errlogSevPrintf(errlogMajor, "%s:%s: unknown type %s for parameter %s\n", driverName,
696  functionName, item->type.c_str(), it->first.c_str());
697  }
698  }
699  connectVars();
700 }
701 
703 {
704  m_params.clear();
705  char control_name_xpath[MAX_PATH_LEN];
706  _snprintf(control_name_xpath, sizeof(control_name_xpath), "/ninetvar/section[@name='%s']/param", m_configSection.c_str());
707  IXMLDOMNodeList* pXMLDomNodeList = NULL;
708  HRESULT hr = m_pxmldom->selectNodes(_bstr_t(control_name_xpath), &pXMLDomNodeList);
709  if (FAILED(hr) || pXMLDomNodeList == NULL)
710  {
711  std::cerr << "getParams failed" << std::endl;
712  return;
713  }
714  IXMLDOMNode *pNode, *pAttrNode1, *pAttrNode2, *pAttrNode3, *pAttrNode4, *pAttrNode5;
715  long n = 0;
716  int field;
717  unsigned access_mode;
718  char *last_str = NULL;
719  char *access_str, *str;
720  pXMLDomNodeList->get_length(&n);
721  for(long i=0; i<n; ++i)
722  {
723  pNode = NULL;
724  hr = pXMLDomNodeList->get_item(i, &pNode);
725  if (SUCCEEDED(hr) && pNode != NULL)
726  {
727  IXMLDOMNamedNodeMap *attributeMap = NULL;
728  pAttrNode1 = pAttrNode2 = pAttrNode3 = pAttrNode4 = pAttrNode5 = NULL;
729  pNode->get_attributes(&attributeMap);
730  hr = attributeMap->getNamedItem(_bstr_t("name"), &pAttrNode1);
731  hr = attributeMap->getNamedItem(_bstr_t("type"), &pAttrNode2);
732  hr = attributeMap->getNamedItem(_bstr_t("access"), &pAttrNode3);
733  hr = attributeMap->getNamedItem(_bstr_t("netvar"), &pAttrNode4);
734  hr = attributeMap->getNamedItem(_bstr_t("field"), &pAttrNode5);
735  BSTR bstrValue1 = NULL, bstrValue2 = NULL, bstrValue3 = NULL, bstrValue4 = NULL, bstrValue5 = NULL;
736  hr=pAttrNode1->get_text(&bstrValue1);
737  hr=pAttrNode2->get_text(&bstrValue2);
738  hr=pAttrNode3->get_text(&bstrValue3);
739  hr=pAttrNode4->get_text(&bstrValue4);
740  if (pAttrNode5 != NULL)
741  {
742  hr=pAttrNode5->get_text(&bstrValue5);
743  field = atoi(COLE2CT(bstrValue5));
744  }
745  else
746  {
747  field = -1;
748  }
749  access_str = strdup(COLE2CT(bstrValue3));
750  str = epicsStrtok_r(access_str, ",", &last_str);
751  while( str != NULL )
752  {
753  if (!strcmp(str, "R"))
754  {
755  access_mode |= NvItem::Read;
756  }
757  else if (!strcmp(str, "BR"))
758  {
759  access_mode |= NvItem::BufferedRead;
760  }
761  else if (!strcmp(str, "W"))
762  {
763  access_mode |= NvItem::Write;
764  }
765  else if (!strcmp(str, "BW"))
766  {
767  access_mode |= NvItem::BufferedWrite;
768  }
769  else
770  {
771  std::cerr << "getParams: Unknown access mode \"" << str << "\" for param " << COLE2CT(bstrValue1) << std::endl;
772  }
773  str = epicsStrtok_r(NULL, ",", &last_str);
774  }
775  free(access_str);
776  m_params[std::string(COLE2CT(bstrValue1))] = new NvItem(COLE2CT(bstrValue4),COLE2CT(bstrValue2),access_mode,field);
777  SysFreeString(bstrValue1);
778  SysFreeString(bstrValue2);
779  SysFreeString(bstrValue3);
780  SysFreeString(bstrValue4);
781  if (bstrValue5 != NULL)
782  {
783  SysFreeString(bstrValue5);
784  }
785  pAttrNode1->Release();
786  pAttrNode2->Release();
787  pAttrNode3->Release();
788  pAttrNode4->Release();
789  if (pAttrNode5 != NULL)
790  {
791  pAttrNode5->Release();
792  }
793  attributeMap->Release();
794  pNode->Release();
795  }
796  }
797  pXMLDomNodeList->Release();
798 }
799 
800 template <>
801 void NINetVarInterface::setValue(const char* param, const std::string& value)
802 {
803  ScopedCNVData cvalue;
804  int status = CNVCreateScalarDataValue(&cvalue, CNVString, value.c_str());
805  ERROR_CHECK("CNVCreateScalarDataValue", status);
806  setValueCNV(param, cvalue);
807 }
808 
809 template <typename T>
810 void NINetVarInterface::setValue(const char* param, const T& value)
811 {
812  ScopedCNVData cvalue;
813  int status = CNVCreateScalarDataValue(&cvalue, static_cast<CNVDataType>(C2CNV<T>::nvtype), value);
814  ERROR_CHECK("CNVCreateScalarDataValue", status);
815  setValueCNV(param, cvalue);
816 }
817 
818 template <typename T>
819 void NINetVarInterface::setArrayValue(const char* param, const T* value, size_t nElements)
820 {
821  ScopedCNVData cvalue;
822  size_t dimensions[1] = { nElements };
823  int status = CNVCreateArrayDataValue(&cvalue, static_cast<CNVDataType>(C2CNV<T>::nvtype), value, 1, dimensions);
824  ERROR_CHECK("CNVCreateArrayDataValue", status);
825  setValueCNV(param, cvalue);
826 }
827 
828 void NINetVarInterface::setValueCNV(const std::string& name, CNVData value)
829 {
830  int wait_ms = CNVWaitForever/*CNVDoNotWait*/, b_wait_ms = CNVDoNotWait/*CNVWaitForever*/;
831  NvItem* item = m_params[name];
832  int error = 0;
833  if (item->field != -1)
834  {
835  throw std::runtime_error("setValueCNV: unable to update struct variable via param \"" + name + "\"");
836  }
837  if (item->access & NvItem::Write)
838  {
839  error = CNVWrite(item->writer, value, wait_ms);
840  }
841  else if (item->access & NvItem::BufferedWrite)
842  {
843  error = CNVPutDataInBuffer(item->b_writer, value, b_wait_ms);
844  }
845  else
846  {
847  throw std::runtime_error("setValueCNV: param \"" + name + "\" does not define a writer for \"" + item->nv_name + "\"");
848  }
849  ERROR_CHECK("setValue", error);
850 }
851 
852 // update values from buffered subscribers
854 {
855  CNVBufferDataStatus dataStatus;
856  int status;
857  m_driver->lock();
858  for(params_t::const_iterator it=m_params.begin(); it != m_params.end(); ++it)
859  {
860  const NvItem* item = it->second;
861  if (item->access & NvItem::Read)
862  {
863  ; // we are a subscriber so automatically get updates on changes
864  }
865  else if (item->access & NvItem::BufferedRead)
866  {
867  ScopedCNVData value;
868  status = CNVGetDataFromBuffer(item->b_subscriber, &value, &dataStatus);
869  ERROR_CHECK("CNVGetDataFromBuffer", status);
870  if (dataStatus == CNVNewData || dataStatus == CNVDataWasLost)
871  {
872  updateParamCNV(item->id, value, false);
873  }
874  if (dataStatus == CNVDataWasLost)
875  {
876  std::cerr << "updateValues: data was lost for param \"" << it->first << "\" (" << item->nv_name << ")" << std::endl;
877  }
878  }
879  else
880  {
881  ; // we have not explicitly defined a reader
882  }
883  }
884  m_driver->callParamCallbacks();
885  m_driver->unlock();
886 }
887 
889 void NINetVarInterface::report(FILE* fp, int details)
890 {
891  fprintf(fp, "XML ConfigFile: \"%s\"\n", m_configFile.c_str());
892  fprintf(fp, "XML ConfigFile section: \"%s\"\n", m_configSection.c_str());
893  fprintf(fp, "NINetVarConfigure() Options: %d\n", m_options);
894  for(params_t::iterator it=m_params.begin(); it != m_params.end(); ++it)
895  {
896  NvItem* item = it->second;
897  item->report(it->first, fp);
898  }
899 }
900 
901 #ifndef DOXYGEN_SHOULD_SKIP_THIS
902 
903 template void NINetVarInterface::setValue(const char* param, const double& value);
904 template void NINetVarInterface::setValue(const char* param, const int& value);
905 
906 template void NINetVarInterface::setArrayValue(const char* param, const double* value, size_t nElements);
907 template void NINetVarInterface::setArrayValue(const char* param, const float* value, size_t nElements);
908 template void NINetVarInterface::setArrayValue(const char* param, const int* value, size_t nElements);
909 template void NINetVarInterface::setArrayValue(const char* param, const short* value, size_t nElements);
910 template void NINetVarInterface::setArrayValue(const char* param, const char* value, size_t nElements);
911 
912 template void NINetVarInterface::readArrayValue(const char* paramName, double* value, size_t nElements, size_t* nIn);
913 template void NINetVarInterface::readArrayValue(const char* paramName, float* value, size_t nElements, size_t* nIn);
914 template void NINetVarInterface::readArrayValue(const char* paramName, int* value, size_t nElements, size_t* nIn);
915 template void NINetVarInterface::readArrayValue(const char* paramName, short* value, size_t nElements, size_t* nIn);
916 template void NINetVarInterface::readArrayValue(const char* paramName, char* value, size_t nElements, size_t* nIn);
917 
918 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
static epicsThreadOnceId onceId
NINetVarInterface(const char *configSection, const char *configFile, int options)
section name of configFile to use to configure this asyn port
CNVBufferedWriter b_writer
static void free(ctype)
function to free any memory associated with type
NINetVarException(const std::string &function, int code)
details about a network shared variable we have connected to an asyn parameter
header for NINetVarInterface class.
int m_options
the various NINetVarOptions currently in use
void dataTransferredCallback(void *handle, int error, CallbackData *cb_data)
called when data has been transferred to the variable
static void CVICALLBACK StatusCallback(void *handle, CNVConnectionStatus status, int error, void *callbackData)
called when status of a network shared variable changes
int ctype
an instance of the underlying type
Definition: cnvconvert.h:22
STL namespace.
void updateParamValue(int param_index, T val, bool do_asyn_param_callbacks)
void setValue(const char *param, const T &value)
enum NvItem::@13 NvAccessMode
possible access modes to network shared variable
void updateParamArrayValue(int param_index, T *val, size_t nElements)
void updateParamCNVImpl(int param_index, CNVData data, CNVDataType type, unsigned int nDims, bool do_asyn_param_callbacks)
void setArrayValue(const char *param, const T *value, size_t nElements)
NINetVarException(const std::string &message)
void setValueCNV(const std::string &name, CNVData value)
NvItem(const char *nv_name_, const char *type_, unsigned access_, int field_)
CNVWriter writer
static void CVICALLBACK DataCallback(void *handle, CNVData data, void *callbackData)
called when new data is available on a subscriber connection
void statusCallback(void *handle, CNVConnectionStatus status, int error, CallbackData *cb_data)
called when status of a network shared variable changes
#define MAX_PATH_LEN
unsigned access
combination of NvAccessMode
ScopedCNVData(const CNVData &d)
static const char * driverName
Name of driver for use in message printing.
IXMLDOMDocument2 * m_pxmldom
void report(const std::string &name, FILE *fp)
helper for asyn driver report function
std::string nv_name
full path to network shared variable
Stores information to be passed back via a shared variable callback on a subscriber connection...
ScopedCNVData & operator=(const CNVData &d)
void dataCallback(void *handle, CNVData data, CallbackData *cb_data)
called when new data is available on a subscriber connection
NINetVarInterface * intf
static void initCV(void *)
CNVBufferedSubscriber b_subscriber
void createParams(asynPortDriver *driver)
Header file for network shared variable convertion routines.
std::vector< char > array_data
only used for array parameters, contains cached copy of data as this is not stored in usual asyn para...
void report(FILE *fp, const char *conn_type, void *handle, bool buffered)
static const char * connectionStatus(CNVConnectionStatus status)
connection status of a network shared variable
void report(FILE *fp, int details)
Helper for EPICS driver report function.
asynPortDriver * m_driver
int id
asyn parameter id, -1 if not assigned
#define ERROR_CHECK(__func, __code)
A CNVData item that automatically "disposes" itself.
CallbackData(NINetVarInterface *intf_, const std::string &nv_name_, int param_index_)
std::string nv_name
std::string type
type as specified in the XML file e.g. float64array
CNVSubscriber subscriber
static void CVICALLBACK DataTransferredCallback(void *handle, int error, void *callbackData)
called when data has been transferred to the variable
static std::string ni_message(const std::string &function, int code)
Manager class for the NetVar Interaction. Parses an netvarconfig.xml file and provides access to the ...
std::string m_configSection
section of configFile to load information from
void updateParamCNV(int param_index, CNVData data, bool do_asyn_param_callbacks)
void updateParamArrayValueImpl(int param_index, T *val, size_t nElements)
CNVData * operator&()
ScopedCNVData & operator=(const ScopedCNVData &d)
static void epicsExitFunc(void *arg)
int field
if we refer to a struct, this is the index of the field (starting at 0), otherwise it is -1 ...
static std::string dataQuality(CNVDataQuality quality)
the quality of the data in a network shared variable
void readArrayValue(const char *paramName, T *value, size_t nElements, size_t *nIn)
An STL exception object encapsulating a shared variable error message.
For a given C data type, provide the appropriate network shared variable type.
Definition: cnvconvert.h:133
std::string m_configFile
ScopedCNVData(const ScopedCNVData &d)
Copyright © 2013 Science and Technology Facilities Council | Generated by   doxygen 1.8.8