ISIS Logo
lvDCOM
An EPICS support module to export LabVIEW values as process variables
lvDCOMDriver.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 <stdlib.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include <errno.h>
15 #include <math.h>
16 #include <exception>
17 #include <iostream>
18 #include <map>
19 
20 #include <epicsTypes.h>
21 #include <epicsTime.h>
22 #include <epicsThread.h>
23 #include <epicsString.h>
24 #include <epicsTimer.h>
25 #include <epicsMutex.h>
26 #include <epicsEvent.h>
27 #include <errlog.h>
28 #include <iocsh.h>
29 
30 #include "lvDCOMDriver.h"
31 #include <epicsExport.h>
32 
33 #include "lvDCOMInterface.h"
34 #include "convertToString.h"
35 #include "variant_utils.h"
36 
37 static const char *driverName="lvDCOMDriver";
38 
41 static void seTransFunction(unsigned int u, EXCEPTION_POINTERS* pExp)
42 {
43  throw Win32StructuredException(u, pExp);
44 }
45 
48 {
49  _set_se_translator(seTransFunction);
50 }
51 
52 template<typename T>
53 asynStatus lvDCOMDriver::writeValue(asynUser *pasynUser, const char* functionName, T value)
54 {
55  int function = pasynUser->reason;
56  asynStatus status = asynSuccess;
57  const char *paramName = NULL;
59  getParamName(function, &paramName);
60  try
61  {
62  if (m_lvdcom == NULL)
63  {
64  throw std::runtime_error("m_lvdcom is NULL");
65  }
66  m_lvdcom->setLabviewValue(paramName, value);
67  asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
68  "%s:%s: function=%d, name=%s, value=%s\n",
69  driverName, functionName, function, paramName, convertToString(value).c_str());
70  return asynSuccess;
71  }
72  catch(const std::exception& ex)
73  {
74  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
75  "%s:%s: status=%d, function=%d, name=%s, value=%s, error=%s",
76  driverName, functionName, status, function, paramName, convertToString(value).c_str(), ex.what());
77  return asynError;
78  }
79 }
80 
81 template<typename T>
82 asynStatus lvDCOMDriver::readValue(asynUser *pasynUser, const char* functionName, T* value)
83 {
84  int function = pasynUser->reason;
85  asynStatus status = asynSuccess;
86  const char *paramName = NULL;
88  getParamName(function, &paramName);
89  try
90  {
91  if (m_lvdcom == NULL)
92  {
93  throw std::runtime_error("m_lvdcom is NULL");
94  }
95  m_lvdcom->getLabviewValue(paramName, value);
96  asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
97  "%s:%s: function=%d, name=%s, value=%s\n",
98  driverName, functionName, function, paramName, convertToString(*value).c_str());
99  return asynSuccess;
100  }
101  catch(const std::exception& ex)
102  {
103  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
104  "%s:%s: status=%d, function=%d, name=%s, value=%s, error=%s",
105  driverName, functionName, status, function, paramName, convertToString(*value).c_str(), ex.what());
106  return asynError;
107  }
108 }
109 
110 template<typename T>
111 asynStatus lvDCOMDriver::readArray(asynUser *pasynUser, const char* functionName, T *value, size_t nElements, size_t *nIn)
112 {
113  int function = pasynUser->reason;
114  asynStatus status = asynSuccess;
115  const char *paramName = NULL;
117  getParamName(function, &paramName);
118 
119  try
120  {
121  if (m_lvdcom == NULL)
122  {
123  throw std::runtime_error("m_lvdcom is NULL");
124  }
125  m_lvdcom->getLabviewValue(paramName, value, nElements, *nIn);
126  asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
127  "%s:%s: function=%d, name=%s\n",
128  driverName, functionName, function, paramName);
129  return asynSuccess;
130  }
131  catch(const std::exception& ex)
132  {
133  *nIn = 0;
134  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
135  "%s:%s: status=%d, function=%d, name=%s, error=%s",
136  driverName, functionName, status, function, paramName, ex.what());
137  return asynError;
138  }
139 }
140 
141 asynStatus lvDCOMDriver::writeFloat64(asynUser *pasynUser, epicsFloat64 value)
142 {
143  return writeValue(pasynUser, "writeFloat64", value);
144 }
145 
146 asynStatus lvDCOMDriver::writeInt32(asynUser *pasynUser, epicsInt32 value)
147 {
148  return writeValue(pasynUser, "writeInt32", value);
149 }
150 
151 asynStatus lvDCOMDriver::readFloat64Array(asynUser *pasynUser, epicsFloat64 *value, size_t nElements, size_t *nIn)
152 {
153  return readArray(pasynUser, "readFloat64Array", value, nElements, nIn);
154 }
155 
156 asynStatus lvDCOMDriver::readInt32Array(asynUser *pasynUser, epicsInt32 *value, size_t nElements, size_t *nIn)
157 {
158  return readArray(pasynUser, "readInt32Array", value, nElements, nIn);
159 }
160 
161 asynStatus lvDCOMDriver::readFloat64(asynUser *pasynUser, epicsFloat64 *value)
162 {
163  return readValue(pasynUser, "readFloat64", value);
164 }
165 
166 asynStatus lvDCOMDriver::readInt32(asynUser *pasynUser, epicsInt32 *value)
167 {
168  return readValue(pasynUser, "readInt32", value);
169 }
170 
171 asynStatus lvDCOMDriver::readOctet(asynUser *pasynUser, char *value, size_t maxChars, size_t *nActual, int *eomReason)
172 {
173  int function = pasynUser->reason;
174  int status=0;
175  const char *functionName = "readOctet";
176  const char *paramName = NULL;
178  getParamName(function, &paramName);
179  std::string value_s;
180  try
181  {
182  if (m_lvdcom == NULL)
183  {
184  throw std::runtime_error("m_lvdcom is NULL");
185  }
186  m_lvdcom->getLabviewValue(paramName, &value_s);
187  if ( value_s.size() > maxChars ) // did we read more than we have space for?
188  {
189  *nActual = maxChars;
190  if (eomReason) { *eomReason = ASYN_EOM_CNT | ASYN_EOM_END; }
191  asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
192  "%s:%s: function=%d, name=%s, value=\"%s\" (TRUNCATED from %d chars)\n",
193  driverName, functionName, function, paramName, value_s.substr(0,*nActual).c_str(), value_s.size());
194  }
195  else
196  {
197  *nActual = value_s.size();
198  if (eomReason) { *eomReason = ASYN_EOM_END; }
199  asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
200  "%s:%s: function=%d, name=%s, value=\"%s\"\n",
201  driverName, functionName, function, paramName, value_s.c_str());
202  }
203  strncpy(value, value_s.c_str(), maxChars); // maxChars will NULL pad if possible, change to *nActual if we do not want this
204  return asynSuccess;
205  }
206  catch(const std::exception& ex)
207  {
208  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
209  "%s:%s: status=%d, function=%d, name=%s, value=\"%s\", error=%s",
210  driverName, functionName, status, function, paramName, value_s.c_str(), ex.what());
211  *nActual = 0;
212  if (eomReason) { *eomReason = ASYN_EOM_END; }
213  value[0] = '\0';
214  return asynError;
215  }
216 }
217 
218 asynStatus lvDCOMDriver::writeOctet(asynUser *pasynUser, const char *value, size_t maxChars, size_t *nActual)
219 {
220  int function = pasynUser->reason;
221  asynStatus status = asynSuccess;
222  const char *paramName = NULL;
224  getParamName(function, &paramName);
225  const char* functionName = "writeOctet";
226  std::string value_s(value, maxChars);
227  try
228  {
229  if (m_lvdcom == NULL)
230  {
231  throw std::runtime_error("m_lvdcom is NULL");
232  }
233  m_lvdcom->setLabviewValue(paramName, value_s);
234  asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
235  "%s:%s: function=%d, name=%s, value=%s\n",
236  driverName, functionName, function, paramName, value_s.c_str());
237  *nActual = value_s.size();
238  return asynSuccess;
239  }
240  catch(const std::exception& ex)
241  {
242  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
243  "%s:%s: status=%d, function=%d, name=%s, value=%s, error=%s",
244  driverName, functionName, status, function, paramName, value_s.c_str(), ex.what());
245  *nActual = 0;
246  return asynError;
247  }
248 }
249 
251 void lvDCOMDriver::report(FILE* fp, int details)
252 {
253 // fprintf(fp, "lvDCOM report\n");
254  for(std::map<std::string,std::string>::const_iterator it=m_params.begin(); it != m_params.end(); ++it)
255  {
256  fprintf(fp, "Asyn param \"%s\" lvdcom type \"%s\"\n", it->first.c_str(), it->second.c_str());
257  }
258  if (m_lvdcom != NULL)
259  {
260  m_lvdcom->report(fp, details);
261  }
262  else
263  {
264  fprintf(fp, "DCOM pointer is NULL\n");
265  }
266  asynPortDriver::report(fp, details);
267 }
268 
269 
275 lvDCOMDriver::lvDCOMDriver(lvDCOMInterface* dcomint, const char *portName)
276  : asynPortDriver(portName,
277  0, /* maxAddr */
278  dcomint->nParams(),
279  asynInt32Mask | asynInt32ArrayMask | asynFloat64Mask | asynFloat64ArrayMask | asynOctetMask | asynDrvUserMask, /* Interface mask */
280  asynInt32Mask | asynInt32ArrayMask | asynFloat64Mask | asynFloat64ArrayMask | asynOctetMask, /* Interrupt mask */
281  ASYN_CANBLOCK, /* asynFlags. This driver can block but it is not multi-device */
282  1, /* Autoconnect */
283  0, /* Default priority */
284  0), /* Default stack size*/
285  m_lvdcom(dcomint)
286 {
287  int i;
288  const char *functionName = "lvDCOMDriver";
290  for(std::map<std::string,std::string>::const_iterator it=m_params.begin(); it != m_params.end(); ++it)
291  {
292  if (it->second == "float64")
293  {
294  createParam(it->first.c_str(), asynParamFloat64, &i);
295  }
296  else if (it->second == "int32" || it->second == "enum" || it->second == "ring" || it->second == "boolean")
297  {
298  createParam(it->first.c_str(), asynParamInt32, &i);
299  }
300  else if (it->second == "string")
301  {
302  createParam(it->first.c_str(), asynParamOctet, &i);
303  }
304  else if (it->second == "float64array")
305  {
306  createParam(it->first.c_str(), asynParamFloat64Array, &i);
307  }
308  else if (it->second == "int32array")
309  {
310  createParam(it->first.c_str(), asynParamInt32Array, &i);
311  }
312  else
313  {
314  errlogSevPrintf(errlogMajor, "%s:%s: unknown type %s for parameter %s\n", driverName, functionName, it->second.c_str(), it->first.c_str());
315 // std::cerr << driverName << ":" << functionName << ": unknown type " << it->second << " for parameter " << it->first << std::endl;
316  }
317  }
318 
319  // Create the thread for background tasks (not used at present, could be used for I/O intr scanning)
320  if (epicsThreadCreate("lvDCOMDriverTask",
321  epicsThreadPriorityMedium,
322  epicsThreadGetStackSize(epicsThreadStackMedium),
323  (EPICSTHREADFUNC)lvDCOMTaskC, this) == 0)
324  {
325  printf("%s:%s: epicsThreadCreate failure\n", driverName, functionName);
326  return;
327  }
328 }
329 
331 {
332  lvDCOMDriver* driver = (lvDCOMDriver*)arg;
333  driver->lvDCOMTask();
334 }
335 
338 {
340  while(true)
341  {
342  lock();
343  bool new_blocks = m_lvdcom->checkForNewBlockDetails();
344  unlock();
345  if (new_blocks)
346  {
347  std::cerr << "Terminating as in SECI mode and new blocks detected" << std::endl;
348  epicsExit(0);
349  }
350  epicsThreadSleep(30);
351  }
352 }
353 
354 extern "C" {
355 
367  int lvDCOMConfigure(const char *portName, const char* configSection, const char *configFile, const char *host, int options,
368  const char* progid, const char* username, const char* password)
369  {
371  try
372  {
373  lvDCOMInterface* dcomint = new lvDCOMInterface(configSection, configFile, host, options, progid, username, password);
374  if (dcomint != NULL)
375  {
376  new lvDCOMDriver(dcomint, portName);
377  return(asynSuccess);
378  }
379  else
380  {
381  errlogSevPrintf(errlogFatal, "lvDCOMConfigure failed (NULL)\n");
382  return(asynError);
383  }
384 
385  }
386  catch(const std::exception& ex)
387  {
388  errlogSevPrintf(errlogFatal, "lvDCOMConfigure failed: %s\n", ex.what());
389  return(asynError);
390  }
391  }
392 
404  int lvDCOMSECIConfigure(const char *portName, const char* macros, const char* configSection, const char *configFile,
405  const char* dbSubFile, const char *host, int options, const char* blocks_match, const char* progid, const char* username, const char* password)
406  {
408  try
409  {
410  // need to specify both of these so we also get restarted when labview disappears (SECI restart / config change)
411  options |= static_cast<int>(lvDCOMOptions::lvNoStart);
412  options |= static_cast<int>(lvDCOMOptions::lvSECIConfig);
413  lvDCOMInterface* dcomint = new lvDCOMInterface("", "", host, 0x0, progid, username, password);
414  if (dcomint != NULL)
415  {
416  while(dcomint->generateFilesFromSECI(portName, macros, configSection, configFile, dbSubFile, blocks_match, (options & static_cast<int>(lvDCOMOptions::lvSECINoSetter) != 0)) == 0)
417  {
418  std::cerr << "lvDCOMSECIConfigure found no blocks - retrying\n";
419  epicsThreadSleep(30);
420  }
421  return lvDCOMConfigure(portName, configSection, configFile, host, options, progid, username, password);
422  }
423  else
424  {
425  errlogSevPrintf(errlogFatal, "lvDCOMSECIConfigure failed (NULL)\n");
426  return(asynError);
427  }
428  }
429  catch(const std::exception& ex)
430  {
431  errlogSevPrintf(errlogFatal, "lvDCOMSECIConfigure failed: %s\n", ex.what());
432  return(asynError);
433  }
434  }
435  // EPICS iocsh shell commands
436 
437  static const iocshArg initArg0 = { "portName", iocshArgString};
438  static const iocshArg initArg1 = { "configSection", iocshArgString};
439  static const iocshArg initArg2 = { "configFile", iocshArgString};
440  static const iocshArg initArg3 = { "host", iocshArgString};
441  static const iocshArg initArg4 = { "options", iocshArgInt};
442  static const iocshArg initArg5 = { "progid", iocshArgString};
443  static const iocshArg initArg6 = { "username", iocshArgString};
444  static const iocshArg initArg7 = { "password", iocshArgString};
445 
446  static const iocshArg initArgSECI0 = { "portName", iocshArgString};
447  static const iocshArg initArgSECI1 = { "macros", iocshArgString};
448  static const iocshArg initArgSECI2 = { "configSection", iocshArgString};
449  static const iocshArg initArgSECI3 = { "configFile", iocshArgString};
450  static const iocshArg initArgSECI4 = { "dbSubFile", iocshArgString};
451  static const iocshArg initArgSECI5 = { "host", iocshArgString};
452  static const iocshArg initArgSECI6 = { "options", iocshArgInt};
453  static const iocshArg initArgSECI7 = { "blocks_match", iocshArgString};
454  static const iocshArg initArgSECI8 = { "progid", iocshArgString};
455  static const iocshArg initArgSECI9 = { "username", iocshArgString};
456  static const iocshArg initArgSECI10 = { "password", iocshArgString};
457 
458  static const iocshArg * const initArgs[] = { &initArg0,
459  &initArg1,
460  &initArg2,
461  &initArg3,
462  &initArg4,
463  &initArg5,
464  &initArg6,
465  &initArg7 };
466 
467  static const iocshArg * const initArgsSECI[] = { &initArgSECI0,
468  &initArgSECI1,
469  &initArgSECI2,
470  &initArgSECI3,
471  &initArgSECI4,
472  &initArgSECI5,
473  &initArgSECI6,
474  &initArgSECI7,
475  &initArgSECI8,
476  &initArgSECI9,
477  &initArgSECI10 };
478 
479  static const iocshFuncDef initFuncDef = { "lvDCOMConfigure", sizeof(initArgs) / sizeof(iocshArg*), initArgs};
480  static const iocshFuncDef initFuncDefSECI = { "lvDCOMSECIConfigure", sizeof(initArgsSECI) / sizeof(iocshArg*), initArgsSECI};
481 
482  static void initCallFunc(const iocshArgBuf *args)
483  {
484  lvDCOMConfigure(args[0].sval, args[1].sval, args[2].sval, args[3].sval, args[4].ival, args[5].sval, args[6].sval, args[7].sval);
485  }
486 
487  static void initCallFuncSECI(const iocshArgBuf *args)
488  {
489  lvDCOMSECIConfigure(args[0].sval, args[1].sval, args[2].sval, args[3].sval, args[4].sval, args[5].sval, args[6].ival, args[7].sval, args[8].sval, args[9].sval, args[10].sval);
490  }
491 
493  static void lvDCOMRegister(void)
494  {
495  iocshRegister(&initFuncDef, initCallFunc);
496  iocshRegister(&initFuncDefSECI, initCallFuncSECI);
497  }
498 
500 
501 }
502 
virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value)
std::string convertToString(T t)
Convert a numeric type to a string.
int lvDCOMConfigure(const char *portName, const char *configSection, const char *configFile, const char *host, int options, const char *progid, const char *username, const char *password)
EPICS iocsh callable function to call constructor of lvDCOMInterface().
EPICS Asyn port driver class.
Definition: lvDCOMDriver.h:19
virtual void report(FILE *fp, int details)
EPICS driver report function for iocsh dbior command.
static const iocshArg initArg2
Path to the XML input file to load configuration information from.
virtual asynStatus readFloat64Array(asynUser *pasynUser, epicsFloat64 *value, size_t nElements, size_t *nIn)
virtual asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value)
static void initCallFuncSECI(const iocshArgBuf *args)
bool checkForNewBlockDetails()
asynStatus readArray(asynUser *pasynUser, const char *functionName, T *value, size_t nElements, size_t *nIn)
An STL exception describing a Win32 Structured Exception.
Definition: variant_utils.h:27
static const iocshArg initArgSECI9
(optional) remote username for host
Header file for various COM utilities.
static const iocshArg initArgSECI1
macros to substitute when generating dbSubFile
static const iocshArg initArgSECI7
(optional) PCRE expression for blocks to match, default: all
static const iocshArg initArgSECI2
section name of configFile to write settings to
static void seTransFunction(unsigned int u, EXCEPTION_POINTERS *pExp)
Function to translate a Win32 structured exception into a standard C++ exception. ...
virtual asynStatus readInt32Array(asynUser *pasynUser, epicsInt32 *value, size_t nElements, size_t *nIn)
static void lvDCOMTaskC(void *arg)
static const iocshArg initArg0
A name for the asyn driver instance we will create - used to refer to it from EPICS DB files...
epicsExportRegistrar(lvDCOMRegister)
static const iocshArg initArg3
host name where LabVIEW is running (&quot;&quot; for localhost)
virtual asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value)
static const char * driverName
Name of driver for use in message printing.
void report(FILE *fp, int details)
Helper for EPICS driver report function.
asynStatus readValue(asynUser *pasynUser, const char *functionName, T *value)
static const iocshArg initArg1
section name of configFile we will load settings from
lvDCOMInterface * m_lvdcom
Definition: lvDCOMDriver.h:37
(16) Do not start LabVIEW, connect to existing instance otherwise fail. As loading a Vi starts labvie...
virtual asynStatus readFloat64(asynUser *pasynUser, epicsFloat64 *value)
header for lvDCOMInterface class.
static const iocshArg initArg6
(optional) remote username for host
static const iocshArg initArgSECI0
A name for the asyn driver instance we will create - used to refer to it from EPICS DB files...
void getParams(std::map< std::string, std::string > &res)
asynStatus writeValue(asynUser *pasynUser, const char *functionName, T value)
(32) Automatically set if lvDCOMSECIConfigure() has been used
static const iocshArg *const initArgsSECI[]
int lvDCOMSECIConfigure(const char *portName, const char *macros, const char *configSection, const char *configFile, const char *dbSubFile, const char *host, int options, const char *blocks_match, const char *progid, const char *username, const char *password)
A name for the asyn driver instance we will create - used to refer to it from EPICS DB files...
Header for lvDCOMDriver class.
static const iocshArg *const initArgs[]
std::map< std::string, std::string > m_params
Definition: lvDCOMDriver.h:38
Header for templated number to string conversion functions.
virtual asynStatus readOctet(asynUser *pasynUser, char *value, size_t maxChars, size_t *nActual, int *eomReason)
int generateFilesFromSECI(const char *portName, const char *macros, const char *configSection, const char *configFile, const char *dbSubFile, const char *blocks_match, bool no_setter)
generate XML and DB files for SECI blocks
Manager class for LabVIEW DCOM Interaction. Parses an lvinput.xml file and provides access to the Lab...
void setLabviewValue(const char *param, const T &value)
static void lvDCOMRegister(void)
Register new commands with EPICS IOC shell.
static const iocshFuncDef initFuncDef
virtual asynStatus writeOctet(asynUser *pasynUser, const char *value, size_t maxChars, size_t *nActual)
static const iocshArg initArg5
(optional) DCOM ProgID (required if connecting to a compiled LabVIEW application) ...
static const iocshArg initArgSECI4
Path to the epics db substitution file to generate.
lvDCOMDriver(lvDCOMInterface *dcomint, const char *portName)
Constructor for the lvDCOMDriver class.
void getLabviewValue(const char *param, T *value)
static void initCallFunc(const iocshArgBuf *args)
static const iocshArg initArg7
(optional) remote password for username on host
(64) Do not generate setter XML / :SP PVs in SECI mode
static const iocshFuncDef initFuncDefSECI
static const iocshArg initArgSECI5
host name where LabVIEW is running (&quot;&quot; for localhost)
static void registerStructuredExceptionHandler()
Register a handler for Win32 structured exceptions. This needs to be done on a per thread basis...
static const iocshArg initArgSECI10
(optional) remote password for username on host
static const iocshArg initArgSECI3
Path to the XML output file name to write configuration information to.
static const iocshArg initArg4
options as per lvDCOMOptions enum
static const iocshArg initArgSECI6
options as per lvDCOMOptions enum
static const iocshArg initArgSECI8
(optional) DCOM ProgID (required if connecting to a compiled LabVIEW application) ...
Copyright © 2013 Science and Technology Facilities Council | Generated by   doxygen 1.8.5