SDDS ToolKit Programs and Libraries for C and Python
All Classes Files Functions Variables Macros Pages
agilentcomm.cpp
Go to the documentation of this file.
1/**
2 * @file agilentcomm.cpp
3 * @brief Communication interface for Agilent Oscilloscopes via VXI-11 protocol.
4 *
5 * @details
6 * This software enables Ethernet-based communication with Agilent oscilloscopes using the VXI-11 protocol. It supports sending commands, queries, and retrieving responses for both control and data acquisition tasks. The tool facilitates integration into automated testing systems.
7 *
8 * @section Usage
9 * ```
10 * ./agilentcomm -ip <scope_ip> -c <command> [-h]
11 * ```
12 *
13 * @section Options
14 * | Required | Description |
15 * |---------------------------------------|---------------------------------------------------------------------------------------|
16 * | `-ip <scope_ip>` | Specifies the IP address of the Agilent oscilloscope. |
17 * | `-c <command>` | The command or query to send to the oscilloscope. |
18 *
19 * | Optional | Description |
20 * |---------------------------------------|---------------------------------------------------------------------------------------|
21 * | `-h` | Displays help information and usage instructions. |
22 *
23 * @copyright
24 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
25 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
26 *
27 * @license
28 * This file is distributed under the terms of the Software License Agreement
29 * found in the file LICENSE included with this distribution.
30 *
31 * @author
32 * Based on software from Steve Sharples of the Univ. of Nottingham.
33 * Modified by R. Soliday.
34 */
35
36#include <cstdio>
37#include <cstdlib>
38#include <cstring>
39#include <rpc/rpc.h>
40#include <pthread.h>
41
42using Device_Link = long;
43using Device_Flags = long;
44using Device_ErrorCode = long;
45
47 Device_ErrorCode error;
48};
49
51 long clientId;
52 bool_t lockDevice;
53 unsigned long lock_timeout;
54 char *device;
55};
56
58 Device_ErrorCode error;
59 Device_Link lid;
60 unsigned short abortPort;
61 unsigned long maxRecvSize;
62};
63
65 Device_Link lid;
66 unsigned long io_timeout;
67 unsigned long lock_timeout;
68 Device_Flags flags;
69 struct {
70 unsigned int data_len;
71 char *data_val;
72 } data;
73};
74
76 Device_ErrorCode error;
77 unsigned long size;
78};
79
81 Device_Link lid;
82 unsigned long requestSize;
83 unsigned long io_timeout;
84 unsigned long lock_timeout;
85 Device_Flags flags;
86 char termChar;
87};
88
90 Device_ErrorCode error;
91 long reason;
92 struct {
93 unsigned int data_len;
94 char *data_val;
95 } data;
96};
97
98constexpr int CREATE_LINK = 10;
99constexpr int DEVICE_WRITE = 11;
100constexpr int DEVICE_READ = 12;
101constexpr int DESTROY_LINK = 23;
102
103constexpr int VXI11_DEFAULT_TIMEOUT = 10000; // in ms
104constexpr int VXI11_READ_TIMEOUT = 2000; // in ms
105using VXI11_CLIENT = CLIENT;
107constexpr int VXI11_MAX_CLIENTS = 256; // maximum number of unique IP addresses/clients
108constexpr int VXI11_NULL_READ_RESP = 50; // vxi11_receive() return value if a query times out on the instrument
109constexpr int VXI11_NULL_WRITE_RESP = 51; // vxi11_send() return value if a sent command times out on the instrument
110
111struct CLINK {
112 VXI11_CLIENT *client;
113 VXI11_LINK *link;
114};
115
116constexpr int DEVICE_CORE = 0x0607AF;
117constexpr int DEVICE_CORE_VERSION = 1;
118constexpr int RCV_END_BIT = 0x04; // An end indicator has been read
119constexpr int RCV_CHR_BIT = 0x02; // A termchar is set in flags and a character which matches termChar is transferred
120
121char VXI11_IP_ADDRESS[VXI11_MAX_CLIENTS][20];
122CLIENT *VXI11_CLIENT_ADDRESS[VXI11_MAX_CLIENTS];
123int VXI11_DEVICE_NO = 0;
124int VXI11_LINK_COUNT[VXI11_MAX_CLIENTS] = {0};
125
126static struct timeval TIMEOUT = {25, 0};
127
128constexpr int BUF_LEN = 1000000;
129
130// Function prototypes
131bool sc(const char *con, const char *var);
132
133int vxi11_open_device(const char *ip, CLINK *clink);
134int vxi11_open_device(const char *ip, CLINK *clink, const char *device);
135int vxi11_open_device(const char *ip, CLIENT **client, VXI11_LINK **link, const char *device);
136int vxi11_open_link(const char *ip, CLIENT **client, VXI11_LINK **link, const char *device);
137int vxi11_send(CLINK *clink, const char *cmd);
138int vxi11_send(CLINK *clink, const char *cmd, unsigned long len);
139int vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd);
140int vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd, unsigned long len);
141int vxi11_close_device(const char *ip, CLINK *clink);
142int vxi11_close_device(const char *ip, CLIENT *client, VXI11_LINK *link);
143int vxi11_close_link(const char *ip, CLIENT *client, VXI11_LINK *link);
144double vxi11_obtain_double_value(CLINK *clink, const char *cmd);
145double vxi11_obtain_double_value(CLINK *clink, const char *cmd, unsigned long timeout);
146long vxi11_send_and_receive(CLINK *clink, const char *cmd, char *buf, unsigned long buf_len, unsigned long timeout);
147long vxi11_receive(CLINK *clink, char *buffer, unsigned long len);
148long vxi11_receive(CLIENT *client, VXI11_LINK *link, char *buffer, unsigned long len, unsigned long timeout);
149long vxi11_receive(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout);
150long vxi11_obtain_long_value(CLINK *clink, const char *cmd, unsigned long timeout);
151long vxi11_obtain_long_value(CLINK *clink, const char *cmd);
152long vxi11_receive_data_block(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout);
153
154// RPC function prototypes
155enum clnt_stat create_link_1(Create_LinkParms *argp, Create_LinkResp *clnt_res, CLIENT *clnt);
156enum clnt_stat device_write_1(Device_WriteParms *argp, Device_WriteResp *clnt_res, CLIENT *clnt);
157enum clnt_stat destroy_link_1(Device_Link *argp, Device_Error *clnt_res, CLIENT *clnt);
158enum clnt_stat device_read_1(Device_ReadParms *argp, Device_ReadResp *clnt_res, CLIENT *clnt);
159
160// XDR function prototypes
161bool_t xdr_Create_LinkParms(XDR *xdrs, Create_LinkParms *objp);
162bool_t xdr_Create_LinkResp(XDR *xdrs, Create_LinkResp *objp);
163bool_t xdr_Device_ErrorCode(XDR *xdrs, Device_ErrorCode *objp);
164bool_t xdr_Device_Link(XDR *xdrs, Device_Link *objp);
165bool_t xdr_Device_WriteParms(XDR *xdrs, Device_WriteParms *objp);
166bool_t xdr_Device_WriteResp(XDR *xdrs, Device_WriteResp *objp);
167bool_t xdr_Device_Flags(XDR *xdrs, Device_Flags *objp);
168bool_t xdr_Device_Error(XDR *xdrs, Device_Error *objp);
169bool_t xdr_Device_ReadParms(XDR *xdrs, Device_ReadParms *objp);
170bool_t xdr_Device_ReadResp(XDR *xdrs, Device_ReadResp *objp);
171
172int main(int argc, char **argv) {
173 const char *progname = argv[0];
174 const char *serverIP = nullptr;
175 char comm[256] = {0};
176 long bytes_returned = 0;
177 bool got_ip = false;
178 bool got_comm = false;
179 bool got_help = false;
180 int index = 1;
181 CLINK *clink = new CLINK{nullptr, nullptr};
182 char outputbuf[BUF_LEN] = {0};
183
184 // Parse command-line arguments
185 while (index < argc) {
186 if (sc(argv[index], "-ip") || sc(argv[index], "-ip_address") || sc(argv[index], "-IP")) {
187 if (++index < argc) {
188 serverIP = argv[index];
189 got_ip = true;
190 }
191 }
192
193 if (sc(argv[index], "-command") || sc(argv[index], "-c") || sc(argv[index], "-comm")) {
194 if (++index < argc) {
195 std::snprintf(comm, sizeof(comm), "%s", argv[index]);
196 got_comm = true;
197 }
198 }
199
200 // Handle help option
201 if (sc(argv[index], "-h") || sc(argv[index], "-help")) {
202 got_help = true;
203 }
204
205 index++;
206 }
207
208 // Validate required arguments
209 if (!got_ip || !got_comm || got_help) {
210 if (!got_ip || !got_comm) {
211 std::fprintf(stderr, "Error: Missing required arguments.\n\n");
212 }
213 std::printf("Usage: %s [OPTIONS]\n\n", progname);
214 std::printf("Options:\n");
215 std::printf(" -ip, -ip_address, -IP IP address of scope (e.g., 128.243.74.232)\n");
216 std::printf(" -c, -command, -comm Command or query to send\n");
217 std::printf(" -h, -help Display this help message\n\n");
218 std::printf("Documentation:\n");
219 std::printf(" http://cp.literature.agilent.com/litweb/pdf/54855-97017.pdf\n");
220 delete clink;
221 return EXIT_FAILURE;
222 }
223
224 // Open device
225 if (vxi11_open_device(serverIP, clink) != 0) {
226 std::fprintf(stderr, "Error: Failed to open device at IP %s.\n", serverIP);
227 delete clink;
228 return EXIT_FAILURE;
229 }
230
231 // Send command
232 if (vxi11_send(clink, comm) != 0) {
233 std::fprintf(stderr, "Error: Failed to send command '%s' to device.\n", comm);
234 vxi11_close_device(serverIP, clink);
235 delete clink;
236 return EXIT_FAILURE;
237 }
238
239 // Check if it's a query
240 if (std::strchr(comm, '?') != nullptr) {
241 bytes_returned = vxi11_receive(clink, outputbuf, BUF_LEN);
242 vxi11_close_device(serverIP, clink);
243
244 if (bytes_returned > 0) {
245 std::printf("%s\n", outputbuf);
246 } else if (bytes_returned == -VXI11_NULL_READ_RESP) {
247 std::fprintf(stderr, "Error: Nothing received after sending scope command '%s'.\n", comm);
248 delete clink;
249 return EXIT_FAILURE;
250 } else {
251 std::fprintf(stderr, "Error: Failed to receive response for command '%s'.\n", comm);
252 delete clink;
253 return EXIT_FAILURE;
254 }
255 } else {
256 vxi11_close_device(serverIP, clink);
257 }
258
259 delete clink;
260 return EXIT_SUCCESS;
261}
262
263// String comparison function for parsing
264bool sc(const char *con, const char *var) {
265 return std::strcmp(con, var) == 0;
266}
267
268int vxi11_open_device(const char *ip, CLINK *clink) {
269 const char device[] = "inst0";
270 return vxi11_open_device(ip, clink, device);
271}
272
273int vxi11_open_device(const char *ip, CLIENT **client, VXI11_LINK **link, const char *device) {
274#ifdef __APPLE__
275 *client = clnt_create(reinterpret_cast<char *>(ip), DEVICE_CORE, DEVICE_CORE_VERSION, "tcp");
276#else
277 *client = clnt_create(ip, DEVICE_CORE, DEVICE_CORE_VERSION, "tcp");
278#endif
279
280 if (*client == nullptr) {
281#ifdef __APPLE__
282 clnt_pcreateerror(reinterpret_cast<char *>(ip));
283#else
284 clnt_pcreateerror(ip);
285#endif
286 return -1;
287 }
288
289 return vxi11_open_link(ip, client, link, device);
290}
291
292int vxi11_open_device(const char *ip, CLINK *clink, const char *device) {
293 int ret;
294 int device_no = -1;
295
296 for (int l = 0; l < VXI11_MAX_CLIENTS; l++) {
297 if (std::strcmp(ip, VXI11_IP_ADDRESS[l]) == 0) {
298 device_no = l;
299 break;
300 }
301 }
302
303 if (device_no < 0) {
304 if (VXI11_DEVICE_NO >= VXI11_MAX_CLIENTS) {
305 std::fprintf(stderr, "Error: Maximum of %d clients allowed.\n", VXI11_MAX_CLIENTS);
306 ret = -VXI11_MAX_CLIENTS;
307 } else {
308 ret = vxi11_open_device(ip, &(clink->client), &(clink->link), device);
309 std::strncpy(VXI11_IP_ADDRESS[VXI11_DEVICE_NO], ip, sizeof(VXI11_IP_ADDRESS[VXI11_DEVICE_NO]) - 1);
310 VXI11_IP_ADDRESS[VXI11_DEVICE_NO][sizeof(VXI11_IP_ADDRESS[VXI11_DEVICE_NO]) - 1] = '\0';
311 VXI11_CLIENT_ADDRESS[VXI11_DEVICE_NO] = clink->client;
312 VXI11_LINK_COUNT[VXI11_DEVICE_NO] = 1;
313 VXI11_DEVICE_NO++;
314 }
315 } else {
316 clink->client = VXI11_CLIENT_ADDRESS[device_no];
317 ret = vxi11_open_link(ip, &(clink->client), &(clink->link), device);
318 VXI11_LINK_COUNT[device_no]++;
319 }
320 return ret;
321}
322
323int vxi11_open_link(const char *ip, CLIENT **client, VXI11_LINK **link, const char *device) {
324 Create_LinkParms link_parms;
325
326 // Set link parameters
327 link_parms.clientId = reinterpret_cast<long>(*client);
328 link_parms.lockDevice = false;
329 link_parms.lock_timeout = VXI11_DEFAULT_TIMEOUT;
330 link_parms.device = const_cast<char *>(device);
331
332 *link = new Create_LinkResp();
333
334 if (create_link_1(&link_parms, *link, *client) != RPC_SUCCESS) {
335#ifdef __APPLE__
336 clnt_perror(*client, reinterpret_cast<char *>(ip));
337#else
338 clnt_perror(*client, ip);
339#endif
340 return -2;
341 }
342 return 0;
343}
344
345enum clnt_stat create_link_1(Create_LinkParms *argp, Create_LinkResp *clnt_res, CLIENT *clnt) {
346 return clnt_call(clnt, CREATE_LINK,
347 reinterpret_cast<xdrproc_t>(xdr_Create_LinkParms), reinterpret_cast<caddr_t>(argp),
348 reinterpret_cast<xdrproc_t>(xdr_Create_LinkResp), reinterpret_cast<caddr_t>(clnt_res),
349 TIMEOUT);
350}
351
352bool_t xdr_Create_LinkParms(XDR *xdrs, Create_LinkParms *objp) {
353#if defined(SOLARIS) && !defined(_LP64)
354 long *buf;
355#else
356 int32_t *buf;
357#endif
358
359 if (xdrs->x_op == XDR_ENCODE) {
360 buf = reinterpret_cast<int32_t *>(XDR_INLINE(xdrs, 3 * BYTES_PER_XDR_UNIT));
361 if (buf == nullptr) {
362 if (!xdr_long(xdrs, &objp->clientId))
363 return FALSE;
364 if (!xdr_bool(xdrs, &objp->lockDevice))
365 return FALSE;
366 if (!xdr_u_long(xdrs, &objp->lock_timeout))
367 return FALSE;
368 } else {
369 IXDR_PUT_INT32(buf, objp->clientId);
370 IXDR_PUT_BOOL(buf, objp->lockDevice);
371 IXDR_PUT_U_INT32(buf, objp->lock_timeout);
372 }
373 if (!xdr_string(xdrs, &objp->device, ~0))
374 return FALSE;
375 return TRUE;
376 } else if (xdrs->x_op == XDR_DECODE) {
377 buf = reinterpret_cast<int32_t *>(XDR_INLINE(xdrs, 3 * BYTES_PER_XDR_UNIT));
378 if (buf == nullptr) {
379 if (!xdr_long(xdrs, &objp->clientId))
380 return FALSE;
381 if (!xdr_bool(xdrs, &objp->lockDevice))
382 return FALSE;
383 if (!xdr_u_long(xdrs, &objp->lock_timeout))
384 return FALSE;
385 } else {
386 objp->clientId = IXDR_GET_INT32(buf);
387 objp->lockDevice = IXDR_GET_BOOL(buf);
388 objp->lock_timeout = IXDR_GET_U_INT32(buf);
389 }
390 if (!xdr_string(xdrs, &objp->device, ~0))
391 return FALSE;
392 return TRUE;
393 }
394
395 if (!xdr_long(xdrs, &objp->clientId))
396 return FALSE;
397 if (!xdr_bool(xdrs, &objp->lockDevice))
398 return FALSE;
399 if (!xdr_u_long(xdrs, &objp->lock_timeout))
400 return FALSE;
401 if (!xdr_string(xdrs, &objp->device, ~0))
402 return FALSE;
403 return TRUE;
404}
405
406bool_t xdr_Create_LinkResp(XDR *xdrs, Create_LinkResp *objp) {
407 if (!xdr_Device_ErrorCode(xdrs, &objp->error))
408 return FALSE;
409 if (!xdr_Device_Link(xdrs, &objp->lid))
410 return FALSE;
411 if (!xdr_u_short(xdrs, &objp->abortPort))
412 return FALSE;
413 if (!xdr_u_long(xdrs, &objp->maxRecvSize))
414 return FALSE;
415 return TRUE;
416}
417
418bool_t xdr_Device_ErrorCode(XDR *xdrs, Device_ErrorCode *objp) {
419 return xdr_long(xdrs, objp);
420}
421
422bool_t xdr_Device_Link(XDR *xdrs, Device_Link *objp) {
423 return xdr_long(xdrs, objp);
424}
425
426int vxi11_send(CLINK *clink, const char *cmd) {
427 return vxi11_send(clink, cmd, std::strlen(cmd));
428}
429
430int vxi11_send(CLINK *clink, const char *cmd, unsigned long len) {
431 return vxi11_send(clink->client, clink->link, cmd, len);
432}
433
434int vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd) {
435 return vxi11_send(client, link, cmd, std::strlen(cmd));
436}
437
438int vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd, unsigned long len) {
439 Device_WriteParms write_parms;
440 int bytes_left = static_cast<int>(len);
441 char *send_cmd = new char[len];
442 std::memcpy(send_cmd, cmd, len);
443
444 write_parms.lid = link->lid;
445 write_parms.io_timeout = VXI11_DEFAULT_TIMEOUT;
446 write_parms.lock_timeout = VXI11_DEFAULT_TIMEOUT;
447
448 // We can only write (link->maxRecvSize) bytes at a time
449 while (bytes_left > 0) {
450 Device_WriteResp write_resp{};
451
452 if (static_cast<unsigned int>(bytes_left) <= link->maxRecvSize) {
453 write_parms.flags = 8;
454 write_parms.data.data_len = bytes_left;
455 } else {
456 write_parms.flags = 0;
457 write_parms.data.data_len = (link->maxRecvSize > 0) ? link->maxRecvSize : 4096;
458 }
459 write_parms.data.data_val = send_cmd + (len - bytes_left);
460
461 if (device_write_1(&write_parms, &write_resp, client) != RPC_SUCCESS) {
462 delete[] send_cmd;
463 return -VXI11_NULL_WRITE_RESP;
464 }
465 if (write_resp.error != 0) {
466 std::fprintf(stderr, "vxi11_user: write error: %ld\n", write_resp.error);
467 delete[] send_cmd;
468 return -(write_resp.error);
469 }
470 bytes_left -= write_resp.size;
471 }
472
473 delete[] send_cmd;
474 return 0;
475}
476
477enum clnt_stat device_write_1(Device_WriteParms *argp, Device_WriteResp *clnt_res, CLIENT *clnt) {
478 return clnt_call(clnt, DEVICE_WRITE,
479 reinterpret_cast<xdrproc_t>(xdr_Device_WriteParms), reinterpret_cast<caddr_t>(argp),
480 reinterpret_cast<xdrproc_t>(xdr_Device_WriteResp), reinterpret_cast<caddr_t>(clnt_res),
481 TIMEOUT);
482}
483
484bool_t xdr_Device_WriteParms(XDR *xdrs, Device_WriteParms *objp) {
485 if (!xdr_Device_Link(xdrs, &objp->lid))
486 return FALSE;
487 if (!xdr_u_long(xdrs, &objp->io_timeout))
488 return FALSE;
489 if (!xdr_u_long(xdrs, &objp->lock_timeout))
490 return FALSE;
491 if (!xdr_Device_Flags(xdrs, &objp->flags))
492 return FALSE;
493 if (!xdr_bytes(xdrs, reinterpret_cast<char **>(&objp->data.data_val), reinterpret_cast<u_int *>(&objp->data.data_len), ~0))
494 return FALSE;
495 return TRUE;
496}
497
498bool_t xdr_Device_WriteResp(XDR *xdrs, Device_WriteResp *objp) {
499 if (!xdr_Device_ErrorCode(xdrs, &objp->error))
500 return FALSE;
501 if (!xdr_u_long(xdrs, &objp->size))
502 return FALSE;
503 return TRUE;
504}
505
506bool_t xdr_Device_Flags(XDR *xdrs, Device_Flags *objp) {
507 return xdr_long(xdrs, objp);
508}
509
510int vxi11_close_device(const char *ip, CLINK *clink) {
511 int ret;
512 int device_no = -1;
513
514 // Identify the device number based on IP
515 for (int l = 0; l < VXI11_MAX_CLIENTS; l++) {
516 if (std::strcmp(ip, VXI11_IP_ADDRESS[l]) == 0) {
517 device_no = l;
518 break;
519 }
520 }
521
522 if (device_no == -1) {
523 std::fprintf(stderr, "vxi11_close_device: error: No record of opening device with IP address %s.\n", ip);
524 ret = -4;
525 } else {
526 if (VXI11_LINK_COUNT[device_no] > 1) {
527 ret = vxi11_close_link(ip, clink->client, clink->link);
528 VXI11_LINK_COUNT[device_no]--;
529 } else {
530 ret = vxi11_close_device(ip, clink->client, clink->link);
531 }
532 }
533 return ret;
534}
535
536int vxi11_close_device(const char *ip, CLIENT *client, VXI11_LINK *link) {
537 int ret = vxi11_close_link(ip, client, link);
538 clnt_destroy(client);
539 return ret;
540}
541
542int vxi11_close_link(const char *ip, CLIENT *client, VXI11_LINK *link) {
543 Device_Error dev_error{};
544
545 if (destroy_link_1(&link->lid, &dev_error, client) != RPC_SUCCESS) {
546#ifdef __APPLE__
547 clnt_perror(client, reinterpret_cast<char *>(ip));
548#else
549 clnt_perror(client, ip);
550#endif
551 return -1;
552 }
553
554 return 0;
555}
556
557enum clnt_stat destroy_link_1(Device_Link *argp, Device_Error *clnt_res, CLIENT *clnt) {
558 return clnt_call(clnt, DESTROY_LINK,
559 reinterpret_cast<xdrproc_t>(xdr_Device_Link), reinterpret_cast<caddr_t>(argp),
560 reinterpret_cast<xdrproc_t>(xdr_Device_Error), reinterpret_cast<caddr_t>(clnt_res),
561 TIMEOUT);
562}
563
564bool_t xdr_Device_Error(XDR *xdrs, Device_Error *objp) {
565 return xdr_Device_ErrorCode(xdrs, &objp->error);
566}
567
568double vxi11_obtain_double_value(CLINK *clink, const char *cmd) {
569 return vxi11_obtain_double_value(clink, cmd, VXI11_READ_TIMEOUT);
570}
571
572double vxi11_obtain_double_value(CLINK *clink, const char *cmd, unsigned long timeout) {
573 char buf[50] = {0}; // 50=arbitrary length... more than enough for one number in ascii
574 if (vxi11_send_and_receive(clink, cmd, buf, sizeof(buf), timeout) != 0) {
575 std::fprintf(stderr, "Warning: Failed to obtain double value. Returning 0.0.\n");
576 return 0.0;
577 }
578 return std::strtod(buf, nullptr);
579}
580
581long vxi11_send_and_receive(CLINK *clink, const char *cmd, char *buf, unsigned long buf_len, unsigned long timeout) {
582 int ret;
583 long bytes_returned;
584 do {
585 ret = vxi11_send(clink, cmd);
586 if (ret != 0) {
587 if (ret != -VXI11_NULL_WRITE_RESP) {
588 std::fprintf(stderr, "Error: vxi11_send_and_receive: Could not send command '%s'. Return code: %d.\n", cmd, ret);
589 return -1;
590 } else {
591 std::printf("(Info: VXI11_NULL_WRITE_RESP in vxi11_send_and_receive, resending query)\n");
592 }
593 }
594
595 bytes_returned = vxi11_receive(clink, buf, buf_len, timeout);
596 if (bytes_returned <= 0) {
597 if (bytes_returned > -VXI11_NULL_READ_RESP) {
598 std::fprintf(stderr, "Error: vxi11_send_and_receive: Problem reading reply. Return code: %ld.\n", bytes_returned);
599 return -2;
600 } else {
601 std::printf("(Info: VXI11_NULL_READ_RESP in vxi11_send_and_receive, resending query)\n");
602 }
603 }
604 } while (bytes_returned == -VXI11_NULL_READ_RESP || ret == -VXI11_NULL_WRITE_RESP);
605 return 0;
606}
607
608long vxi11_receive(CLIENT *client, VXI11_LINK *link, char *buffer, unsigned long len, unsigned long timeout) {
609 Device_ReadParms read_parms;
610 Device_ReadResp read_resp{};
611 long curr_pos = 0;
612
613 read_parms.lid = link->lid;
614 read_parms.requestSize = len;
615 read_parms.io_timeout = timeout; // in ms
616 read_parms.lock_timeout = timeout; // in ms
617 read_parms.flags = 0;
618 read_parms.termChar = 0;
619
620 while (true) {
621 read_resp = Device_ReadResp{};
622 read_resp.data.data_val = buffer + curr_pos;
623 read_parms.requestSize = len - curr_pos; // Never request more total data than originally specified in len
624
625 if (device_read_1(&read_parms, &read_resp, client) != RPC_SUCCESS) {
626 return -VXI11_NULL_READ_RESP; // No data to read
627 }
628 if (read_resp.error != 0) {
629 std::fprintf(stderr, "vxi11_user: read error: %ld\n", read_resp.error);
630 return -(read_resp.error);
631 }
632
633 if ((unsigned long)(curr_pos + read_resp.data.data_len) <= len) {
634 curr_pos += read_resp.data.data_len;
635 }
636 if ((read_resp.reason & RCV_END_BIT) || (read_resp.reason & RCV_CHR_BIT)) {
637 break;
638 } else if ((unsigned long)curr_pos == len) {
639 std::fprintf(stderr, "vxi11_user: read error: Buffer too small. Read %ld bytes without hitting terminator.\n", curr_pos);
640 return -100;
641 }
642 }
643 return curr_pos; // Actual number of bytes received
644}
645
646long vxi11_receive(CLINK *clink, char *buffer, unsigned long len) {
647 return vxi11_receive(clink, buffer, len, VXI11_READ_TIMEOUT);
648}
649
650long vxi11_receive(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout) {
651 return vxi11_receive(clink->client, clink->link, buffer, len, timeout);
652}
653
654enum clnt_stat device_read_1(Device_ReadParms *argp, Device_ReadResp *clnt_res, CLIENT *clnt) {
655 return clnt_call(clnt, DEVICE_READ,
656 reinterpret_cast<xdrproc_t>(xdr_Device_ReadParms), reinterpret_cast<caddr_t>(argp),
657 reinterpret_cast<xdrproc_t>(xdr_Device_ReadResp), reinterpret_cast<caddr_t>(clnt_res),
658 TIMEOUT);
659}
660
661bool_t xdr_Device_ReadParms(XDR *xdrs, Device_ReadParms *objp) {
662#if defined(SOLARIS) && !defined(_LP64)
663 long *buf;
664#else
665 int32_t *buf;
666#endif
667
668 if (xdrs->x_op == XDR_ENCODE) {
669 if (!xdr_Device_Link(xdrs, &objp->lid))
670 return FALSE;
671 buf = reinterpret_cast<int32_t *>(XDR_INLINE(xdrs, 3 * BYTES_PER_XDR_UNIT));
672 if (buf == nullptr) {
673 if (!xdr_u_long(xdrs, &objp->requestSize))
674 return FALSE;
675 if (!xdr_u_long(xdrs, &objp->io_timeout))
676 return FALSE;
677 if (!xdr_u_long(xdrs, &objp->lock_timeout))
678 return FALSE;
679 } else {
680 IXDR_PUT_U_INT32(buf, objp->requestSize);
681 IXDR_PUT_U_INT32(buf, objp->io_timeout);
682 IXDR_PUT_U_INT32(buf, objp->lock_timeout);
683 }
684 if (!xdr_Device_Flags(xdrs, &objp->flags))
685 return FALSE;
686 if (!xdr_char(xdrs, &objp->termChar))
687 return FALSE;
688 return TRUE;
689 } else if (xdrs->x_op == XDR_DECODE) {
690 if (!xdr_Device_Link(xdrs, &objp->lid))
691 return FALSE;
692 buf = reinterpret_cast<int32_t *>(XDR_INLINE(xdrs, 3 * BYTES_PER_XDR_UNIT));
693 if (buf == nullptr) {
694 if (!xdr_u_long(xdrs, &objp->requestSize))
695 return FALSE;
696 if (!xdr_u_long(xdrs, &objp->io_timeout))
697 return FALSE;
698 if (!xdr_u_long(xdrs, &objp->lock_timeout))
699 return FALSE;
700 } else {
701 objp->requestSize = IXDR_GET_U_INT32(buf);
702 objp->io_timeout = IXDR_GET_U_INT32(buf);
703 objp->lock_timeout = IXDR_GET_U_INT32(buf);
704 }
705 if (!xdr_Device_Flags(xdrs, &objp->flags))
706 return FALSE;
707 if (!xdr_char(xdrs, &objp->termChar))
708 return FALSE;
709 return TRUE;
710 }
711
712 if (!xdr_Device_Link(xdrs, &objp->lid))
713 return FALSE;
714 if (!xdr_u_long(xdrs, &objp->requestSize))
715 return FALSE;
716 if (!xdr_u_long(xdrs, &objp->io_timeout))
717 return FALSE;
718 if (!xdr_u_long(xdrs, &objp->lock_timeout))
719 return FALSE;
720 if (!xdr_Device_Flags(xdrs, &objp->flags))
721 return FALSE;
722 if (!xdr_char(xdrs, &objp->termChar))
723 return FALSE;
724 return TRUE;
725}
726
727bool_t xdr_Device_ReadResp(XDR *xdrs, Device_ReadResp *objp) {
728 if (!xdr_Device_ErrorCode(xdrs, &objp->error))
729 return FALSE;
730 if (!xdr_long(xdrs, &objp->reason))
731 return FALSE;
732 if (!xdr_bytes(xdrs, reinterpret_cast<char **>(&objp->data.data_val), reinterpret_cast<u_int *>(&objp->data.data_len), ~0))
733 return FALSE;
734 return TRUE;
735}
736
737long vxi11_obtain_long_value(CLINK *clink, const char *cmd, unsigned long timeout) {
738 char buf[50] = {0}; // 50=arbitrary length... more than enough for one number in ascii
739 if (vxi11_send_and_receive(clink, cmd, buf, sizeof(buf), timeout) != 0) {
740 std::fprintf(stderr, "Warning: Failed to obtain long value. Returning 0.\n");
741 return 0;
742 }
743 return std::strtol(buf, nullptr, 10);
744}
745
746// Lazy wrapper function with default read timeout
747long vxi11_obtain_long_value(CLINK *clink, const char *cmd) {
748 return vxi11_obtain_long_value(clink, cmd, VXI11_READ_TIMEOUT);
749}
750
751long vxi11_receive_data_block(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout) {
752 // Assuming the maximum length of the header is 11 (#9 + 9 digits)
753 unsigned long necessary_buffer_size = len + 12;
754 char *in_buffer = new char[necessary_buffer_size];
755 long ret = vxi11_receive(clink, in_buffer, necessary_buffer_size, timeout);
756 if (ret < 0) {
757 delete[] in_buffer;
758 return ret;
759 }
760 if (in_buffer[0] != '#') {
761 std::fprintf(stderr, "vxi11_user: data block error: Data block does not begin with '#'.\n");
762 std::fprintf(stderr, "First 20 characters received were: '");
763 for (size_t l = 0; l < 20 && l < (size_t)necessary_buffer_size; l++) {
764 std::fprintf(stderr, "%c", in_buffer[l]);
765 }
766 std::fprintf(stderr, "'\n");
767 delete[] in_buffer;
768 return -3;
769 }
770
771 // First, find out how many digits
772 int ndigits = 0;
773 sscanf(in_buffer, "#%1d", &ndigits);
774
775 // Some instruments, if there is a problem acquiring the data, return only "#0"
776 if (ndigits > 0) {
777 // Convert the next <ndigits> bytes into an unsigned long
778 char scan_cmd[20];
779 std::snprintf(scan_cmd, sizeof(scan_cmd), "#%%1d%%%dlu", ndigits);
780 unsigned long returned_bytes = 0;
781 sscanf(in_buffer, scan_cmd, &ndigits, &returned_bytes);
782 std::memcpy(buffer, in_buffer + (ndigits + 2), returned_bytes);
783 delete[] in_buffer;
784 return static_cast<long>(returned_bytes);
785 } else {
786 delete[] in_buffer;
787 return 0;
788 }
789}