ISIS Logo
VISAdrv
ASYN driver for National Instruments VISA
drvAsynVISAPort.cpp
Go to the documentation of this file.
1 
3 #include <string.h>
4 #include <ctype.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <fcntl.h>
8 #include <osiUnistd.h>
9 #include <cantProceed.h>
10 #include <errlog.h>
11 #include <iocsh.h>
12 #include <epicsAssert.h>
13 #include <epicsExit.h>
14 #include <epicsStdio.h>
15 #include <epicsString.h>
16 #include <epicsThread.h>
17 #include <epicsTime.h>
18 #include <osiUnistd.h>
19 
20 #include <iostream>
21 #include <string>
22 
23 #include <visa.h>
24 
25 #include "asynDriver.h"
26 #include "asynOctet.h"
27 #include "asynOption.h"
28 #include "asynInterposeCom.h"
29 #include "asynInterposeEos.h"
30 
31 #include <epicsExport.h>
32 
33 #include "drvAsynVISAPort.h"
34 
36 typedef struct {
37  asynUser *pasynUser;
38  char *portName;
39  ViSession defaultRM;
40  ViSession vi;
41  bool connected;
42  char *resourceName;
43  unsigned long nReadBytes;
44  unsigned long nWriteBytes;
45  unsigned long nReadCalls;
46  unsigned long nWriteCalls;
47  double timeout;
48  bool isSerial;
49  bool isGPIB;
52  ViUInt8 termCharIn;
54  asynInterface common;
55  asynInterface option;
56  asynInterface octet;
57 } visaDriver_t;
58 
60 static std::string errMsg(ViSession vi, ViStatus err)
61 {
62  char err_msg[1024]={0};
63  viStatusDesc (vi, err, err_msg);
64  return std::string(err_msg);
65 }
66 
67 #define VI_CHECK_ERROR(__command, __err) \
68  if (__err < 0) \
69  { \
70  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, \
71  "%s: %s %s", driver->resourceName, __command, errMsg(driver->vi, err).c_str()); \
72  return asynError; \
73  }
74 
78 static asynStatus
79 getOption(void *drvPvt, asynUser *pasynUser,
80  const char *key, char *val, int valSize)
81 {
82  visaDriver_t *driver = (visaDriver_t*)drvPvt;
83  assert(driver);
84  if (!driver->connected)
85  {
86  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
87  "%s disconnected:", driver->resourceName);
88  return asynError;
89  }
90  if (!driver->isSerial)
91  {
92  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
93  "%s getOption - not a serial device", driver->resourceName);
94  return asynError;
95  }
96  ViUInt32 viu32;
97  ViUInt16 viu16, flow;
98  int l = -1;
99  ViStatus err = viGetAttribute(driver->vi, VI_ATTR_ASRL_FLOW_CNTRL, &flow);
100  VI_CHECK_ERROR(key, err);
101  if (epicsStrCaseCmp(key, "baud") == 0) {
102  if ( (err = viGetAttribute(driver->vi, VI_ATTR_ASRL_BAUD, &viu32)) == VI_SUCCESS ) {
103  l = epicsSnprintf(val, valSize, "%u", (unsigned)viu32);
104  }
105  }
106  else if (epicsStrCaseCmp(key, "bits") == 0) {
107  if ( (err = viGetAttribute(driver->vi, VI_ATTR_ASRL_DATA_BITS, &viu16)) == VI_SUCCESS ) {
108  l = epicsSnprintf(val, valSize, "%u", (unsigned)viu16);
109  }
110  }
111  else if (epicsStrCaseCmp(key, "parity") == 0) {
112  if ( (err = viGetAttribute(driver->vi, VI_ATTR_ASRL_PARITY, &viu16)) == VI_SUCCESS ) {
113  switch (viu16) {
114  case VI_ASRL_PAR_NONE:
115  l = epicsSnprintf(val, valSize, "none");
116  break;
117  case VI_ASRL_PAR_ODD:
118  l = epicsSnprintf(val, valSize, "odd");
119  break;
120  case VI_ASRL_PAR_EVEN:
121  l = epicsSnprintf(val, valSize, "even");
122  break;
123  case VI_ASRL_PAR_MARK:
124  l = epicsSnprintf(val, valSize, "mark");
125  break;
126  case VI_ASRL_PAR_SPACE:
127  l = epicsSnprintf(val, valSize, "space");
128  break;
129  default:
130  l = epicsSnprintf(val, valSize, "unknown");
131  break;
132  }
133  }
134  }
135  else if (epicsStrCaseCmp(key, "stop") == 0) {
136  if ( (err = viGetAttribute(driver->vi, VI_ATTR_ASRL_STOP_BITS, &viu16)) == VI_SUCCESS ) {
137  switch (viu16) {
138  case VI_ASRL_STOP_ONE:
139  l = epicsSnprintf(val, valSize, "1");
140  break;
141  case VI_ASRL_STOP_ONE5:
142  l = epicsSnprintf(val, valSize, "1.5");
143  break;
144  case VI_ASRL_STOP_TWO:
145  l = epicsSnprintf(val, valSize, "2");
146  break;
147  default:
148  l = epicsSnprintf(val, valSize, "unknown");
149  break;
150  }
151  }
152  }
153  else if (epicsStrCaseCmp(key, "clocal") == 0) {
154  l = epicsSnprintf(val, valSize, "%c", (flow & VI_ASRL_FLOW_DTR_DSR) ? 'N' : 'Y');
155  }
156  else if (epicsStrCaseCmp(key, "crtscts") == 0) {
157  l = epicsSnprintf(val, valSize, "%c", (flow & VI_ASRL_FLOW_RTS_CTS) ? 'Y' : 'N');
158  }
159  else if (epicsStrCaseCmp(key, "ixon") == 0) {
160  l = epicsSnprintf(val, valSize, "%c", (flow & VI_ASRL_FLOW_XON_XOFF) ? 'Y' : 'N');
161  }
162  else if (epicsStrCaseCmp(key, "ixany") == 0) {
163  l = epicsSnprintf(val, valSize, "%c", 'N'); // ixany not supported on windows?
164  }
165  else if (epicsStrCaseCmp(key, "ixoff") == 0) {
166  l = epicsSnprintf(val, valSize, "%c", (flow & VI_ASRL_FLOW_XON_XOFF) ? 'Y' : 'N');
167  }
168 // this is the formatted io buffer, not the low level one
169 // else if (epicsStrCaseCmp(key, "rbuff") == 0) {
170 // err = viGetAttribute(driver->vi, VI_ATTR_RD_BUF_SIZE, &viu32);
171 // l = epicsSnprintf(val, valSize, "%u", (unsigned)viu32);
172 // }
173 // else if (epicsStrCaseCmp(key, "wbuff") == 0) {
174 // err = viGetAttribute(driver->vi, VI_ATTR_WR_BUF_SIZE, &viu32);
175 // l = epicsSnprintf(val, valSize, "%u", (unsigned)viu32);
176 // }
177  else {
178  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
179  "Unsupported key \"%s\"", key);
180  return asynError;
181  }
182  VI_CHECK_ERROR(key, err);
183  if (l >= valSize) {
184  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
185  "Value buffer for key '%s' is too small.", key);
186  return asynError;
187  }
188  asynPrint(driver->pasynUser, ASYN_TRACEIO_DRIVER,
189  "%s getOption, key=%s, val=%s\n",
190  driver->portName, key, val);
191  return asynSuccess;
192 }
193 
197 static asynStatus
198 setOption(void *drvPvt, asynUser *pasynUser, const char *key, const char *val)
199 {
200  visaDriver_t *driver = (visaDriver_t*)drvPvt;
201  assert(driver);
202  if (!driver->connected)
203  {
204  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
205  "%s disconnected:", driver->resourceName);
206  return asynError;
207  }
208  if (!driver->isSerial)
209  {
210  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
211  "%s setOption - not a serial device", driver->resourceName);
212  return asynError;
213  }
214  asynPrint(pasynUser, ASYN_TRACE_FLOW,
215  "%s setOption key %s val %s\n", driver->portName, key, val);
216  ViUInt16 flow, old_flow;
217  ViStatus err = viGetAttribute(driver->vi, VI_ATTR_ASRL_FLOW_CNTRL, &flow);
218  VI_CHECK_ERROR(key, err);
219  old_flow = flow;
220  if (epicsStrCaseCmp(key, "baud") == 0) {
221  int baud;
222  if(sscanf(val, "%d", &baud) != 1) {
223  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
224  "Bad number");
225  return asynError;
226  }
227  err = viSetAttribute(driver->vi, VI_ATTR_ASRL_BAUD, baud);
228  }
229  else if (epicsStrCaseCmp(key, "bits") == 0) {
230  int bits;
231  if(sscanf(val, "%d", &bits) != 1) {
232  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
233  "Bad number");
234  return asynError;
235  }
236  err = viSetAttribute(driver->vi, VI_ATTR_ASRL_DATA_BITS, bits);
237  }
238  else if (epicsStrCaseCmp(key, "parity") == 0) {
239  if (epicsStrCaseCmp(val, "none") == 0) {
240  err = viSetAttribute(driver->vi, VI_ATTR_ASRL_PARITY, VI_ASRL_PAR_NONE);
241  }
242  else if (epicsStrCaseCmp(val, "odd") == 0) {
243  err = viSetAttribute(driver->vi, VI_ATTR_ASRL_PARITY, VI_ASRL_PAR_ODD);
244  }
245  else if (epicsStrCaseCmp(val, "even") == 0) {
246  err = viSetAttribute(driver->vi, VI_ATTR_ASRL_PARITY, VI_ASRL_PAR_EVEN);
247  }
248  else if (epicsStrCaseCmp(val, "mark") == 0) {
249  err = viSetAttribute(driver->vi, VI_ATTR_ASRL_PARITY, VI_ASRL_PAR_MARK);
250  }
251  else if (epicsStrCaseCmp(val, "space") == 0) {
252  err = viSetAttribute(driver->vi, VI_ATTR_ASRL_PARITY, VI_ASRL_PAR_SPACE);
253  }
254  else {
255  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
256  "Invalid parity.");
257  return asynError;
258  }
259  }
260  else if (epicsStrCaseCmp(key, "stop") == 0) {
261  if (epicsStrCaseCmp(val, "1") == 0) {
262  err = viSetAttribute(driver->vi, VI_ATTR_ASRL_STOP_BITS, VI_ASRL_STOP_ONE);
263  }
264  else if (epicsStrCaseCmp(val, "1.5") == 0) {
265  err = viSetAttribute(driver->vi, VI_ATTR_ASRL_STOP_BITS, VI_ASRL_STOP_ONE5);
266  }
267  else if (epicsStrCaseCmp(val, "2") == 0) {
268  err = viSetAttribute(driver->vi, VI_ATTR_ASRL_STOP_BITS, VI_ASRL_STOP_TWO);
269  }
270  else {
271  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
272  "Invalid number of stop bits.");
273  return asynError;
274  }
275  }
276  else if (epicsStrCaseCmp(key, "clocal") == 0) {
277  if (epicsStrCaseCmp(val, "Y") == 0) {
278  flow &= ~VI_ASRL_FLOW_DTR_DSR;
279  }
280  else if (epicsStrCaseCmp(val, "N") == 0) {
281  flow |= VI_ASRL_FLOW_DTR_DSR;
282  }
283  else {
284  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
285  "Invalid clocal value.");
286  return asynError;
287  }
288  }
289  else if (epicsStrCaseCmp(key, "crtscts") == 0) {
290  if (epicsStrCaseCmp(val, "Y") == 0) {
291  flow |= VI_ASRL_FLOW_RTS_CTS;
292  }
293  else if (epicsStrCaseCmp(val, "N") == 0) {
294  flow &= ~VI_ASRL_FLOW_RTS_CTS;
295  }
296  else {
297  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
298  "Invalid crtscts value.");
299  return asynError;
300  }
301  }
302  else if ( (epicsStrCaseCmp(key, "ixon") == 0) || (epicsStrCaseCmp(key, "ixoff") == 0 ) ) {
303  if (epicsStrCaseCmp(val, "Y") == 0) {
304  flow |= VI_ASRL_FLOW_XON_XOFF;
305  }
306  else if (epicsStrCaseCmp(val, "N") == 0) {
307  flow &= ~VI_ASRL_FLOW_XON_XOFF;
308  }
309  else {
310  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
311  "Invalid %s value.", key);
312  return asynError;
313  }
314  }
315  else if (epicsStrCaseCmp(key, "ixany") == 0) {
316  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
317  "Option ixany not supported on Windows");
318  return asynError;
319  }
320  else if (epicsStrCaseCmp(key, "wbuff") == 0) {
321  int buflen;
322  if(sscanf(val, "%d", &buflen) != 1) {
323  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
324  "Bad number");
325  return asynError;
326  }
327  err = viSetBuf(driver->vi, VI_IO_OUT_BUF, buflen);
328  }
329  else if (epicsStrCaseCmp(key, "rbuff") == 0) {
330  int buflen;
331  if(sscanf(val, "%d", &buflen) != 1) {
332  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
333  "Bad number");
334  return asynError;
335  }
336  err = viSetBuf(driver->vi, VI_IO_IN_BUF, buflen);
337  }
338  else if (epicsStrCaseCmp(key, "flush") == 0) {
339  if (epicsStrCaseCmp(val, "Y") == 0) {
340  driver->flush_on_write = true;
341  }
342  else if (epicsStrCaseCmp(val, "N") == 0) {
343  driver->flush_on_write = false;
344  }
345  else {
346  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
347  "Invalid flush value.");
348  return asynError;
349  }
350  }
351  else if (epicsStrCaseCmp(key, "") != 0) {
352  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
353  "Unsupported key \"%s\"", key);
354  return asynError;
355  }
356  if (err == VI_SUCCESS && flow != old_flow)
357  {
358  err = viSetAttribute(driver->vi, VI_ATTR_ASRL_FLOW_CNTRL, flow);
359  }
360  VI_CHECK_ERROR(key, err);
361  asynPrint(driver->pasynUser, ASYN_TRACEIO_DRIVER,
362  "%s setOption, key=%s, val=%s\n",
363  driver->portName, key, val);
364  return asynSuccess;
365 }
366 
367 static const struct asynOption asynOptionMethods = { setOption, getOption };
368 
370 static asynStatus
371 closeConnection(asynUser *pasynUser, visaDriver_t *driver, const char* reason)
372 {
373  asynPrint(pasynUser, ASYN_TRACE_FLOW,
374  "Close %s connection %s\n", driver->resourceName, reason);
375  if (!driver->connected) {
376  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
377  "%s: session already closed", driver->resourceName);
378  return asynError;
379  }
380  ViStatus err;
381  if ( (err = viClose(driver->vi)) != VI_SUCCESS )
382  {
383  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
384  "%s: viClose error", driver->resourceName);
385  return asynError;
386  }
387  driver->connected = false;
388  driver->vi = VI_NULL;
389  pasynManager->exceptionDisconnect(pasynUser);
390  return asynSuccess;
391 }
392 
393 
395 static void
396 asynCommonReport(void *drvPvt, FILE *fp, int details)
397 {
398  visaDriver_t *driver = (visaDriver_t*)drvPvt;
399  char termChar[16]; // bit of space for encoding an escape sequence
400  assert(driver);
401  if (details >= 1) {
402  fprintf(fp, " Port %s: %sonnected\n",
403  driver->resourceName,
404  (driver->connected ? "C" : "Disc"));
405  }
406  if (driver->termCharIn != 0)
407  {
408  epicsStrnEscapedFromRaw(termChar, sizeof(termChar), reinterpret_cast<const char*>(&(driver->termCharIn)), 1);
409  }
410  else
411  {
412  strncpy(termChar, "<none>", sizeof(termChar));
413  }
414  if (details >= 2) {
415  fprintf(fp, " Characters written: %lu\n", driver->nWriteBytes);
416  fprintf(fp, " Characters read: %lu\n", driver->nReadBytes);
417  fprintf(fp, " write operations: %lu\n", driver->nWriteCalls);
418  fprintf(fp, " read operations: %lu\n", driver->nReadCalls);
419  fprintf(fp, " Is serial device: %c\n", (driver->isSerial ? 'Y' : 'N'));
420  fprintf(fp, " Is GPIB device: %c\n", (driver->isGPIB ? 'Y' : 'N'));
421  fprintf(fp, " Device sends EOM: %c\n", (driver->deviceSendsEOM ? 'Y' : 'N'));
422  fprintf(fp, " Input term char hint: \"%s\" (0x%x)\n", termChar, (unsigned)driver->termCharIn);
423  fprintf(fp, "Internal read tmo (ms): %d\n", ((int)driver->readIntTimeout));
424  }
425 }
426 
427 static void
428 visaCleanup (void *arg)
429 {
430  asynStatus status;
431  visaDriver_t *driver = (visaDriver_t*)arg;
432 
433  if (!arg) return;
434  status=pasynManager->lockPort(driver->pasynUser);
435  if(status!=asynSuccess)
436  asynPrint(driver->pasynUser, ASYN_TRACE_ERROR, "%s: cleanup locking error\n", driver->portName);
437 
438  if(status==asynSuccess)
439  pasynManager->unlockPort(driver->pasynUser);
440 
441  viClose(driver->defaultRM); // this will automatically close all sessions
442 }
443 
444 static void
446 {
447  if (driver)
448  {
449  free(driver->portName);
450  free(driver->resourceName);
451  free(driver);
452  }
453 }
454 
456 static asynStatus
457 connectIt(void *drvPvt, asynUser *pasynUser)
458 {
459  visaDriver_t *driver = (visaDriver_t*)drvPvt;
460  assert(driver);
461  asynPrint(pasynUser, ASYN_TRACE_FLOW,
462  "Open connection to \"%s\" reason: %d\n", driver->resourceName,
463  pasynUser->reason);
464 
465  if (driver->connected) {
466  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
467  "%s: session already open.", driver->resourceName);
468  return asynError;
469  }
470  ViStatus err;
471  if ( (err = viOpen(driver->defaultRM, driver->resourceName,VI_NULL,VI_NULL,&(driver->vi))) != VI_SUCCESS )
472  {
473  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
474  "%s: viOpen %s", driver->resourceName, errMsg(driver->defaultRM, err).c_str());
475  return asynError;
476  }
477  ViUInt16 intf_type;
478  char intf_name[256];
479  intf_name[0] = '\0';
480  err = viGetAttribute(driver->vi, VI_ATTR_INTF_INST_NAME, intf_name);
481  VI_CHECK_ERROR("intf_name", err);
482  err = viGetAttribute(driver->vi, VI_ATTR_INTF_TYPE, &intf_type);
483  VI_CHECK_ERROR("intf_type", err);
484 
485  if (intf_type == VI_INTF_ASRL) // is it a serial device?
486  {
487  driver->isSerial = true;
488  err = viSetAttribute(driver->vi, VI_ATTR_ASRL_END_OUT, VI_ASRL_END_NONE);
489  VI_CHECK_ERROR("VI_ATTR_ASRL_END_OUT", err);
490  err = viSetAttribute(driver->vi, VI_ATTR_SEND_END_EN, VI_FALSE);
491  VI_CHECK_ERROR("VI_ATTR_SEND_END_EN", err);
492  err = viSetAttribute(driver->vi, VI_ATTR_SUPPRESS_END_EN, VI_TRUE);
493  VI_CHECK_ERROR("VI_ATTR_SUPPRESS_END_EN", err);
494  }
495  else
496  {
497  driver->isSerial = false;
498  }
499  if (intf_type == VI_INTF_GPIB)
500  {
501  driver->isGPIB = true;
502  // we should make these configurable
503  err = viSetAttribute(driver->vi, VI_ATTR_GPIB_READDR_EN, VI_TRUE);
504  VI_CHECK_ERROR("VI_ATTR_GPIB_READDR_EN", err);
505 // The LabVIEW driver set this to VI_TRUE (default is VI_FALSE) but causes problems for stress rig if we set it
506 // err = viSetAttribute(driver->vi, VI_ATTR_GPIB_UNADDR_EN, VI_TRUE);
507 // VI_CHECK_ERROR("VI_ATTR_GPIB_UNADDR_EN", err);
508  err = viSetAttribute(driver->vi, VI_ATTR_SEND_END_EN, VI_TRUE);
509  VI_CHECK_ERROR("VI_ATTR_SEND_END_EN", err);
510  }
511  else
512  {
513  driver->isGPIB = false;
514  }
515  if (driver->termCharIn != 0)
516  {
517  // tell VISA to terminate a read early when this character is seen
518  if (driver->isSerial)
519  {
520  err = viSetAttribute(driver->vi, VI_ATTR_ASRL_END_IN, VI_ASRL_END_TERMCHAR);
521  VI_CHECK_ERROR("VI_ATTR_ASTR_END_IN", err);
522  }
523  err = viSetAttribute(driver->vi, VI_ATTR_TERMCHAR, driver->termCharIn);
524  VI_CHECK_ERROR("VI_ATTR_TERMCHAR", err);
525  err = viSetAttribute(driver->vi, VI_ATTR_TERMCHAR_EN, VI_TRUE);
526  }
527  else
528  {
529  // disable read/write command exit on termination character VI_ATTR_TERMCHAR in general
530  if (driver->isSerial)
531  {
532  err = viSetAttribute(driver->vi, VI_ATTR_ASRL_END_IN, VI_ASRL_END_NONE);
533  VI_CHECK_ERROR("VI_ATTR_ASTR_END_IN", err);
534  }
535  err = viSetAttribute(driver->vi, VI_ATTR_TERMCHAR_EN, VI_FALSE);
536  }
537  VI_CHECK_ERROR("VI_ATTR_TERMCHAR_EN", err);
538 
539  err = viClear(driver->vi);
540  VI_CHECK_ERROR("viClear", err);
541 
542  // these are the defaults, need to change?
543 // viSetAttribute(driver->vi, VI_ATTR_SEND_END_EN, VI_TRUE);
544 // viSetAttribute(driver->vi, VI_ATTR_SUPPRESS_END_EN, VI_FALSE);
545 
546  // don't think we need to do anything about these as we using raw rather than buffered io
547  // VI_ATTR_RD_BUF_OPER_MODE
548  // VI_ATTR_WR_BUF_OPER_MODE -> VI_FLUSH_ON_ACCESS
549 
550  asynPrint(pasynUser, ASYN_TRACE_FLOW,
551  "Opened connection to \"%s\" (%s) isSerial=%c isGPIB=%c\n", driver->resourceName,
552  intf_name, (driver->isSerial ? 'Y' : 'N'), (driver->isGPIB ? 'Y' : 'N'));
553  driver->connected = true;
554  return asynSuccess;
555 }
556 
557 
558 static asynStatus
559 asynCommonConnect(void *drvPvt, asynUser *pasynUser)
560 {
561  asynStatus status = asynSuccess;
562 
563  status = connectIt(drvPvt, pasynUser);
564  if (status == asynSuccess)
565  pasynManager->exceptionConnect(pasynUser);
566  return status;
567 }
568 
569 static asynStatus
570 asynCommonDisconnect(void *drvPvt, asynUser *pasynUser)
571 {
572  visaDriver_t *driver = (visaDriver_t*)drvPvt;
573 
574  assert(driver);
575  return closeConnection(pasynUser,driver,"Disconnect request");
576 }
577 
579 static asynStatus writeIt(void *drvPvt, asynUser *pasynUser,
580  const char *data, size_t numchars, size_t *nbytesTransfered)
581 {
582  visaDriver_t *driver = (visaDriver_t*)drvPvt;
583  asynStatus status = asynSuccess;
584  bool timedout = false;
585  epicsTimeStamp epicsTS1, epicsTS2;
586 
587  assert(driver);
588  asynPrint(pasynUser, ASYN_TRACE_FLOW,
589  "%s write.\n", driver->resourceName);
590  asynPrintIO(pasynUser, ASYN_TRACEIO_DRIVER, data, numchars,
591  "%s write %lu\n", driver->resourceName, (unsigned long)numchars);
592  epicsTimeGetCurrent(&epicsTS1);
593  *nbytesTransfered = 0;
594  if (!driver->connected)
595  {
596  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
597  "%s disconnected:", driver->resourceName);
598  return asynError;
599  }
600  ++(driver->nWriteCalls);
601  if (numchars == 0)
602  {
603  return asynSuccess;
604  }
605  ViStatus err;
606  // always need to set timeout as use immediate as part of read
607  driver->timeout = pasynUser->timeout;
608  if (driver->timeout == 0)
609  {
610  err = viSetAttribute(driver->vi, VI_ATTR_TMO_VALUE, VI_TMO_INFINITE);
611  }
612  else
613  {
614  err = viSetAttribute(driver->vi, VI_ATTR_TMO_VALUE, static_cast<int>(driver->timeout * 1000.0));
615  }
616  VI_CHECK_ERROR("set timeout", err);
617  unsigned long actual = 0;
618  err = viWrite(driver->vi, (ViBuf)data, static_cast<ViUInt32>(numchars), &actual);
619  if ( err == VI_ERROR_TMO )
620  {
621  timedout = true;
622  }
623  else if ( err != VI_SUCCESS )
624  {
625  closeConnection(pasynUser,driver,"Write error");
626  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
627  "%s write error %s", driver->resourceName, errMsg(driver->vi, err).c_str());
628  return asynError;
629  }
630  if (driver->flush_on_write)
631  {
632  err = viFlush(driver->vi, VI_IO_OUT_BUF);
633  if ( err == VI_ERROR_TMO )
634  {
635  timedout = true;
636  }
637  else if ( err != VI_SUCCESS )
638  {
639  closeConnection(pasynUser,driver,"Write error");
640  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
641  "%s write error %s", driver->resourceName, errMsg(driver->vi, err).c_str());
642  return asynError;
643  }
644  }
645  driver->nWriteBytes += actual;
646  *nbytesTransfered = actual;
647  if (timedout)
648  {
649  status = asynTimeout;
650  }
651  epicsTimeGetCurrent(&epicsTS2);
652  asynPrint(pasynUser, ASYN_TRACE_FLOW,
653  "wrote %lu/%lu chars to %s, return %s.\n", (unsigned long)*nbytesTransfered, (unsigned long)numchars,
654  driver->resourceName,
655  pasynManager->strStatus(status));
656  asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s Write took %f timeout was %f\n",
657  driver->resourceName, epicsTimeDiffInSeconds(&epicsTS2, &epicsTS1), pasynUser->timeout);
658  return status;
659 }
660 
662 static asynStatus readIt(void *drvPvt, asynUser *pasynUser,
663  char *data, size_t maxchars, size_t *nbytesTransfered, int *gotEom)
664 {
665  visaDriver_t *driver = (visaDriver_t*)drvPvt;
666  int reason = 0;
667  asynStatus status = asynSuccess;
668  epicsTimeStamp epicsTS1, epicsTS2;
669  unsigned long actual = 0, actualex = 0;
670  ViStatus err;
671 
672  assert(driver);
673  asynPrint(pasynUser, ASYN_TRACE_FLOW,
674  "%s read.\n", driver->resourceName);
675  epicsTimeGetCurrent(&epicsTS1);
676  *nbytesTransfered = 0;
677  if (gotEom) *gotEom = 0;
678  if (!driver->connected)
679  {
680  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
681  "%s disconnected:", driver->resourceName);
682  return asynError;
683  }
684  ++(driver->nReadCalls);
685  if (maxchars <= 0) {
686  epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
687  "%s maxchars %d. Why <=0?",driver->resourceName,(int)maxchars);
688  return asynError;
689  }
690  driver->timeout = pasynUser->timeout;
691  // this is an optimisation - stream device does a zero timeout read to clear the input buffer
692  if (driver->timeout == 0 && driver->readIntTimeout < 0)
693  {
694 // err = viFlush(driver->vi, VI_IO_IN_BUF_DISCARD);
695  // this seems to error on GPIB?
696 // VI_CHECK_ERROR("viFlush", err);
697  data[0] = 0; // already checked maxchars > 0 above
698  status = asynTimeout;
699  epicsTimeGetCurrent(&epicsTS2);
700  asynPrint(pasynUser, ASYN_TRACE_FLOW,
701  "read %lu from %s, return %s.\n", (unsigned long)*nbytesTransfered,
702  driver->resourceName,
703  pasynManager->strStatus(status));
704  asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s Read took %f timeout was %f\n", driver->resourceName,
705  epicsTimeDiffInSeconds(&epicsTS2, &epicsTS1), pasynUser->timeout);
706  return asynTimeout;
707  }
708 // ViUInt32 avail = 0;
709  // should we only ever try and read one character?
710 // if (driver->isSerial)
711 // {
712 // if (viGetAttribute(driver->vi, VI_ATTR_ASRL_AVAIL_NUM, &avail) == VI_SUCCESS)
713 // {
714 // if (avail > maxchars)
715 // {
716 // avail = maxchars;
717 // }
718 // }
719 // }
720 
721 // try and read one character, if we don't time out try and read more with an immediate timeout
722 // we always need to set timeout as we reset to immediate below
723 // we don't use driver->readIntTimeout
724 // whatever out timeout, we can get called with a timeout of 0 by higher levels to flush the input queue
725 // prior to a write, hence we need to map to readIntTimeout to avois problems on GPIB-ENET
726  if (driver->timeout == 0)
727  {
728  err = viSetAttribute(driver->vi, VI_ATTR_TMO_VALUE, (driver->readIntTimeout == 0 ? VI_TMO_IMMEDIATE : driver->readIntTimeout) );
729  }
730  else
731  {
732  err = viSetAttribute(driver->vi, VI_ATTR_TMO_VALUE, static_cast<int>(driver->timeout * 1000.0));
733  }
734  VI_CHECK_ERROR("set timeout", err);
735  // if the device sends an EOM the read will terminate then rather than on timeout
736  if (driver->deviceSendsEOM)
737  {
738  err = viRead(driver->vi, (ViBuf)data, static_cast<ViUInt32>(maxchars), &actual);
739  // we have had issues with GPIB-ENET and immediate timeout, it returns bus error sometimes
740  // so don't close connectuion here, but ultimately return asynError via later logic
741  if (err < 0 && err != VI_ERROR_TMO && (driver->timeout != 0 || (driver->timeout == 0 && driver->readIntTimeout != 0)) )
742  {
743  closeConnection(pasynUser, driver, "Read error");
744  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
745  "%s read error %s", driver->resourceName, errMsg(driver->vi, err).c_str());
746  return asynError;
747  }
748  }
749  else
750  {
751  err = viRead(driver->vi, (ViBuf)data, 1, &actual);
752  // we have had issues with GPIB-ENET and immediate timeout, returns bus error sometimes
753  // so don't close connectuion here, but ultimately return asynError via later logic
754  if (err < 0 && err != VI_ERROR_TMO && (driver->timeout != 0 || (driver->timeout == 0 && driver->readIntTimeout != 0)) )
755  {
756  closeConnection(pasynUser, driver, "Read error (stage 1)");
757  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
758  "%s read error %s", driver->resourceName, errMsg(driver->vi, err).c_str());
759  return asynError;
760  }
761  if (actual > 0 && err == VI_SUCCESS_MAX_CNT)
762  {
763  // read anything else that might be there, originally this used VI_TMO_IMMEDIATE
764  // but we had a few timeout issues with GPIP over ethernet so this is now
765  // configurable to a small finite value
766  err = viSetAttribute(driver->vi, VI_ATTR_TMO_VALUE, (driver->readIntTimeout > 0 ? driver->readIntTimeout : VI_TMO_IMMEDIATE));
767  VI_CHECK_ERROR("set timeout", err);
768  err = viRead(driver->vi, reinterpret_cast<ViBuf>(data + actual), static_cast<ViUInt32>(maxchars - actual), &actualex);
769  if (err < 0 && err != VI_ERROR_TMO)
770  {
771  closeConnection(pasynUser, driver, "Read error (stage 2)");
772  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
773  "%s read error %s", driver->resourceName, errMsg(driver->vi, err).c_str());
774  return asynError;
775  }
776  actual += actualex;
777  // a VI_SUCCESS on GPIB means we got the EOM, on serial it doesn't necessarily mean this
778  // so on timeout don't convert to VI_SUCCESS but to something that will get ignored in the
779  // next case statement
780  // remove expected VI_ERROR_TMO, but leave VI_SUCCESS_TERM_CHAR etc.
781  if (err < 0)
782  {
783  err = VI_WARN_UNKNOWN_STATUS;
784  }
785  }
786  }
787  switch(err)
788  {
789  case VI_SUCCESS:
790  if (driver->deviceSendsEOM && actual > 0)
791  {
792  reason |= ASYN_EOM_END;
793  }
794  break;
795 
796  case VI_SUCCESS_TERM_CHAR:
797  reason |= ASYN_EOM_EOS;
798  break;
799 
800  case VI_SUCCESS_MAX_CNT:
801 // reason |= ASYN_EOM_CNT; // we read avail not maxchars, add this later if needed
802  break;
803 
804  case VI_ERROR_TMO:
805  status = asynTimeout;
806  break;
807 
808  default:
809  if (err < 0)
810  {
811  status = asynError;
812  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
813  "%s read error %s", driver->resourceName, errMsg(driver->vi, err).c_str());
814  }
815  break;
816  }
817  if (actual > 0)
818  {
819  asynPrintIO(pasynUser, ASYN_TRACEIO_DRIVER, data, actual,
820  "%s read %d\n", driver->resourceName, actual);
821  driver->nReadBytes += (unsigned long)actual;
822  }
823 // else
824 // {
825 // epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
826 // "%s read", driver->resourceName);
827 // closeConnection(pasynUser,driver,"Read error");
828 // status = asynError;
829 // }
830  *nbytesTransfered = actual;
831  /* If there is room add a null byte */
832  if (actual < (int) maxchars)
833  data[actual] = 0;
834  else
835  reason |= ASYN_EOM_CNT;
836  if (gotEom) *gotEom = reason;
837  epicsTimeGetCurrent(&epicsTS2);
838  asynPrint(pasynUser, ASYN_TRACE_FLOW,
839  "read %lu from %s, return %s.\n", (unsigned long)*nbytesTransfered,
840  driver->resourceName,
841  pasynManager->strStatus(status));
842  asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s Read took %f timeout was %f\n", driver->resourceName,
843  epicsTimeDiffInSeconds(&epicsTS2, &epicsTS1), pasynUser->timeout);
844  return status;
845 }
846 
848 static asynStatus
849 flushIt(void *drvPvt,asynUser *pasynUser)
850 {
851  epicsTimeStamp epicsTS1, epicsTS2;
852  epicsTimeGetCurrent(&epicsTS1);
853  visaDriver_t *driver = (visaDriver_t*)drvPvt;
854  assert(driver);
855  if (!driver->connected)
856  {
857  epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize,
858  "%s disconnected:", driver->resourceName);
859  return asynError;
860  }
861  // ViStatus err = viFlush(driver->vi, VI_IO_OUT_BUF);
862 // ViStatus err = viFlush(driver->vi, VI_IO_IN_BUF_DISCARD);
863 // VI_CHECK_ERROR("flush", err);
864  epicsTimeGetCurrent(&epicsTS2);
865  asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s flush\n", driver->resourceName);
866  asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s flush took %f\n", driver->resourceName,
867  epicsTimeDiffInSeconds(&epicsTS2, &epicsTS1));
868  return asynSuccess;
869 }
870 
871 static asynOctet asynOctetMethods = { writeIt, readIt, flushIt };
872 
873 /*
874  * asynCommon methods
875  */
876 static const struct asynCommon asynCommonMethods = {
880 };
881 
882 
892 epicsShareFunc int
893 drvAsynVISAPortConfigure(const char *portName,
894  const char *resourceName,
895  unsigned int priority,
896  int noAutoConnect,
897  int noProcessEos,
898  int readIntTmoMs,
899  const char* termCharIn,
900  int deviceSendsEOM)
901 {
902  visaDriver_t *driver;
903  asynStatus status;
904  static int firstTime = 1;
905 
906  /*
907  * Check arguments
908  */
909  if (portName == NULL) {
910  printf("drvAsynVISAPortConfigure: Port name missing.\n");
911  return -1;
912  }
913  if (resourceName == NULL) {
914  printf("drvAsynVISAPortConfigure: resourceName information missing.\n");
915  return -1;
916  }
917 
918  /*
919  * Perform some one-time-only initializations
920  */
921  if (firstTime) {
922  firstTime = 0;
923  }
924 
925  /*
926  * Create a driver
927  */
928  driver = (visaDriver_t *)callocMustSucceed(1, sizeof(visaDriver_t), "drvAsyVISAPortConfigure()");
929  driver->connected = false;
930  driver->resourceName = epicsStrDup(resourceName);
931  driver->portName = epicsStrDup(portName);
932  driver->timeout = -0.1;
933  driver->isSerial = false;
934  driver->isGPIB = false;
935  driver->flush_on_write = false;
936  driver->deviceSendsEOM = (deviceSendsEOM != 0);
937  if (readIntTmoMs != 0)
938  {
939  printf("drvAsynVISAPortConfigure: using internal read timeout of %d ms\n", readIntTmoMs);
940  driver->readIntTimeout = readIntTmoMs;
941  }
942  else
943  {
944  driver->readIntTimeout = 0;
945  }
946  driver->termCharIn = 0;
947  // we also trap "0" as a term char as likely sent by accident
948  if ( termCharIn != NULL && *termCharIn != '\0' && !(strlen(termCharIn) == 1 && *termCharIn == '0') )
949  {
950  char termChar[16];
951  epicsStrnRawFromEscaped(termChar, sizeof(termChar), termCharIn, strlen(termCharIn));
952  if (strlen(termChar) == 1)
953  {
954  driver->termCharIn = termChar[0];
955  printf("drvAsynVISAPortConfigure: using term char hint \"%s\" (0x%x)\n", termCharIn, (unsigned)driver->termCharIn);
956  }
957  else
958  {
959  printf("drvAsynVISAPortConfigure: termChar must be single character - NOT SET\n");
960  }
961  }
962  if (viOpenDefaultRM(&(driver->defaultRM)) != VI_SUCCESS)
963  {
964  printf("drvAsynVISAPortConfigure: viOpenDefaultRM failed for port \"%s\"\n", driver->portName);
965  driverCleanup(driver);
966  return -1;
967  }
968  driver->pasynUser = pasynManager->createAsynUser(0,0);
969 
970  /*
971  * Link with higher level routines
972  */
973  driver->common.interfaceType = asynCommonType;
974  driver->common.pinterface = (void *)&asynCommonMethods;
975  driver->common.drvPvt = driver;
976  driver->option.interfaceType = asynOptionType;
977  driver->option.pinterface = (void *)&asynOptionMethods;
978  driver->option.drvPvt = driver;
979 
980  if (pasynManager->registerPort(driver->portName,
981  ASYN_CANBLOCK,
982  !noAutoConnect,
983  priority,
984  0) != asynSuccess) {
985  printf("drvAsynVISAPortConfigure: Can't register myself.\n");
986  driverCleanup(driver);
987  return -1;
988  }
989  status = pasynManager->registerInterface(driver->portName,&driver->common);
990  if(status != asynSuccess) {
991  printf("drvAsynVISAPortConfigure: Can't register common.\n");
992  driverCleanup(driver);
993  return -1;
994  }
995  status = pasynManager->registerInterface(driver->portName,&driver->option);
996  if(status != asynSuccess) {
997  printf("drvAsynVISAPortConfigure: Can't register option.\n");
998  driverCleanup(driver);
999  return -1;
1000  }
1001  driver->octet.interfaceType = asynOctetType;
1002  driver->octet.pinterface = &asynOctetMethods;
1003  driver->octet.drvPvt = driver;
1004  status = pasynOctetBase->initialize(driver->portName,&driver->octet,
1005  (noProcessEos ? 0 : 1),(noProcessEos ? 0 : 1),1);
1006  if(status != asynSuccess) {
1007  printf("drvAsynVISAPortConfigure: Can't register octet.\n");
1008  driverCleanup(driver);
1009  return -1;
1010  }
1011  status = pasynManager->connectDevice(driver->pasynUser,driver->portName,-1);
1012  if(status != asynSuccess) {
1013  printf("drvAsynVISAPortConfigure: connectDevice failed %s\n",driver->pasynUser->errorMessage);
1014  visaCleanup(driver);
1015  driverCleanup(driver);
1016  return -1;
1017  }
1018 
1019  /*
1020  * Register for socket cleanup
1021  */
1022  epicsAtExit(visaCleanup, driver);
1023  return 0;
1024 }
1025 
1026 /*
1027  * IOC shell command registration
1028  */
1029 
1031 static const iocshArg drvAsynVISAPortConfigureArg0 = { "portName",iocshArgString};
1033 static const iocshArg drvAsynVISAPortConfigureArg1 = { "resourceName",iocshArgString};
1035 static const iocshArg drvAsynVISAPortConfigureArg2 = { "priority",iocshArgInt};
1037 static const iocshArg drvAsynVISAPortConfigureArg3 = { "noAutoConnect",iocshArgInt};
1040 static const iocshArg drvAsynVISAPortConfigureArg4 = { "noProcessEos",iocshArgInt};
1048 static const iocshArg drvAsynVISAPortConfigureArg5 = { "readIntTmoMs",iocshArgInt};
1052 static const iocshArg drvAsynVISAPortConfigureArg6 = { "termCharIn",iocshArgString};
1057 static const iocshArg drvAsynVISAPortConfigureArg7 = { "deviceSendsEOM",iocshArgInt};
1058 
1059 static const iocshArg *drvAsynVISAPortConfigureArgs[] = {
1063 
1064 };
1065 
1066 static const iocshFuncDef drvAsynVISAPortConfigureFuncDef =
1067  {"drvAsynVISAPortConfigure",sizeof(drvAsynVISAPortConfigureArgs)/sizeof(iocshArg*),drvAsynVISAPortConfigureArgs};
1068 
1069 static void drvAsynVISAPortConfigureCallFunc(const iocshArgBuf *args)
1070 {
1071  drvAsynVISAPortConfigure(args[0].sval, args[1].sval, args[2].ival, args[3].ival,
1072  args[4].ival, args[5].ival, args[6].sval, args[7].ival);
1073 }
1074 
1075 extern "C"
1076 {
1077 
1078 static void
1080 {
1081  static int firstTime = 1;
1082  if (firstTime) {
1084  firstTime = 0;
1085  }
1086 }
1087 
1089 
1090 }
unsigned long nWriteCalls
number of written calls to this resource
static void visaCleanup(void *arg)
static struct asynCommon asynCommonMethods
static asynStatus writeIt(void *drvPvt, asynUser *pasynUser, const char *data, size_t numchars, size_t *nbytesTransfered)
write values to device
bool flush_on_write
use viFlush to flush output buffer every write
asynUser * pasynUser
ASYN driver for National Instruments VISA.
static struct asynOption asynOptionMethods
ViSession vi
VISA session handle.
bool deviceSendsEOM
Indicates that the device signals an &quot;end of message&quot;.
double timeout
requested timeout for current operation
unsigned long nReadBytes
number of bytes read from this resource name
static asynStatus closeConnection(asynUser *pasynUser, visaDriver_t *driver, const char *reason)
close a VISA session
static const iocshArg drvAsynVISAPortConfigureArg4
Should the driver interpose layer be called for EOS (termination) character processing (0=yes) If you...
ViSession defaultRM
static const iocshArg drvAsynVISAPortConfigureArg3
Should the driver automatically connect to the device (0=yes)
unsigned long nReadCalls
number of read calls from this resource name
bool connected
are we currently connected
static asynStatus flushIt(void *drvPvt, asynUser *pasynUser)
flush device
static asynStatus readIt(void *drvPvt, asynUser *pasynUser, char *data, size_t maxchars, size_t *nbytesTransfered, int *gotEom)
read values from device
char * resourceName
VISA resource name session connected to.
static asynStatus getOption(void *drvPvt, asynUser *pasynUser, const char *key, char *val, int valSize)
asynOption interface - get options
static asynStatus asynCommonConnect(void *drvPvt, asynUser *pasynUser)
static const iocshArg drvAsynVISAPortConfigureArg7
Indicates that the device signals an &quot;end of message&quot;.
static void drvAsynVISAPortConfigureCallFunc(const iocshArgBuf *args)
static asynStatus asynCommonDisconnect(void *drvPvt, asynUser *pasynUser)
asynInterface option
ViUInt8 termCharIn
read termination character, this is purely to improve read efficiency and is independent of any chara...
asynInterface common
static const iocshArg drvAsynVISAPortConfigureArg0
A name for the asyn driver instance we will create e.g. &quot;L0&quot;.
static const iocshArg drvAsynVISAPortConfigureArg2
Driver priority.
#define VI_CHECK_ERROR(__command, __err)
static std::string errMsg(ViSession vi, ViStatus err)
translate VISA error code to readable string
char * portName
asyn port name
unsigned long nWriteBytes
number of bytes written to this resource
asynInterface octet
static const iocshArg drvAsynVISAPortConfigureArg5
internal read timeout (ms) used instead of a zero timeout immediate read.
epicsShareFunc int drvAsynVISAPortConfigure(const char *portName, const char *resourceName, unsigned int priority, int noAutoConnect, int noProcessEos, int readIntTmoMs, const char *termCharIn, int deviceSendsEOM)
Create a VISA device.
static const iocshArg drvAsynVISAPortConfigureArg1
VISA resource name to connect to e.g. &quot;GPIB0::3::INSTR&quot; or &quot;COM10&quot;.
driver private data structure
static const iocshFuncDef drvAsynVISAPortConfigureFuncDef
bool isGPIB
are we a GPIB device?
static void asynCommonReport(void *drvPvt, FILE *fp, int details)
asynCommon interface - Report link parameters
static void driverCleanup(visaDriver_t *driver)
bool isSerial
are we an RS232 style serial device?
static asynOctet asynOctetMethods
static const iocshArg drvAsynVISAPortConfigureArg6
read termination character, this is purely to improve read efficiency and is independent of any chara...
epicsExportRegistrar(drvAsynVISAPortConfigureRegister)
int readIntTimeout
internal read timeout (ms) used instead of a zero timeout immediate read.
static void drvAsynVISAPortConfigureRegister(void)
static asynStatus setOption(void *drvPvt, asynUser *pasynUser, const char *key, const char *val)
asynOption interface - set options
static asynStatus connectIt(void *drvPvt, asynUser *pasynUser)
create a link
static const iocshArg * drvAsynVISAPortConfigureArgs[]
Copyright © 2013 Science and Technology Facilities Council | Generated by   doxygen 1.8.5