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 #define NOMINMAX
16 #include <windows.h>
17 #include <comutil.h>
18 #else
19 #include <math.h>
20 #include <unistd.h>
21 #endif /* _WIN32 */
22 
23 #include <string>
24 #include <vector>
25 #include <map>
26 #include <list>
27 #include <stdexcept>
28 #include <sstream>
29 #include <fstream>
30 #include <iostream>
31 #include <algorithm>
32 #include <cstring>
33 #include <limits>
34 
35 #include <cvirte.h>
36 #include <userint.h>
37 #include <cvinetv.h>
38 
39 #include <shareLib.h>
40 #include <macLib.h>
41 #include <epicsGuard.h>
42 #include <epicsString.h>
43 #include <errlog.h>
44 #include <cantProceed.h>
45 #include <epicsTime.h>
46 #include <alarm.h>
47 
48 #include "pugixml.hpp"
49 
50 #include "asynPortDriver.h"
51 
52 #include <epicsExport.h>
53 
54 #include "NetShrVarInterface.h"
55 #include "cnvconvert.h"
56 
57 #define MAX_PATH_LEN 256
58 
59 static const char *driverName="NetShrVarInterface";
60 
62 {
63  int64_t sec_from_epoch;
64  uint64_t frac;
65 };
66 
68 class NetShrVarException : public std::runtime_error
69 {
70 public:
71  explicit NetShrVarException(const std::string& message) : std::runtime_error(message) { }
72  explicit NetShrVarException(const std::string& function, int code) : std::runtime_error(ni_message(function, code)) { }
73  static std::string ni_message(const std::string& function, int code)
74  {
75  return function + ": " + CNVGetErrorDescription(code);
76  }
77 };
78 
79 #define ERROR_CHECK(__func, __code) \
80  if (__code < 0) \
81  { \
82  throw NetShrVarException(__func, __code); \
83  }
84 
85 #define ERROR_PRINT_CONTINUE(__func, __code) \
86  if (__code < 0) \
87  { \
88  std::cerr << NetShrVarException::ni_message(__func, __code); \
89  continue; \
90  }
91 
93 static const char* connectionStatus(CNVConnectionStatus status)
94 {
95  switch (status)
96  {
97  case CNVConnecting:
98  return "Connecting...";
99  break;
100  case CNVConnected:
101  return "Connected";
102  break;
103  case CNVDisconnected:
104  return "Disconnected";
105  break;
106  default:
107  return "UNKNOWN";
108  break;
109  }
110 }
111 
114 {
115  CNVData m_value;
116  public:
117  ScopedCNVData(const CNVData& d) : m_value(d) { }
120  CNVData* operator&() { return &m_value; }
121  operator CNVData*() { return &m_value; }
122  operator CNVData() { return m_value; }
123  ScopedCNVData& operator=(const ScopedCNVData& d) { m_value = d.m_value; return *this; }
124  ScopedCNVData& operator=(const CNVData& d) { m_value = d; return *this; }
125  bool operator==(CNVData d) const { return m_value == d; }
126  bool operator!=(CNVData d) const { return m_value != d; }
127  void dispose()
128  {
129  int status = 0;
130  if (m_value != 0)
131  {
132  status = CNVDisposeData(m_value);
133  m_value = 0;
134  }
135  ERROR_CHECK("CNVDisposeData", status);
136  }
138 };
139 
141 struct NvItem
142 {
143  enum { Read=0x1, Write=0x2, BufferedRead=0x4, BufferedWrite=0x8, SingleRead=0x10 } NvAccessMode;
144  std::string nv_name;
145  std::string type;
146  unsigned access;
147  int field;
148  int id;
149  std::string ts_param;
150  bool with_ts;
152  std::vector<char> array_data;
153  CNVSubscriber subscriber;
154  CNVBufferedSubscriber b_subscriber;
155  CNVWriter writer;
156  CNVReader reader;
157  CNVBufferedWriter b_writer;
158  epicsTimeStamp epicsTS;
159  NvItem(const std::string& nv_name_, const char* type_, unsigned access_, int field_, const std::string& ts_param_, bool with_ts_) : nv_name(nv_name_), type(type_), access(access_),
160  field(field_), ts_param(ts_param_), with_ts(with_ts_), id(-1), subscriber(0), b_subscriber(0), writer(0), b_writer(0), reader(0), connected_alarm(false)
161  {
162  memset(&epicsTS, 0, sizeof(epicsTS));
163  std::replace(nv_name.begin(), nv_name.end(), '/', '\\'); // we accept / as well as \ in the XML file for path to variable
164  }
166  void report(const std::string& name, FILE* fp)
167  {
168  fprintf(fp, "Report for asyn parameter \"%s\" type \"%s\" network variable \"%s\"\n", name.c_str(), type.c_str(), nv_name.c_str());
169  if (array_data.size() > 0)
170  {
171  fprintf(fp, " Current array size (bytes): %d\n", (int)array_data.size());
172  }
173  if (field != -1)
174  {
175  fprintf(fp, " Network variable structure index: %d\n", field);
176  }
177  char tbuffer[60];
178  if (epicsTimeToStrftime(tbuffer, sizeof(tbuffer), "%Y-%m-%d %H:%M:%S.%06f", &epicsTS) <= 0)
179  {
180  strcpy(tbuffer, "<unknown>");
181  }
182  fprintf(fp, " Update time: %s\n", tbuffer);
183  report(fp, "subscriber", subscriber, false);
184  report(fp, "buffered subscriber", b_subscriber, true);
185  report(fp, "writer", writer, false);
186  report(fp, "buffered writer", b_writer, true);
187  report(fp, "reader", reader, false);
188  }
189  void report(FILE* fp, const char* conn_type, void* handle, bool buffered)
190  {
191  int error, conn_error;
192  CNVConnectionStatus status;
193  if (handle == 0)
194  {
195  return;
196  }
197  fprintf(fp, " Connection type: %s", conn_type);
198  try
199  {
200  error = CNVGetConnectionAttribute(handle, CNVConnectionStatusAttribute, &status);
201  ERROR_CHECK("CNVGetConnectionAttribute", error);
202  fprintf(fp, " status: %s", connectionStatus(status));
203  error = CNVGetConnectionAttribute(handle, CNVConnectionErrorAttribute, &conn_error);
204  ERROR_CHECK("CNVGetConnectionAttribute", error);
205  if (conn_error < 0)
206  {
207  fprintf(fp, " error present: %s", CNVGetErrorDescription(conn_error));
208  }
209  if (buffered)
210  {
211  int nitems, maxitems;
212  error = CNVGetConnectionAttribute(handle, CNVClientBufferNumberOfItemsAttribute, &nitems);
213  ERROR_CHECK("CNVGetConnectionAttribute", error);
214  error = CNVGetConnectionAttribute(handle, CNVClientBufferMaximumItemsAttribute, &maxitems);
215  ERROR_CHECK("CNVGetConnectionAttribute", error);
216  fprintf(fp, " Client buffer: %d items (buffer size = %d)", nitems, maxitems);
217  }
218  fprintf(fp, "\n");
219  }
220  catch (const std::exception& ex)
221  {
222  fprintf(fp, " Unable to get connection status: %s\n", ex.what());
223  }
224  }
225 };
226 
229 {
231  std::string nv_name;
233  CallbackData(NetShrVarInterface* intf_, const std::string& nv_name_, int param_index_) : intf(intf_), nv_name(nv_name_), param_index(param_index_) { }
234 };
235 
236 static void CVICALLBACK DataCallback (void * handle, CNVData data, void * callbackData);
237 static void CVICALLBACK StatusCallback (void * handle, CNVConnectionStatus status, int error, void * callbackData);
238 static void CVICALLBACK DataTransferredCallback(void * handle, int error, void * callbackData);
239 
242 {
243  int waitTime = 3000; // in milliseconds, or CNVWaitForever
244  CNVReader reader;
245  try {
246  int error = CNVCreateReader(item->nv_name.c_str(), NULL, NULL, waitTime, 0, &reader);
247  ERROR_CHECK("CNVCreateReader", error);
248  ScopedCNVData cvalue;
249  int status = CNVRead(reader, 10, &cvalue);
250  ERROR_CHECK("CNVRead", status);
251  if (cvalue != 0)
252  {
253  updateParamCNV(item->id, cvalue, NULL, true);
254  }
255  CNVDispose(reader);
256  }
257  catch(const std::exception& ex)
258  {
259  std::cerr << "Unable to read initial value from \"" << item->nv_name << "\": " << ex.what() << std::endl;
260  setParamStatus(item->id, asynError);
261  }
262 }
263 
264 static const char* getBrowseType(CNVBrowseType browseType)
265 {
266  switch(browseType)
267  {
268  case CNVBrowseTypeUndefined:
269  return "The item's browse type is not defined.";
270  break;
271 
272  case CNVBrowseTypeMachine:
273  return "The item is a computer.";
274  break;
275 
276  case CNVBrowseTypeProcess:
277  return "This item is a process.";
278  break;
279 
280  case CNVBrowseTypeFolder:
281  return "The item is a folder.";
282  break;
283 
284  case CNVBrowseTypeItem:
285  return "The item is a variable.";
286  break;
287 
288  case CNVBrowseTypeItemRange:
289  return "The item is a range of variables. ";
290  break;
291 
292  case CNVBrowseTypeImplicitItem:
293  return "The item is an implict item.";
294  break;
295 
296  default:
297  return "unknown.";
298  break;
299  }
300 }
301 
302 // this looks to see if a path can be browsed
303 bool NetShrVarInterface::pathExists(const std::string& path)
304 {
305 #ifdef _WIN32
306  CNVBrowser browser = NULL;
307  char* item = NULL;
308  int leaf, error;
309  CNVBrowseType browseType = CNVBrowseTypeUndefined;
310  error = CNVCreateBrowser(&browser);
311  ERROR_CHECK("CNVCreateBrowser", error);
312  error = CNVBrowse(browser, path.c_str()); // error < 0 = not found
313  if (error < 0)
314  {
315  CNVDisposeBrowser(browser);
316  return false;
317  }
318  if (false)
319  {
320  error = CNVBrowseNextItem(browser, &item, &leaf, &browseType, NULL);
321  std::cerr << "error " << error << " leaf " << leaf << " type " << getBrowseType(browseType) << std::endl;
322  // if error > 0 then item != NULL and is the browsed next item
323  if (item != NULL)
324  {
325  std::cerr << item << std::endl;
326  CNVFreeMemory(item);
327  }
328  }
329  CNVDisposeBrowser(browser);
330  return true;
331 #else
332  return true;
333 #endif
334 }
335 
336 // this only works for localhost variables
337 bool NetShrVarInterface::varExists(const std::string& path)
338 {
339 #ifdef _WIN32
340  int error, exists = 0;
341  size_t proc_pos = path.find('\\', 2); // 2 for after \\ in \\localhost\proc\var
342  size_t var_pos = path.rfind('\\');
343 
344  if (proc_pos != std::string::npos && var_pos != std::string::npos)
345  {
346  std::string host_name = path.substr(2, proc_pos - 2);
347  std::string proc_name = path.substr(proc_pos + 1, var_pos - proc_pos - 1);
348  std::string var_name = path.substr(var_pos + 1);
349  if (host_name == "localhost")
350  {
351  error = CNVVariableExists(proc_name.c_str(), var_name.c_str(), &exists);
352  ERROR_CHECK("CNVVariableExists", error);
353  if (exists != 0)
354  {
355  return true;
356  }
357  else
358  {
359  return false;
360  }
361  }
362  else
363  {
364  return false;
365  }
366  }
367  else
368  {
369  std::cerr << "varExists: cannot parse \"" << path << "\"" << std::endl;
370  return false;
371  }
372 #else
373  return false;
374 #endif
375 }
376 
378 {
379  int error;
380  CallbackData* cb_data;
381  int waitTime = 3000; // in milliseconds, or CNVWaitForever
382  int clientBufferMaxItems = 200;
383 #ifdef _WIN32
384  int running = 0;
385  error = CNVVariableEngineIsRunning(&running);
386  ERROR_CHECK("CNVVariableEngineIsRunning", error);
387  if (running == 0)
388  {
389  std::cerr << "connectVars: NI Variable engine is not running" << std::endl;
390  }
391  char** processes = NULL;
392  int numberOfProcesses = 0;
393  int isRunning = 0;
394  error = CNVGetProcesses(&processes, &numberOfProcesses);
395  ERROR_CHECK("CNVGetProcesses", error);
396  std::cerr << "connectVars: NSV processes on machine:";
397  for(int i=0; i<numberOfProcesses; ++i)
398  {
399  error = CNVProcessIsRunning(processes[i], &isRunning);
400  ERROR_CHECK("CNVProcessIsRunning", error);
401  std::cerr << " \"" << processes[i] << "\" (" << (isRunning != 0 ? "RUNNING" : "NOT RUNNING") << ")";
402  }
403  std::cerr << std::endl;
404  CNVFreeMemory(processes);
405 #endif
406 
407  // look for alarm network variables
408  static const char* alarm_fields[] = { "Hi", "HiHi", "Lo", "LoLo" };
409  params_t new_params;
410  for(params_t::const_iterator it=m_params.begin(); it != m_params.end(); ++it)
411  {
412  NvItem* item = it->second;
413  std::string param_name = it->first;
414  if (pathExists(item->nv_name))
415  {
416  for(int i=0; i<sizeof(alarm_fields) / sizeof(const char*); ++i)
417  {
418  std::string prefix = item->nv_name + "\\Alarms\\" + alarm_fields[i] + "\\";
419  if (pathExists(prefix + "Enable"))
420  {
421  std::cerr << "Adding " << alarm_fields[i] << " alarm field for " << item->nv_name << " (asyn parameter: " << param_name << ")" << std::endl;
422  item->connected_alarm = true;
423  new_params[param_name + "_" + alarm_fields[i] + "_Enable"] = new NvItem(prefix + "Enable", "boolean", NvItem::Read|NvItem::Write, -1, "", false);
424  new_params[param_name + "_" + alarm_fields[i] + "_Set"] = new NvItem(prefix + "Set", "boolean", NvItem::Read, -1, "", false);
425  new_params[param_name + "_" + alarm_fields[i] + "_Ack"] = new NvItem(prefix + "Ack", "boolean", NvItem::Read, -1, "", false);
426  new_params[param_name + "_" + alarm_fields[i] + "_AckType"] = new NvItem(prefix + "AckType", "int32", NvItem::Read|NvItem::Write, -1, "", false);
427  new_params[param_name + "_" + alarm_fields[i] + "_level"] = new NvItem(prefix + "level", "float64", NvItem::Read|NvItem::Write, -1, "", false);
428  new_params[param_name + "_" + alarm_fields[i] + "_deadband"] = new NvItem(prefix + "deadband", "float64", NvItem::Read|NvItem::Write, -1, "", false);
429  }
430  }
431  }
432  }
433  m_params.insert(new_params.begin(), new_params.end());
434 
436 
437  // now connect vars
438  for(params_t::const_iterator it=m_params.begin(); it != m_params.end(); ++it)
439  {
440  NvItem* item = it->second;
441  cb_data = new CallbackData(this, item->nv_name, item->id);
442 
443  std::cerr << "connectVars: connecting to \"" << item->nv_name << "\"" << std::endl;
444 
445  // create either reader or buffered reader
446  if (item->access & NvItem::Read)
447  {
448  error = CNVCreateSubscriber(item->nv_name.c_str(), DataCallback, StatusCallback, cb_data, waitTime, 0, &(item->subscriber));
449  ERROR_PRINT_CONTINUE("CNVCreateSubscriber", error);
450  readVarInit(item);
451  }
452  else if (item->access & NvItem::BufferedRead)
453  {
454  error = CNVCreateBufferedSubscriber(item->nv_name.c_str(), StatusCallback, cb_data, clientBufferMaxItems, waitTime, 0, &(item->b_subscriber));
455  ERROR_PRINT_CONTINUE("CNVCreateBufferedSubscriber", error);
456  readVarInit(item);
457  }
458  else if (item->access & NvItem::SingleRead)
459  {
460  error = CNVCreateReader(item->nv_name.c_str(), StatusCallback, cb_data, waitTime, 0, &(item->reader));
461  ERROR_PRINT_CONTINUE("CNVCreateReader", error);
462  }
463  // create either writer or buffered writer
464  if (item->access & NvItem::Write)
465  {
466  error = CNVCreateWriter(item->nv_name.c_str(), StatusCallback, cb_data, waitTime, 0, &(item->writer));
467  ERROR_PRINT_CONTINUE("CNVCreateWriter", error);
468  }
469  else if (item->access & NvItem::BufferedWrite)
470  {
471  error = CNVCreateBufferedWriter(item->nv_name.c_str(), DataTransferredCallback, StatusCallback, cb_data, clientBufferMaxItems, waitTime, 0, &(item->b_writer));
472  ERROR_PRINT_CONTINUE("CNVCreateBufferedWriter", error);
473  }
474  }
475 }
476 
478 static std::string dataQuality(CNVDataQuality quality)
479 {
480  std::string res;
481  char* description = NULL;
482  int error = CNVGetDataQualityDescription(quality, ";", &description);
483  if (error == 0)
484  {
485  res = description;
486  CNVFreeMemory(description);
487  }
488  else
489  {
490  res = std::string("CNVGetDataQualityDescription: ") + CNVGetErrorDescription(error);
491  }
492  return res;
493 }
494 
496 static void CVICALLBACK DataTransferredCallback(void * handle, int error, void * callbackData)
497 {
498  CallbackData* cb_data = (CallbackData*)callbackData;
499  cb_data->intf->dataTransferredCallback(handle, error, cb_data);
500 }
501 
503 void NetShrVarInterface::dataTransferredCallback (void * handle, int error, CallbackData* cb_data)
504 {
505  if (error < 0)
506  {
507  std::cerr << "dataTransferredCallback: \"" << cb_data->nv_name << "\": " << CNVGetErrorDescription(error) << std::endl;
508  setParamStatus(cb_data->param_index, asynError);
509  }
510 // else
511 // {
512 // std::cerr << "dataTransferredCallback: " << cb_data->nv_name << " OK " << std::endl;
513 // }
514 }
515 
517 static void CVICALLBACK DataCallback (void * handle, CNVData data, void * callbackData)
518 {
519  try
520  {
521  CallbackData* cb_data = (CallbackData*)callbackData;
522  cb_data->intf->dataCallback(handle, data, cb_data);
523  CNVDisposeData (data);
524  }
525  catch(const std::exception& ex)
526  {
527  std::cerr << "DataCallback: ERROR : " << ex.what() << std::endl;
528  }
529  catch(...)
530  {
531  std::cerr << "DataCallback: ERROR" << std::endl;
532  }
533 }
534 
536 void NetShrVarInterface::dataCallback (void * handle, CNVData data, CallbackData* cb_data)
537 {
538 // std::cerr << "dataCallback: index " << cb_data->param_index << std::endl;
539  try
540  {
541  updateParamCNV(cb_data->param_index, data, NULL, true);
542  }
543  catch(const std::exception& ex)
544  {
545  std::cerr << "dataCallback: ERROR updating param index " << cb_data->param_index << ": " << ex.what() << std::endl;
546  }
547  catch(...)
548  {
549  std::cerr << "dataCallback: ERROR updating param index " << cb_data->param_index << std::endl;
550  }
551 }
552 
553 void NetShrVarInterface::updateConnectedAlarmStatus(const std::string& paramName, int value, const std::string& alarmStr, epicsAlarmCondition stat, epicsAlarmSeverity sevr)
554 {
555  asynStatus status;
556  int connected_param_index = -1;
557  const char *conectedParamName = NULL;
558  std::string suffix = "_" + alarmStr + "_Set";
559  if ( (paramName.size() > suffix.size()) && (paramName.substr(paramName.size() - suffix.size()) == suffix) )
560  {
561  std::string connectedParamName = paramName.substr(0, paramName.size() - suffix.size());
562  if (m_driver->findParam(connectedParamName.c_str(), &connected_param_index) == asynSuccess)
563  {
564  // check if param is in error, if so don't update alarm sttaus
565  if ( (m_driver->getParamStatus(connected_param_index, &status) == asynSuccess) && (status == asynSuccess) )
566  {
567  std::cerr << "Alarm type " << alarmStr << (value != 0 ? " raised" : " cleared") << " for asyn parameter " << connectedParamName << std::endl;
568  if (value != 0)
569  {
570  setParamStatus(connected_param_index, asynSuccess, stat, sevr);
571  }
572  else
573  {
574  setParamStatus(connected_param_index, asynSuccess);
575  }
576  }
577  }
578  }
579 }
580 
581 template<typename T>
582 void NetShrVarInterface::updateParamValue(int param_index, T val, epicsTimeStamp* epicsTS, bool do_asyn_param_callbacks)
583 {
584  const char *paramName = NULL;
585  m_driver->lock();
586  m_driver->getParamName(param_index, &paramName);
587  m_driver->setTimeStamp(epicsTS);
588  m_params[paramName]->epicsTS = *epicsTS;
589  if (m_params[paramName]->type == "float64" || m_params[paramName]->type == "ftimestamp")
590  {
591  m_driver->setDoubleParam(param_index, convertToScalar<double>(val));
592  }
593  else if (m_params[paramName]->type == "int32" || m_params[paramName]->type == "boolean")
594  {
595  int intVal = convertToScalar<int>(val);
596  m_driver->setIntegerParam(param_index, intVal);
597  updateConnectedAlarmStatus(paramName, intVal, "Hi", epicsAlarmHigh, epicsSevMinor);
598  updateConnectedAlarmStatus(paramName, intVal, "HiHi", epicsAlarmHiHi, epicsSevMajor);
599  updateConnectedAlarmStatus(paramName, intVal, "Lo", epicsAlarmLow, epicsSevMinor);
600  updateConnectedAlarmStatus(paramName, intVal, "LoLo", epicsAlarmLoLo, epicsSevMajor);
601  }
602  else if (m_params[paramName]->type == "string" || m_params[paramName]->type == "timestamp")
603  {
604  m_driver->setStringParam(param_index, convertToPtr<char>(val));
605  }
606  else
607  {
608  std::cerr << "updateParamValue: unknown type \"" << m_params[paramName]->type << "\" for param \"" << paramName << "\"" << std::endl;
609  }
610  if (do_asyn_param_callbacks)
611  {
612  m_driver->callParamCallbacks();
613  }
614  m_driver->unlock();
615 }
616 
617 template<typename T,typename U>
618 void NetShrVarInterface::updateParamArrayValueImpl(int param_index, T* val, size_t nElements)
619 {
620  const char *paramName = NULL;
621  m_driver->getParamName(param_index, &paramName);
622  std::vector<char>& array_data = m_params[paramName]->array_data;
623  U* eval = convertToPtr<U>(val);
624  if (eval != 0)
625  {
626  array_data.resize(nElements * sizeof(T));
627  memcpy(&(array_data[0]), eval, nElements * sizeof(T));
628  (m_driver->*C2CNV<U>::asyn_callback)(reinterpret_cast<U*>(&(array_data[0])), nElements, param_index, 0);
629  }
630  else
631  {
632  std::cerr << "updateParamArrayValue: cannot update param \"" << paramName << "\": shared variable data type incompatible \"" << C2CNV<T>::desc << "\"" << std::endl;
633  }
634 }
635 
636 // labview timestamp is seconds since 01-01-1904 00:00:00
637 // epics timestamp epoch is seconds since 01-01-1990 00:00:00
638 static void convertLabviewTimeToEpicsTime(const uint64_t* lv_time, epicsTimeStamp* epicsTS)
639 {
640  static const uint64_t epoch_diff = 2713996800u; // seconds from 01-01-1904 to 01-01-1990
641  static const uint64_t to_nsec = std::numeric_limits<uint64_t>::max() / 1000000000u;
642  epicsTS->secPastEpoch = lv_time[0] - epoch_diff;
643  epicsTS->nsec = lv_time[1] / to_nsec;
644 }
645 
646 template<typename T>
647 void NetShrVarInterface::updateParamArrayValue(int param_index, T* val, size_t nElements, epicsTimeStamp* epicsTS, bool do_asyn_param_callbacks)
648 {
649  const char *paramName = NULL;
650  epicsTimeStamp epicsTSv;
651  m_driver->getParamName(param_index, &paramName);
652  bool with_ts = m_params[paramName]->with_ts;
653  if (with_ts) // first 128bits of data are timestamp
654  {
655  size_t n_ts_elem = 16 / sizeof(T);
656  const uint64_t* time_data = reinterpret_cast<const uint64_t*>(val);
657  if (nElements > n_ts_elem)
658  {
659  convertLabviewTimeToEpicsTime(time_data, &epicsTSv);
660  val += n_ts_elem;
661  nElements -= n_ts_elem;
662  epicsTS = &epicsTSv;
663  }
664  else
665  {
666  std::cerr << "updateParamArrayValue: param \"" << paramName << "\" not enough elements for timestamp" << std::endl;
667  return;
668  }
669  }
670  m_driver->lock();
671  m_driver->setTimeStamp(epicsTS);
672  m_params[paramName]->epicsTS = *epicsTS;
673  if (m_params[paramName]->type == "float64array")
674  {
675  updateParamArrayValueImpl<T,epicsFloat64>(param_index, val, nElements);
676  }
677  else if (m_params[paramName]->type == "float32array")
678  {
679  updateParamArrayValueImpl<T,epicsFloat32>(param_index, val, nElements);
680  }
681  else if (m_params[paramName]->type == "int32array")
682  {
683  updateParamArrayValueImpl<T,epicsInt32>(param_index, val, nElements);
684  }
685  else if (m_params[paramName]->type == "int16array")
686  {
687  updateParamArrayValueImpl<T,epicsInt16>(param_index, val, nElements);
688  }
689  else if (m_params[paramName]->type == "int8array")
690  {
691  updateParamArrayValueImpl<T,epicsInt8>(param_index, val, nElements);
692  }
693  else if (m_params[paramName]->type == "timestamp" || m_params[paramName]->type == "ftimestamp") // this is an array of two uint64 elements
694  {
695  if ( nElements == 2 && sizeof(T) == sizeof(uint64_t) )
696  {
697  uint64_t* time_data = reinterpret_cast<uint64_t*>(val);
698  convertLabviewTimeToEpicsTime(time_data, epicsTS);
699  // we do not need to call m_driver->setTimeStamp(epicsTS) etc as this is done in updateParamValue
700  if (m_params[paramName]->type == "timestamp")
701  {
702  char time_buffer[40]; // max size of epics simple string type
703  epicsTimeToStrftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%H:%M:%S.%06f", epicsTS);
704  updateParamValue(param_index, time_buffer, epicsTS, do_asyn_param_callbacks);
705  }
706  else
707  {
708  double dval = epicsTS->secPastEpoch + epicsTS->nsec / 1e9;
709  updateParamValue(param_index, dval, epicsTS, do_asyn_param_callbacks);
710  }
711  }
712  else
713  {
714  std::cerr << "updateParamArrayValue: timestamp param \"" << paramName << "\" not given UInt64[2] array" << std::endl;
715  }
716  }
717  else
718  {
719  std::cerr << "updateParamArrayValue: unknown type \"" << m_params[paramName]->type << "\" for param \"" << paramName << "\"" << std::endl;
720  }
721  m_driver->unlock();
722 }
723 
725 template <typename T>
726 void NetShrVarInterface::readArrayValue(const char* paramName, T* value, size_t nElements, size_t* nIn)
727 {
728  NvItem* item = m_params[paramName];
729  if (item->access & NvItem::SingleRead)
730  {
731  ScopedCNVData cvalue;
732  if (item->reader != NULL)
733  {
734  m_driver->unlock(); // to allow DataCallback to work while we try and read
735  int status = CNVRead(item->reader, 10, &cvalue);
736  m_driver->lock();
737  ERROR_CHECK("CNVRead", status);
738  if (status > 0) // 0 means no new value, 1 means a new value since last read
739  {
740  updateParamCNV(item->id, cvalue, NULL, false);
741  }
742  }
743  else
744  {
745  std::cerr << "NetShrVarInterface::readArrayValue: Param \"" << paramName << "\" (" << item->nv_name << ") is not valid" << std::endl;
746  }
747  }
748  std::vector<char>& array_data = item->array_data;
749  size_t n = array_data.size() / sizeof(T);
750  if (n > nElements)
751  {
752  n = nElements;
753  }
754  *nIn = n;
755  memcpy(value, &(array_data[0]), n * sizeof(T));
756  m_driver->setTimeStamp(&(m_params[paramName]->epicsTS));
757 }
758 
760 void NetShrVarInterface::readValue(const char* param)
761 {
762  NvItem* item = m_params[param];
763  if (item->access & NvItem::SingleRead)
764  {
765  ScopedCNVData cvalue;
766  if (item->reader != NULL)
767  {
768  m_driver->unlock(); // to allow DataCallback to work while we try and read
769  int status = CNVRead(item->reader, 10, &cvalue);
770  m_driver->lock();
771  ERROR_CHECK("CNVRead", status);
772  if (cvalue != 0)
773  {
774  updateParamCNV(item->id, cvalue, NULL, true);
775  }
776  }
777  else
778  {
779  std::cerr << "NetShrVarInterface::readValue: Param \"" << param << "\" (" << item->nv_name << ") is not valid" << std::endl;
780  }
781  }
782 // m_driver->setTimeStamp(&(m_params[paramName]->epicsTS)); // don't think this is needed
783 }
784 
785 template<CNVDataType cnvType>
786 void NetShrVarInterface::updateParamCNVImpl(int param_index, CNVData data, CNVDataType type, unsigned int nDims,
787  epicsTimeStamp* epicsTS, bool do_asyn_param_callbacks)
788 {
789  static const int maxDims = 10;
790  if (nDims == 0)
791  {
792  typename CNV2C<cnvType>::ctype val;
793  int status = CNVGetScalarDataValue (data, type, &val);
794  ERROR_CHECK("CNVGetScalarDataValue", status);
795  updateParamValue(param_index, val, epicsTS, do_asyn_param_callbacks);
798  }
799  else if (nDims <= maxDims)
800  {
801  typename CNV2C<cnvType>::ctype* val = NULL;
802  size_t dimensions[maxDims];
803  int status = CNVGetArrayDataDimensions(data, nDims, dimensions);
804  ERROR_CHECK("CNVGetArrayDataDimensions", status);
805  size_t nElements = 1;
806  for(unsigned i=0; i<nDims; ++i)
807  {
808  nElements *= dimensions[i];
809  }
810  if (nElements > 0)
811  {
812  val = new typename CNV2C<cnvType>::ctype[nElements];
813  if (val != NULL)
814  {
815  status = CNVGetArrayDataValue(data, type, val, nElements);
816  ERROR_CHECK("CNVGetArrayDataValue", status);
817  updateParamArrayValue(param_index, val, nElements, epicsTS, do_asyn_param_callbacks);
818  delete[] val;
819  updateBytesReadCount(nElements * sizeof(CNV2C<cnvType>::ctype));
820  }
821  }
822  }
823 }
824 
827 bool NetShrVarInterface::convertTimeStamp(unsigned __int64 timestamp, epicsTimeStamp *epicsTS)
828 {
829  int year, month, day, hour, minute;
830  double second;
831  int status = CNVGetTimestampInfo(timestamp, &year, &month, &day, &hour, &minute, &second);
832  if (status < 0)
833  {
834 // std::cerr << "convertTimestamp " << status << ": " << CNVGetErrorDescription(status) << std::endl;
835  return false;
836  }
837  struct tm tms;
838  memset(&tms, 0, sizeof(tms));
839  tms.tm_year = year - 1900;
840  tms.tm_mon = month - 1;
841  tms.tm_mday = day;
842  tms.tm_hour = hour;
843  tms.tm_min = minute;
844  tms.tm_sec = static_cast<int>(floor(second));
845  unsigned long nanosec = static_cast<unsigned long>(floor((second - floor(second)) * 1.e9 + 0.5));
846  epicsTimeFromGMTM(epicsTS, &tms, nanosec);
847 // debugging check
848 // char buffer[60];
849 // epicsTimeToStrftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S.%06f", epicsTS);
850  return true;
851 }
852 
853 void NetShrVarInterface::updateParamCNV (int param_index, CNVData data, epicsTimeStamp* epicsTS, bool do_asyn_param_callbacks)
854 {
855  unsigned int nDims = 0;
856  unsigned int serverError;
857  CNVDataType type;
858  CNVDataQuality quality;
859  int good, status;
860  unsigned short numberOfFields = 0;
861  const char *paramName = NULL;
862  unsigned __int64 timestamp;
863  epicsTimeStamp epicsTSLocal;
864  m_driver->getParamName(param_index, &paramName);
865  if (paramName == NULL)
866  {
867  return;
868  }
869  std::string paramNameStr = paramName;
870  if (data == 0)
871  {
872 // std::cerr << "updateParamCNV: no data for param " << paramName << std::endl;
873  return;
874  }
875  status = CNVGetDataType (data, &type, &nDims);
876  ERROR_CHECK("CNVGetDataType", status);
877  // the update time for an item in a shared variable structure/cluster is the upadate time of the structure variable
878  // so we need to propagate the structure time when we recurse into its fields
879  const std::string& ts_param = m_params[paramName]->ts_param;
880  if (ts_param.size() > 0)
881  {
882  epicsTS = &(m_params[ts_param]->epicsTS);
883  }
884  if (epicsTS == NULL)
885  {
886  status = CNVGetDataUTCTimestamp(data, &timestamp);
887  ERROR_CHECK("CNVGetDataUTCTimestamp", status);
888  if (!convertTimeStamp(timestamp, &epicsTSLocal))
889  {
890  epicsTimeGetCurrent(&epicsTSLocal);
891  }
892  epicsTS = &epicsTSLocal;
893  }
894  if (type == CNVStruct)
895  {
896  int field = m_params[paramName]->field;
897  status = CNVGetNumberOfStructFields(data, &numberOfFields);
898  ERROR_CHECK("CNVGetNumberOfStructFields", status);
899  if (numberOfFields == 0)
900  {
901  throw std::runtime_error("number of fields");
902  }
903  if (field < 0 || field >= numberOfFields)
904  {
905  throw std::runtime_error("field index");
906  }
907  CNVData* fields = new CNVData[numberOfFields];
908  status = CNVGetStructFields(data, fields, numberOfFields);
909  ERROR_CHECK("CNVGetStructFields", status);
910  // loop round all params interested in this structure
911  // i.e. not just param_index and field
912  const std::string& this_nv = m_params[paramName]->nv_name;
913  // we so timestamp fields first so if we are linked to them
914  // via ts_param then we get the correct time value applied later
915  std::vector<const NvItem*> items_left;
916  items_left.reserve(numberOfFields);
917  for (params_t::iterator it = m_params.begin(); it != m_params.end(); ++it)
918  {
919  NvItem* item = it->second;
920  if (item->field != -1 && item->nv_name == this_nv)
921  {
922  if (item->type == "timestamp" || item->type == "ftimestamp")
923  {
924  updateParamCNV(item->id, fields[item->field], NULL, do_asyn_param_callbacks);
925  }
926  else
927  {
928  items_left.push_back(item);
929  }
930  }
931  }
932  for (std::vector<const NvItem*>::const_iterator it = items_left.begin(); it != items_left.end(); ++it)
933  {
934  const NvItem* item = *it;
935  updateParamCNV(item->id, fields[item->field], epicsTS, do_asyn_param_callbacks);
936  }
937  for(int i=0; i<numberOfFields; ++i)
938  {
939  status = CNVDisposeData(fields[i]);
940  ERROR_CHECK("CNVDisposeData", status);
941  }
942  delete[] fields;
943  return;
944  }
945  status = CNVGetDataQuality(data, &quality);
946  ERROR_CHECK("CNVGetDataQuality", status);
947  status = CNVCheckDataQuality(quality, &good);
948  ERROR_CHECK("CNVCheckDataQuality", status);
949  asynStatus p_stat;
950  int p_alarmStat, p_alarmSevr;
951  getParamStatus(param_index, p_stat, p_alarmStat, p_alarmSevr);
952  if (good == 1 && p_stat != asynSuccess)
953  {
954  std::cerr << "updateParamCNV: data for param " << paramName << " is good quality again" << std::endl;
955  setParamStatus(param_index, asynSuccess);
956  }
957  // no else here as we don't want to check quality for alarms if good == 0 but do if good == 1
958  if (good == 0)
959  {
960  std::cerr << "updateParamCNV: data for param " << paramName << " is not good quality: " << dataQuality(quality) << std::endl;
961  setParamStatus(param_index, asynError);
962  }
963  else if ( quality & (CNVDataQualityLowLimited | CNVDataQualityHighLimited) )
964  {
965  std::cerr << "NV has signaled CNVDataQualityLowLimited / CNVDataQualityHighLimited for " << paramName << std::endl;
966  if (p_stat == asynSuccess && p_alarmStat == epicsAlarmNone && p_alarmSevr == epicsSevNone)
967  {
968  setParamStatus(param_index, asynSuccess, epicsAlarmHwLimit, epicsSevMinor);
969  }
970  }
971  else if ( quality & CNVDataQualityInAlarm )
972  {
973  // we should get the EPICS alarm set via our connected alarms
974  // we did try alarming here if not otherwise in alarm, but the connected alarms do not repeat
975  // so you can get race conditions and conflict especially if you gaev buffered readers for one
976  // and readers for the other
977  if (!(m_params[paramName]->connected_alarm))
978  {
979  if (p_stat == asynSuccess && p_alarmStat == epicsAlarmNone && p_alarmSevr == epicsSevNone)
980  {
981  std::cerr << "Unexpected Alarm for " << m_params[paramName]->nv_name << " - Alarming enabled after IOC started?" << std::endl;
982  std::cerr << "Raising generic HWLIMIT/MINOR Alarm for \"" << paramName << "\"" << std::endl;
983  std::cerr << "(For more specific HI/LOW etc alarms start this IOC after enabling Alarming)" << std::endl;
984  setParamStatus(param_index, asynSuccess, epicsAlarmHwLimit, epicsSevMinor);
985  }
986  }
987  }
988  else
989  {
990  // we only clear a hwLimit alarm here, others some as connected alarms
991  if (p_stat == asynSuccess && p_alarmStat == epicsAlarmHwLimit)
992  {
993  std::cerr << "Clearing HWLIMIT Alarm for \"" << paramName << "\"" << std::endl;
994  setParamStatus(param_index, asynSuccess);
995  }
996  }
997  switch(type)
998  {
999  case CNVEmpty:
1000  break;
1001 
1002  case CNVBool:
1003  updateParamCNVImpl<CNVBool>(param_index, data, type, nDims, epicsTS, do_asyn_param_callbacks);
1004  break;
1005 
1006  case CNVString:
1007  updateParamCNVImpl<CNVString>(param_index, data, type, nDims, epicsTS, do_asyn_param_callbacks);
1008  break;
1009 
1010  case CNVSingle:
1011  updateParamCNVImpl<CNVSingle>(param_index, data, type, nDims, epicsTS, do_asyn_param_callbacks);
1012  break;
1013 
1014  case CNVDouble:
1015  updateParamCNVImpl<CNVDouble>(param_index, data, type, nDims, epicsTS, do_asyn_param_callbacks);
1016  break;
1017 
1018  case CNVInt8:
1019  updateParamCNVImpl<CNVInt8>(param_index, data, type, nDims, epicsTS, do_asyn_param_callbacks);
1020  break;
1021 
1022  case CNVUInt8:
1023  updateParamCNVImpl<CNVUInt8>(param_index, data, type, nDims, epicsTS, do_asyn_param_callbacks);
1024  break;
1025 
1026  case CNVInt16:
1027  updateParamCNVImpl<CNVInt16>(param_index, data, type, nDims, epicsTS, do_asyn_param_callbacks);
1028  break;
1029 
1030  case CNVUInt16:
1031  updateParamCNVImpl<CNVUInt16>(param_index, data, type, nDims, epicsTS, do_asyn_param_callbacks);
1032  break;
1033 
1034  case CNVInt32:
1035  updateParamCNVImpl<CNVInt32>(param_index, data, type, nDims, epicsTS, do_asyn_param_callbacks);
1036  break;
1037 
1038  case CNVUInt32:
1039  updateParamCNVImpl<CNVUInt32>(param_index, data, type, nDims, epicsTS, do_asyn_param_callbacks);
1040  break;
1041 
1042  case CNVInt64:
1043  updateParamCNVImpl<CNVInt64>(param_index, data, type, nDims, epicsTS, do_asyn_param_callbacks);
1044  break;
1045 
1046  case CNVUInt64:
1047  updateParamCNVImpl<CNVUInt64>(param_index, data, type, nDims, epicsTS, do_asyn_param_callbacks);
1048  break;
1049 
1050  default:
1051  std::cerr << "updateParamCNV: unknown type " << type << " for param " << paramName << std::endl;
1052  break;
1053  }
1054  status = CNVGetDataServerError(data, &serverError);
1055  if (status == 0 && serverError != 0)
1056  {
1057  std::cerr << "updateParamCNV: Server error: " << serverError << std::endl;
1058  }
1059  else if (status < 0)
1060  {
1061  std::cerr << "updateParamCNV: CNVGetDataServerError: " << CNVGetErrorDescription(status) << std::endl;
1062  }
1063 }
1064 
1065 
1067 static void CVICALLBACK StatusCallback (void * handle, CNVConnectionStatus status, int error, void * callbackData)
1068 {
1069  CallbackData* cb_data = (CallbackData*)callbackData;
1070  cb_data->intf->statusCallback(handle, status, error, cb_data);
1071 }
1072 
1074 void NetShrVarInterface::statusCallback (void * handle, CNVConnectionStatus status, int error, CallbackData* cb_data)
1075 {
1076  if (error < 0)
1077  {
1078  std::cerr << "StatusCallback: " << cb_data->nv_name << ": " << CNVGetErrorDescription(error) << std::endl;
1079  setParamStatus(cb_data->param_index, asynError);
1080  }
1081  else
1082  {
1083  std::cerr << "StatusCallback: " << cb_data->nv_name << " is " << connectionStatus(status) << std::endl;
1084  if (status != CNVConnected)
1085  {
1086  setParamStatus(cb_data->param_index, asynDisconnected);
1087  }
1088  }
1089 }
1090 
1091 static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
1092 
1093 static void initCV(void*)
1094 {
1095 #ifdef _WIN32
1096  char* dummy_argv[2] = { strdup("NetShrVarInterface"), NULL };
1097  if (InitCVIRTE (0, dummy_argv, 0) == 0)
1098  throw std::runtime_error("InitCVIRTE");
1099 #endif
1100 }
1101 
1104 char* NetShrVarInterface::envExpand(const char *str)
1105 {
1106  long destCapacity = 128;
1107  char *dest = NULL;
1108  int n;
1109  do {
1110  destCapacity *= 2;
1111  /*
1112  * Use free/malloc rather than realloc since there's no need to
1113  * keep the original contents.
1114  */
1115  free(dest);
1116  dest = static_cast<char*>(mallocMustSucceed(destCapacity, "NetShrVarInterface::envExpand"));
1117  n = macExpandString(m_mac_env, str, dest, destCapacity);
1118  } while (n >= (destCapacity - 1));
1119  if (n < 0) {
1120  free(dest);
1121  dest = NULL;
1122  } else {
1123  size_t unused = destCapacity - ++n;
1124 
1125  if (unused >= 20)
1126  dest = static_cast<char*>(realloc(dest, n));
1127  }
1128  return dest;
1129 }
1130 
1134 NetShrVarInterface::NetShrVarInterface(const char *configSection, const char* configFile, int options) :
1135  m_configSection(configSection), m_options(options), m_mac_env(NULL),
1136  m_writer_wait_ms(5000/*also CNVWaitForever or CNVDoNotWait*/),
1137  m_b_writer_wait_ms(CNVDoNotWait/*also CNVWaitForever or CNVDoNotWait*/),
1138  m_items_read(0), m_bytes_read(0)
1139 {
1140  ftime(&m_last_report);
1141  epicsThreadOnce(&onceId, initCV, NULL);
1142  // load current environment into m_mac_env, this is so we can create a macEnvExpand() equivalent
1143  // but tied to the environment at a specific time. It is useful if we want to load the same
1144  // XML file twice but with a macro defined differently in each case
1145  if (macCreateHandle(&m_mac_env, NULL) != 0)
1146  {
1147  throw std::runtime_error("Cannot create mac handle");
1148  }
1149  for(char** cp = environ; *cp != NULL; ++cp)
1150  {
1151  char* str_tmp = strdup(*cp);
1152  char* equals_loc = strchr(str_tmp, '='); // split name=value string
1153  if (equals_loc != NULL)
1154  {
1155  *equals_loc = '\0';
1156  macPutValue(m_mac_env, str_tmp, equals_loc + 1);
1157  }
1158  free(str_tmp);
1159  }
1160  char* configFile_expanded = envExpand(configFile);
1161  m_configFile = configFile_expanded;
1162  epicsAtExit(epicsExitFunc, this);
1163 
1164  pugi::xml_parse_result result = m_xmlconfig.load_file(configFile_expanded);
1165  free(configFile_expanded);
1166  if (result)
1167  {
1168  std::cerr << "Loaded XML config file \"" << m_configFile << "\" (expanded from \"" << configFile << "\")" << std::endl;
1169  }
1170  else
1171  {
1172  throw std::runtime_error("Cannot load XML \"" + m_configFile + "\" (expanded from \"" + std::string(configFile) + "\"): load failure: "
1173  + result.description());
1174  }
1175 }
1176 
1177 // need to be careful here as might get called at wrong point. May need to check with driver.
1179 {
1180 // NetShrVarInterface* netvarint = static_cast<NetShrVarInterface*>(arg);
1181 // if (netvarint == NULL)
1182 // {
1183 // return;
1184 // }
1185 // if ( netvarint->checkOption(Something) )
1186 // {
1187 // }
1188  CNVFinish();
1189 }
1190 
1192 {
1193  char control_name_xpath[MAX_PATH_LEN];
1194  epicsSnprintf(control_name_xpath, sizeof(control_name_xpath), "/netvar/section[@name='%s']/param", m_configSection.c_str());
1195  try
1196  {
1197  pugi::xpath_node_set params = m_xmlconfig.select_nodes(control_name_xpath);
1198  return params.size();
1199  }
1200  catch(const std::exception& ex)
1201  {
1202  std::cerr << "nparams failed " << ex.what() << std::endl;
1203  return 0;
1204  }
1205 }
1206 
1208 {
1209  static const char* functionName = "initAsynParamIds";
1210  for(params_t::iterator it=m_params.begin(); it != m_params.end(); ++it)
1211  {
1212  NvItem* item = it->second;
1213  if (item->id != -1)
1214  {
1215  continue; // already initialised
1216  }
1217  if (item->type == "float64" || item->type == "ftimestamp")
1218  {
1219  m_driver->createParam(it->first.c_str(), asynParamFloat64, &(item->id));
1220  }
1221  else if (item->type == "int32" || item->type == "boolean")
1222  {
1223  m_driver->createParam(it->first.c_str(), asynParamInt32, &(item->id));
1224  }
1225  else if (item->type == "string" || item->type == "timestamp")
1226  {
1227  m_driver->createParam(it->first.c_str(), asynParamOctet, &(item->id));
1228  }
1229  else if (item->type == "float64array")
1230  {
1231  m_driver->createParam(it->first.c_str(), asynParamFloat64Array, &(item->id));
1232  }
1233  else if (item->type == "float32array")
1234  {
1235  m_driver->createParam(it->first.c_str(), asynParamFloat32Array, &(item->id));
1236  }
1237  else if (item->type == "int32array")
1238  {
1239  m_driver->createParam(it->first.c_str(), asynParamInt32Array, &(item->id));
1240  }
1241  else if (item->type == "int16array")
1242  {
1243  m_driver->createParam(it->first.c_str(), asynParamInt16Array, &(item->id));
1244  }
1245  else if (item->type == "int8array")
1246  {
1247  m_driver->createParam(it->first.c_str(), asynParamInt8Array, &(item->id));
1248  }
1249  else
1250  {
1251  errlogSevPrintf(errlogMajor, "%s:%s: unknown type %s for parameter %s\n", driverName,
1252  functionName, item->type.c_str(), it->first.c_str());
1253  }
1254  }
1255 }
1256 
1257 void NetShrVarInterface::createParams(asynPortDriver* driver)
1258 {
1259  static const char* functionName = "createParams";
1260  m_driver = driver;
1261  getParams();
1262  connectVars();
1263 }
1264 
1266 {
1267  m_params.clear();
1268  char control_name_xpath[MAX_PATH_LEN];
1269  epicsSnprintf(control_name_xpath, sizeof(control_name_xpath), "/netvar/section[@name='%s']/param", m_configSection.c_str());
1270  pugi::xpath_node_set params;
1271  try
1272  {
1273  params = m_xmlconfig.select_nodes(control_name_xpath);
1274  if (params.size() == 0)
1275  {
1276  std::cerr << "getParams failed" << std::endl;
1277  return;
1278  }
1279  }
1280  catch(const std::exception& ex)
1281  {
1282  std::cerr << "getParams failed " << ex.what() << std::endl;
1283  return;
1284  }
1285  int field;
1286  unsigned access_mode;
1287  char *last_str = NULL;
1288  char *access_str, *str;
1289  for (pugi::xpath_node_set::const_iterator it = params.begin(); it != params.end(); ++it)
1290  {
1291  pugi::xpath_node node = *it;
1292  std::string attr1 = node.node().attribute("name").value();
1293  std::string attr2 = node.node().attribute("type").value();
1294  std::string attr3 = node.node().attribute("access").value();
1295  std::string attr4 = envExpand(node.node().attribute("netvar").value());
1296  std::string attr5 = node.node().attribute("field").value();
1297  std::string attr6 = node.node().attribute("ts_param").value();
1298  std::string with_ts_s = node.node().attribute("with_ts").value();
1299  bool with_ts = false;
1300  if (with_ts_s == "true")
1301  {
1302  with_ts = true;
1303  }
1304  if (attr5.size() == 0)
1305  {
1306  field = -1;
1307  }
1308  else
1309  {
1310  field = atoi(attr5.c_str());
1311  }
1312  access_str = strdup(attr3.c_str());
1313  access_mode = 0;
1314  str = epicsStrtok_r(access_str, ",", &last_str);
1315  while( str != NULL )
1316  {
1317  if (!strcmp(str, "R"))
1318  {
1319  access_mode |= NvItem::Read;
1320  }
1321  else if (!strcmp(str, "BR"))
1322  {
1323  access_mode |= NvItem::BufferedRead;
1324  }
1325  else if (!strcmp(str, "SR"))
1326  {
1327  access_mode |= NvItem::SingleRead;
1328  }
1329  else if (!strcmp(str, "W"))
1330  {
1331  access_mode |= NvItem::Write;
1332  }
1333  else if (!strcmp(str, "BW"))
1334  {
1335  access_mode |= NvItem::BufferedWrite;
1336  }
1337  else
1338  {
1339  std::cerr << "getParams: Unknown access mode \"" << str << "\" for param " << attr1 << std::endl;
1340  }
1341  str = epicsStrtok_r(NULL, ",", &last_str);
1342  }
1343  free(access_str);
1344  if (attr6.size() > 0 && m_params.find(attr6) == m_params.end())
1345  {
1346  std::cerr << "getParams: Unable to link unknown \"" << attr6 << "\" as ts_param for " << attr1 << std::endl;
1347  attr6 = "";
1348  }
1349  m_params[attr1] = new NvItem(attr4.c_str(),attr2.c_str(),access_mode,field,attr6,with_ts);
1350  }
1351 }
1352 
1353 template <>
1354 void NetShrVarInterface::setValue(const char* param, const std::string& value)
1355 {
1356  ScopedCNVData cvalue;
1357  int status = CNVCreateScalarDataValue(&cvalue, CNVString, value.c_str());
1358  ERROR_CHECK("CNVCreateScalarDataValue", status);
1359  setValueCNV(param, cvalue);
1360 }
1361 
1362 template <typename T>
1363 void NetShrVarInterface::setValue(const char* param, const T& value)
1364 {
1365  ScopedCNVData cvalue;
1366  int status = CNVCreateScalarDataValue(&cvalue, static_cast<CNVDataType>(C2CNV<T>::nvtype), value);
1367  ERROR_CHECK("CNVCreateScalarDataValue", status);
1368  setValueCNV(param, cvalue);
1369 }
1370 
1371 template <typename T>
1372 void NetShrVarInterface::setArrayValue(const char* param, const T* value, size_t nElements)
1373 {
1374  ScopedCNVData cvalue;
1375  size_t dimensions[1] = { nElements };
1376  int status = CNVCreateArrayDataValue(&cvalue, static_cast<CNVDataType>(C2CNV<T>::nvtype), value, 1, dimensions);
1377  ERROR_CHECK("CNVCreateArrayDataValue", status);
1378  setValueCNV(param, cvalue);
1379 }
1380 
1381 void NetShrVarInterface::setValueCNV(const std::string& name, CNVData value)
1382 {
1383  NvItem* item = m_params[name];
1384  int error = 0;
1385  ScopedCNVData cvalue;
1386  if (item->field != -1)
1387  {
1388  if (item->reader == NULL) {
1389  int waitTime = 3000; // in milliseconds, or CNVWaitForever
1390  error = CNVCreateReader(item->nv_name.c_str(), NULL, NULL, waitTime, 0, &(item->reader));
1391  ERROR_CHECK("CNVCreateReader", error);
1392  }
1393  m_driver->unlock(); // to allow DataCallback to work while we try to read
1394  error = CNVRead(item->reader, 10, &cvalue);
1395  m_driver->lock();
1396  ERROR_CHECK("CNVRead", error);
1397  if (cvalue != 0)
1398  {
1399  int field = item->field;
1400  unsigned short numberOfFields = 0;
1401  error = CNVGetNumberOfStructFields(cvalue, &numberOfFields);
1402  ERROR_CHECK("CNVGetNumberOfStructFields", error);
1403  if (numberOfFields == 0)
1404  {
1405  throw std::runtime_error("number of fields");
1406  }
1407  if (field < 0 || field >= numberOfFields)
1408  {
1409  throw std::runtime_error("field index");
1410  }
1411  CNVData* fields = new CNVData[numberOfFields];
1412  error = CNVGetStructFields(cvalue, fields, numberOfFields);
1413  ERROR_CHECK("CNVGetStructFields", error);
1414  error = CNVDisposeData(fields[field]);
1415  ERROR_CHECK("CNVDisposeData", error);
1416  fields[field] = value;
1417  error = CNVSetStructDataValue(cvalue, fields, numberOfFields);
1418  ERROR_CHECK("CNVSetStructDataValue", error);
1419  for(int i=0; i<numberOfFields; ++i) {
1420  if (i != field) { // value will be freed by caller as scoped data
1421  error = CNVDisposeData(fields[i]);
1422  ERROR_CHECK("CNVDisposeData", error);
1423  }
1424  }
1425  delete[] fields;
1426  value = cvalue;
1427  }
1428  else
1429  {
1430  throw std::runtime_error("setValueCNV: param \"" + name + "\" cannot read cluster for \"" + item->nv_name + "\"");
1431  return;
1432  }
1433  }
1434  if (item->access & NvItem::Write)
1435  {
1436  m_driver->unlock(); // to allow DataCallback to work while we try and write
1437  error = CNVWrite(item->writer, value, m_writer_wait_ms);
1438  m_driver->lock();
1439  }
1440  else if (item->access & NvItem::BufferedWrite)
1441  {
1442  m_driver->unlock(); // to allow DataCallback to work while we try and write
1443  error = CNVPutDataInBuffer(item->b_writer, value, m_b_writer_wait_ms);
1444  m_driver->lock();
1445  }
1446  else
1447  {
1448  throw std::runtime_error("setValueCNV: param \"" + name + "\" does not define a writer for \"" + item->nv_name + "\"");
1449  }
1450  ERROR_CHECK("setValue", error);
1451 }
1452 
1453 void NetShrVarInterface::setParamStatus(int param_id, asynStatus status, epicsAlarmCondition alarmStat, epicsAlarmSeverity alarmSevr)
1454 {
1455  m_driver->lock();
1456  m_driver->setParamStatus(param_id, status);
1457  m_driver->setParamAlarmStatus(param_id, alarmStat);
1458  m_driver->setParamAlarmSeverity(param_id, alarmSevr);
1459  m_driver->unlock();
1460 }
1461 
1462 void NetShrVarInterface::getParamStatus(int param_id, asynStatus& status, int& alarmStat, int& alarmSevr)
1463 {
1464  m_driver->lock();
1465  m_driver->getParamStatus(param_id, &status);
1466  m_driver->getParamAlarmStatus(param_id, &alarmStat);
1467  m_driver->getParamAlarmSeverity(param_id, &alarmSevr);
1468  m_driver->unlock();
1469 }
1470 
1474 {
1475  CNVBufferDataStatus dataStatus;
1476  int status;
1477  for(params_t::const_iterator it=m_params.begin(); it != m_params.end(); ++it)
1478  {
1479  const NvItem* item = it->second;
1480  if (item->access & NvItem::Read)
1481  {
1482  ; // we are a subscriber so automatically get updates on changes
1483  }
1484  else if (item->access & NvItem::BufferedRead)
1485  {
1486  ScopedCNVData value;
1487  if (item->b_subscriber != NULL)
1488  {
1489  status = CNVGetDataFromBuffer(item->b_subscriber, &value, &dataStatus);
1490  if (status < 0)
1491  {
1492  std::cerr << NetShrVarException::ni_message("CNVGetDataFromBuffer", status);
1493  setParamStatus(item->id, asynError);
1494  }
1495  if (dataStatus == CNVDataWasLost)
1496  {
1497  std::cerr << "NetShrVarInterface::updateValues: BufferedReader: data was lost for param \"" << it->first << "\" (" << item->nv_name << ") - is poll frequency too low?" << std::endl;
1498  // set an alarm status?
1499  }
1500  if (dataStatus == CNVNewData || dataStatus == CNVDataWasLost) // returns CNVStaleData if value unchanged frm last read
1501  {
1502  updateParamCNV(item->id, value, NULL, true);
1503  }
1504  }
1505  else
1506  {
1507  std::cerr << "NetShrVarInterface::updateValues: BufferedReader: param \"" << it->first << "\" (" << item->nv_name << ") is not valid" << std::endl;
1508  }
1509  }
1510  else
1511  {
1512  ; // we have not explicitly defined a reader
1513  }
1514  }
1515 // we used to pass false to updateParamCNV and do callParamCallbacks here
1516 // m_driver->lock();
1517 // m_driver->callParamCallbacks();
1518 // m_driver->unlock();
1519 }
1520 
1522 void NetShrVarInterface::report(FILE* fp, int details)
1523 {
1524  static uint32_t last_items_read = 0;
1525  static uint64_t last_bytes_read = 0;
1526  struct timeb now;
1527  fprintf(fp, "XML ConfigFile: \"%s\"\n", m_configFile.c_str());
1528  fprintf(fp, "XML ConfigFile section: \"%s\"\n", m_configSection.c_str());
1529  fprintf(fp, "NetShrVarConfigure() Options: %d\n", m_options);
1530  fprintf(fp, "Total items read: %llu\n", static_cast<unsigned long long>(m_items_read));
1531  fprintf(fp, "Total bytes read: %llu\n", static_cast<unsigned long long>(m_bytes_read));
1532  ftime(&now);
1533  double tdiff = difftime(now.time, m_last_report.time) + ((int)now.millitm - (int)m_last_report.millitm) / 1000.0;
1534  fprintf(fp, "* Data rates are average since last call to this command *\n");
1535  fprintf(fp, "Items read /s: %f\n", (m_items_read - last_items_read) / tdiff );
1536  fprintf(fp, "Bytes read /s: %f\n", (m_bytes_read - last_bytes_read) / tdiff );
1537  last_items_read = m_items_read;
1538  last_bytes_read = m_bytes_read;
1539  m_last_report = now;
1540  for(params_t::iterator it=m_params.begin(); it != m_params.end(); ++it)
1541  {
1542  NvItem* item = it->second;
1543  item->report(it->first, fp);
1544  }
1545 }
1546 
1547 #ifndef DOXYGEN_SHOULD_SKIP_THIS
1548 
1549 template void NetShrVarInterface::setValue(const char* param, const double& value);
1550 template void NetShrVarInterface::setValue(const char* param, const int& value);
1551 
1552 template void NetShrVarInterface::setArrayValue(const char* param, const double* value, size_t nElements);
1553 template void NetShrVarInterface::setArrayValue(const char* param, const float* value, size_t nElements);
1554 template void NetShrVarInterface::setArrayValue(const char* param, const int* value, size_t nElements);
1555 template void NetShrVarInterface::setArrayValue(const char* param, const short* value, size_t nElements);
1556 template void NetShrVarInterface::setArrayValue(const char* param, const char* value, size_t nElements);
1557 
1558 template void NetShrVarInterface::readArrayValue(const char* paramName, double* value, size_t nElements, size_t* nIn);
1559 template void NetShrVarInterface::readArrayValue(const char* paramName, float* value, size_t nElements, size_t* nIn);
1560 template void NetShrVarInterface::readArrayValue(const char* paramName, int* value, size_t nElements, size_t* nIn);
1561 template void NetShrVarInterface::readArrayValue(const char* paramName, short* value, size_t nElements, size_t* nIn);
1562 template void NetShrVarInterface::readArrayValue(const char* paramName, char* value, size_t nElements, size_t* nIn);
1563 
1564 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
CNVBufferedSubscriber b_subscriber
void readVarInit(NvItem *item)
used to perform an initial read of a subscribed variable
void report(FILE *fp, const char *conn_type, void *handle, bool buffered)
int m_b_writer_wait_ms
how long to wait for a buffered write operation to complete in milliseconds
CallbackData(NetShrVarInterface *intf_, const std::string &nv_name_, int param_index_)
bool convertTimeStamp(unsigned __int64 timestamp, epicsTimeStamp *epicsTS)
convert a timestamp obtained from CNVGetDataUTCTimestamp() into an EPICS timestamp timestamp has 100n...
bool operator!=(CNVData d) const
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
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)
char * envExpand(const char *str)
expand epics environment strings using previously saved environment based on EPICS macEnvExpand() ...
void updateParamValue(int param_index, T val, epicsTimeStamp *epicsTS, bool do_asyn_param_callbacks)
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
void getParamStatus(int param_id, asynStatus &status, int &alarmStat, int &alarmSevr)
static void CVICALLBACK StatusCallback(void *handle, CNVConnectionStatus status, int error, void *callbackData)
called when status of a network shared variable changes
#define ERROR_PRINT_CONTINUE(__func, __code)
NetShrVarInterface * intf
static const char * getBrowseType(CNVBrowseType browseType)
void updateParamArrayValue(int param_index, T *val, size_t nElements, epicsTimeStamp *epicsTS, bool do_asyn_param_callbacks)
static void CVICALLBACK DataTransferredCallback(void *handle, int error, void *callbackData)
called when data has been transferred to the variable
void updateConnectedAlarmStatus(const std::string &paramName, int value, const std::string &alarmStr, epicsAlarmCondition stat, epicsAlarmSeverity sevr)
std::string ts_param
parameter that is timestamp source
void report(FILE *fp, int details)
Helper for EPICS driver report function.
void updateBytesReadCount(unsigned nbytes)
CNVReader reader
struct timeb m_last_report
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)
bool with_ts
timestamp is encoded in first few array elements
void readValue(const char *param)
read a value and update corresponding asyn parameter
std::string nv_name
full path to network shared variable
void createParams(asynPortDriver *driver)
static void convertLabviewTimeToEpicsTime(const uint64_t *lv_time, epicsTimeStamp *epicsTS)
bool operator==(CNVData d) const
static bool pathExists(const std::string &path)
std::map< std::string, NvItem * > params_t
NvItem(const std::string &nv_name_, const char *type_, unsigned access_, int field_, const std::string &ts_param_, bool with_ts_)
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)
called externally with m_driver locked
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.
epicsTimeStamp epicsTS
timestamp of shared variable update
static void initCV(void *)
int ctype
an instance of the underlying type
Definition: cnvconvert.h:22
int m_writer_wait_ms
how long to wait for a write operation to complete in milliseconds
static void epicsExitFunc(void *arg)
void setArrayValue(const char *param, const T *value, size_t nElements)
void statusCallback(void *handle, CNVConnectionStatus status, int error, CallbackData *cb_data)
called by StatusCallback() when status of a network shared variable changes
void updateParamCNVImpl(int param_index, CNVData data, CNVDataType type, unsigned int nDims, epicsTimeStamp *epicsTS, bool do_asyn_param_callbacks)
void setParamStatus(int param_id, asynStatus status, epicsAlarmCondition alarmStat=epicsAlarmNone, epicsAlarmSeverity alarmSevr=epicsSevNone)
ScopedCNVData & operator=(const CNVData &d)
void updateValues()
This is called from a polling loop in the driver to update values from buffered subscribers.
static bool varExists(const std::string &path)
void updateParamCNV(int param_index, CNVData data, epicsTimeStamp *epicsTS, bool do_asyn_param_callbacks)
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
NetShrVarException(const std::string &message)
For a given C data type, provide the appropriate network shared variable type.
Definition: cnvconvert.h:133
my_atomic_uint64_t m_bytes_read
my_atomic_uint32_t m_items_read
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)
int64_t sec_from_epoch
from 01/01/1904 00:00:00.00 UTC
Copyright © 2013 Science and Technology Facilities Council | Generated by   doxygen 1.8.5