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