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 
19 #include <epicsTypes.h>
20 #include <epicsTime.h>
21 #include <epicsThread.h>
22 #include <epicsString.h>
23 #include <epicsTimer.h>
24 #include <epicsMutex.h>
25 #include <epicsEvent.h>
26 #include <errlog.h>
27 #include <iocsh.h>
28 
29 #include "lvDCOMDriver.h"
30 #include <epicsExport.h>
31 
32 #include "lvDCOMInterface.h"
33 #include "convertToString.h"
34 #include "variant_utils.h"
35 
36 static const char *driverName="lvDCOMDriver";
37 
40 static void seTransFunction(unsigned int u, EXCEPTION_POINTERS* pExp)
41 {
42  throw Win32StructuredException(u, pExp);
43 }
44 
47 {
48  _set_se_translator(seTransFunction);
49 }
50 
51 template<typename T>
52 asynStatus lvDCOMDriver::writeValue(asynUser *pasynUser, const char* functionName, T value)
53 {
54  int function = pasynUser->reason;
55  asynStatus status = asynSuccess;
56  const char *paramName = NULL;
58  getParamName(function, &paramName);
59  try
60  {
61  if (m_lvdcom == NULL)
62  {
63  throw std::runtime_error("m_lvdcom is NULL");
64  }
65  m_lvdcom->setLabviewValue(paramName, value);
66  asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
67  "%s:%s: function=%d, name=%s, value=%s\n",
68  driverName, functionName, function, paramName, convertToString(value).c_str());
69  return asynSuccess;
70  }
71  catch(const std::exception& ex)
72  {
73  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
74  "%s:%s: status=%d, function=%d, name=%s, value=%s, error=%s",
75  driverName, functionName, status, function, paramName, convertToString(value).c_str(), ex.what());
76  return asynError;
77  }
78 }
79 
80 template<typename T>
81 asynStatus lvDCOMDriver::readValue(asynUser *pasynUser, const char* functionName, T* value)
82 {
83  int function = pasynUser->reason;
84  asynStatus status = asynSuccess;
85  const char *paramName = NULL;
87  getParamName(function, &paramName);
88  try
89  {
90  if (m_lvdcom == NULL)
91  {
92  throw std::runtime_error("m_lvdcom is NULL");
93  }
94  m_lvdcom->getLabviewValue(paramName, value);
95  asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
96  "%s:%s: function=%d, name=%s, value=%s\n",
97  driverName, functionName, function, paramName, convertToString(*value).c_str());
98  return asynSuccess;
99  }
100  catch(const std::exception& ex)
101  {
102  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
103  "%s:%s: status=%d, function=%d, name=%s, value=%s, error=%s",
104  driverName, functionName, status, function, paramName, convertToString(*value).c_str(), ex.what());
105  return asynError;
106  }
107 }
108 
109 template<typename T>
110 asynStatus lvDCOMDriver::readArray(asynUser *pasynUser, const char* functionName, T *value, size_t nElements, size_t *nIn)
111 {
112  int function = pasynUser->reason;
113  asynStatus status = asynSuccess;
114  const char *paramName = NULL;
116  getParamName(function, &paramName);
117 
118  try
119  {
120  if (m_lvdcom == NULL)
121  {
122  throw std::runtime_error("m_lvdcom is NULL");
123  }
124  m_lvdcom->getLabviewValue(paramName, value, nElements, *nIn);
125  asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
126  "%s:%s: function=%d, name=%s\n",
127  driverName, functionName, function, paramName);
128  return asynSuccess;
129  }
130  catch(const std::exception& ex)
131  {
132  *nIn = 0;
133  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
134  "%s:%s: status=%d, function=%d, name=%s, error=%s",
135  driverName, functionName, status, function, paramName, ex.what());
136  return asynError;
137  }
138 }
139 
140 asynStatus lvDCOMDriver::writeFloat64(asynUser *pasynUser, epicsFloat64 value)
141 {
142  return writeValue(pasynUser, "writeFloat64", value);
143 }
144 
145 asynStatus lvDCOMDriver::writeInt32(asynUser *pasynUser, epicsInt32 value)
146 {
147  return writeValue(pasynUser, "writeInt32", value);
148 }
149 
150 asynStatus lvDCOMDriver::readFloat64Array(asynUser *pasynUser, epicsFloat64 *value, size_t nElements, size_t *nIn)
151 {
152  return readArray(pasynUser, "readFloat64Array", value, nElements, nIn);
153 }
154 
155 asynStatus lvDCOMDriver::readInt32Array(asynUser *pasynUser, epicsInt32 *value, size_t nElements, size_t *nIn)
156 {
157  return readArray(pasynUser, "readInt32Array", value, nElements, nIn);
158 }
159 
160 asynStatus lvDCOMDriver::readFloat64(asynUser *pasynUser, epicsFloat64 *value)
161 {
162  return readValue(pasynUser, "readFloat64", value);
163 }
164 
165 asynStatus lvDCOMDriver::readInt32(asynUser *pasynUser, epicsInt32 *value)
166 {
167  return readValue(pasynUser, "readInt32", value);
168 }
169 
170 asynStatus lvDCOMDriver::readOctet(asynUser *pasynUser, char *value, size_t maxChars, size_t *nActual, int *eomReason)
171 {
172  int function = pasynUser->reason;
173  int status=0;
174  const char *functionName = "readOctet";
175  const char *paramName = NULL;
177  getParamName(function, &paramName);
178  std::string value_s;
179  try
180  {
181  if (m_lvdcom == NULL)
182  {
183  throw std::runtime_error("m_lvdcom is NULL");
184  }
185  m_lvdcom->getLabviewValue(paramName, &value_s);
186  if ( value_s.size() > maxChars ) // did we read more than we have space for?
187  {
188  *nActual = maxChars;
189  if (eomReason) { *eomReason = ASYN_EOM_CNT | ASYN_EOM_END; }
190  asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
191  "%s:%s: function=%d, name=%s, value=\"%s\" (TRUNCATED from %d chars)\n",
192  driverName, functionName, function, paramName, value_s.substr(0,*nActual).c_str(), value_s.size());
193  }
194  else
195  {
196  *nActual = value_s.size();
197  if (eomReason) { *eomReason = ASYN_EOM_END; }
198  asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
199  "%s:%s: function=%d, name=%s, value=\"%s\"\n",
200  driverName, functionName, function, paramName, value_s.c_str());
201  }
202  strncpy(value, value_s.c_str(), maxChars); // maxChars will NULL pad if possible, change to *nActual if we do not want this
203  return asynSuccess;
204  }
205  catch(const std::exception& ex)
206  {
207  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
208  "%s:%s: status=%d, function=%d, name=%s, value=\"%s\", error=%s",
209  driverName, functionName, status, function, paramName, value_s.c_str(), ex.what());
210  *nActual = 0;
211  if (eomReason) { *eomReason = ASYN_EOM_END; }
212  value[0] = '\0';
213  return asynError;
214  }
215 }
216 
217 asynStatus lvDCOMDriver::writeOctet(asynUser *pasynUser, const char *value, size_t maxChars, size_t *nActual)
218 {
219  int function = pasynUser->reason;
220  asynStatus status = asynSuccess;
221  const char *paramName = NULL;
223  getParamName(function, &paramName);
224  const char* functionName = "writeOctet";
225  std::string value_s(value, maxChars);
226  try
227  {
228  if (m_lvdcom == NULL)
229  {
230  throw std::runtime_error("m_lvdcom is NULL");
231  }
232  m_lvdcom->setLabviewValue(paramName, value_s);
233  asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
234  "%s:%s: function=%d, name=%s, value=%s\n",
235  driverName, functionName, function, paramName, value_s.c_str());
236  *nActual = value_s.size();
237  return asynSuccess;
238  }
239  catch(const std::exception& ex)
240  {
241  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
242  "%s:%s: status=%d, function=%d, name=%s, value=%s, error=%s",
243  driverName, functionName, status, function, paramName, value_s.c_str(), ex.what());
244  *nActual = 0;
245  return asynError;
246  }
247 }
248 
250 void lvDCOMDriver::report(FILE* fp, int details)
251 {
252 // fprintf(fp, "lvDCOM report\n");
253  if (m_lvdcom != NULL)
254  {
255  m_lvdcom->report(fp, details);
256  }
257  else
258  {
259  fprintf(fp, "DCOM pointer is NULL\n");
260  }
261  asynPortDriver::report(fp, details);
262 }
263 
264 
270 lvDCOMDriver::lvDCOMDriver(lvDCOMInterface* dcomint, const char *portName)
271  : asynPortDriver(portName,
272  0, /* maxAddr */
273  dcomint->nParams(),
274  asynInt32Mask | asynInt32ArrayMask | asynFloat64Mask | asynFloat64ArrayMask | asynOctetMask | asynDrvUserMask, /* Interface mask */
275  asynInt32Mask | asynInt32ArrayMask | asynFloat64Mask | asynFloat64ArrayMask | asynOctetMask, /* Interrupt mask */
276  ASYN_CANBLOCK, /* asynFlags. This driver can block but it is not multi-device */
277  1, /* Autoconnect */
278  0, /* Default priority */
279  0), /* Default stack size*/
280  m_lvdcom(dcomint)
281 {
282  int i;
283  const char *functionName = "lvDCOMDriver";
284  std::map<std::string,std::string> res;
285  m_lvdcom->getParams(res);
286  for(std::map<std::string,std::string>::const_iterator it=res.begin(); it != res.end(); ++it)
287  {
288  if (it->second == "float64")
289  {
290  createParam(it->first.c_str(), asynParamFloat64, &i);
291  }
292  else if (it->second == "int32" || it->second == "enum" || it->second == "ring" || it->second == "boolean")
293  {
294  createParam(it->first.c_str(), asynParamInt32, &i);
295  }
296  else if (it->second == "string")
297  {
298  createParam(it->first.c_str(), asynParamOctet, &i);
299  }
300  else if (it->second == "float64array")
301  {
302  createParam(it->first.c_str(), asynParamFloat64Array, &i);
303  }
304  else if (it->second == "int32array")
305  {
306  createParam(it->first.c_str(), asynParamInt32Array, &i);
307  }
308  else
309  {
310  errlogSevPrintf(errlogMajor, "%s:%s: unknown type %s for parameter %s\n", driverName, functionName, it->second.c_str(), it->first.c_str());
311 // std::cerr << driverName << ":" << functionName << ": unknown type " << it->second << " for parameter " << it->first << std::endl;
312  }
313  }
314 
315  // Create the thread for background tasks (not used at present, could be used for I/O intr scanning)
316  if (epicsThreadCreate("lvDCOMDriverTask",
317  epicsThreadPriorityMedium,
318  epicsThreadGetStackSize(epicsThreadStackMedium),
319  (EPICSTHREADFUNC)lvDCOMTask, this) == 0)
320  {
321  printf("%s:%s: epicsThreadCreate failure\n", driverName, functionName);
322  return;
323  }
324 }
325 
327 void lvDCOMDriver::lvDCOMTask(void* arg)
328 {
329  lvDCOMDriver* driver = (lvDCOMDriver*)arg;
331 }
332 
333 
334 extern "C" {
335 
347  int lvDCOMConfigure(const char *portName, const char* configSection, const char *configFile, const char *host, int options,
348  const char* progid, const char* username, const char* password)
349  {
351  try
352  {
353  lvDCOMInterface* dcomint = new lvDCOMInterface(configSection, configFile, host, options, progid, username, password);
354  if (dcomint != NULL)
355  {
356  new lvDCOMDriver(dcomint, portName);
357  return(asynSuccess);
358  }
359  else
360  {
361  errlogSevPrintf(errlogFatal, "lvDCOMConfigure failed (NULL)\n");
362  return(asynError);
363  }
364 
365  }
366  catch(const std::exception& ex)
367  {
368  errlogSevPrintf(errlogFatal, "lvDCOMConfigure failed: %s\n", ex.what());
369  return(asynError);
370  }
371  }
372 
373  // EPICS iocsh shell commands
374 
375  static const iocshArg initArg0 = { "portName", iocshArgString};
376  static const iocshArg initArg1 = { "configSection", iocshArgString};
377  static const iocshArg initArg2 = { "configFile", iocshArgString};
378  static const iocshArg initArg3 = { "host", iocshArgString};
379  static const iocshArg initArg4 = { "options", iocshArgInt};
380  static const iocshArg initArg5 = { "progid", iocshArgString};
381  static const iocshArg initArg6 = { "username", iocshArgString};
382  static const iocshArg initArg7 = { "password", iocshArgString};
383 
384  static const iocshArg * const initArgs[] = { &initArg0,
385  &initArg1,
386  &initArg2,
387  &initArg3,
388  &initArg4,
389  &initArg5,
390  &initArg6,
391  &initArg7 };
392 
393  static const iocshFuncDef initFuncDef = {"lvDCOMConfigure", sizeof(initArgs) / sizeof(iocshArg*), initArgs};
394 
395  static void initCallFunc(const iocshArgBuf *args)
396  {
397  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);
398  }
399 
401  static void lvDCOMRegister(void)
402  {
403  iocshRegister(&initFuncDef, initCallFunc);
404  }
405 
407 
408 }
409 
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)
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
Header file for various COM utilities.
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 const iocshArg initArg0
The name of the asyn driver port we will create.
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 to use to configure this asyn port
lvDCOMInterface * m_lvdcom
Definition: lvDCOMDriver.h:36
virtual asynStatus readFloat64(asynUser *pasynUser, epicsFloat64 *value)
static void lvDCOMTask(void *arg)
header for lvDCOMInterface class.
static const iocshArg initArg6
(optional) remote username for host
void getParams(std::map< std::string, std::string > &res)
asynStatus writeValue(asynUser *pasynUser, const char *functionName, T value)
Header for lvDCOMDriver class.
static const iocshArg *const initArgs[]
Header for templated number to string conversion functions.
virtual asynStatus readOctet(asynUser *pasynUser, char *value, size_t maxChars, size_t *nActual, int *eomReason)
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) ...
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
static void registerStructuredExceptionHandler()
Register a handler for Win32 strcutured exceptions. This needs to be done on a per thread basis...
static const iocshArg initArg4
options as per lvDCOMOptions enum