vxi11 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,639 @@
1
+ /* vxi11_user.cc
2
+ * Copyright (C) 2006 Steve D. Sharples
3
+ *
4
+ * User library for opening, closing, sending to and receiving from
5
+ * a device enabled with the VXI11 RPC ethernet protocol. Uses the files
6
+ * generated by rpcgen vxi11.x.
7
+ *
8
+ * This program is free software; you can redistribute it and/or
9
+ * modify it under the terms of the GNU General Public License
10
+ * as published by the Free Software Foundation; either version 2
11
+ * of the License, or (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program; if not, write to the Free Software
20
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21
+ *
22
+ * The author's email address is steve.sharples@nottingham.ac.uk
23
+ */
24
+
25
+ #include "vxi11_user.h"
26
+
27
+ /*****************************************************************************
28
+ * GENERAL NOTES
29
+ *****************************************************************************
30
+ *
31
+ * There are four functions at the heart of this library:
32
+ *
33
+ * int vxi11_open_device(char *ip, CLIENT **client, VXI11_LINK **link)
34
+ * int vxi11_close_device(char *ip, CLIENT *client, VXI11_LINK *link)
35
+ * int vxi11_send(CLIENT *client, VXI11_LINK *link, char *cmd, unsigned long len)
36
+ * long vxi11_receive(CLIENT *client, VXI11_LINK *link, char *buffer, unsigned long len, unsigned long timeout)
37
+ *
38
+ * Note that all 4 of these use separate client and link structures. All the
39
+ * other functions are built on these four core functions, and the first layer
40
+ * of abstraction is to combine the CLIENT and VXI11_LINK structures into a
41
+ * single entity, which I've called a CLINK. For the send and receive
42
+ * functions, this is just a simple wrapper. For the open and close functions
43
+ * it's a bit more complicated, because we somehow have to keep track of
44
+ * whether we've already opened a device with the same IP address before (in
45
+ * which case we need to recycle a previously created client), or whether
46
+ * we've still got any other links to a given IP address left when we are
47
+ * asked to close a clink (in which case we can sever the link, but have to
48
+ * keep the client open). This is so the person using this library from
49
+ * userland does not have to keep track of whether they are talking to a
50
+ * different physical instrument or not each time they establish a connection.
51
+ *
52
+ * So the base functions that the user will probably want to use are:
53
+ *
54
+ * int vxi11_open_device(char *ip, CLINK *clink)
55
+ * int vxi11_close_device(char *ip, CLINK *clink)
56
+ * int vxi11_send(CLINK *clink, char *cmd, unsigned long len)
57
+ * --- or --- (if sending just text)
58
+ * int vxi11_send(CLINK *clink, char *cmd)
59
+ * long vxi11_receive(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout)
60
+ *
61
+ * There are then useful (to me, anyway) more specific functions built on top
62
+ * of these:
63
+ *
64
+ * int vxi11_send_data_block(CLINK *clink, char *cmd, char *buffer, unsigned long len)
65
+ * long vxi11_receive_data_block(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout)
66
+ * long vxi11_send_and_receive(CLINK *clink, char *cmd, char *buf, unsigned long buf_len, unsigned long timeout)
67
+ * long vxi11_obtain_long_value(CLINK *clink, char *cmd, unsigned long timeout)
68
+ * double vxi11_obtain_double_value(CLINK *clink, char *cmd, unsigned long timeout)
69
+ *
70
+ * (then there are some shorthand wrappers for the above without specifying
71
+ * the timeout due to sheer laziness---explore yourself)
72
+ */
73
+
74
+
75
+ /* Global variables. Keep track of multiple links per client. We need this
76
+ * because:
77
+ * - we'd like the library to be able to cope with multiple links to a given
78
+ * client AND multiple links to multiple clients
79
+ * - we'd like to just refer to a client/link ("clink") as a single
80
+ * entity from user land, we don't want to worry about different
81
+ * initialisation procedures, depending on whether it's an instrument
82
+ * with the same IP address or not
83
+ */
84
+ char VXI11_IP_ADDRESS[VXI11_MAX_CLIENTS][20];
85
+ CLIENT *VXI11_CLIENT_ADDRESS[VXI11_MAX_CLIENTS];
86
+ int VXI11_DEVICE_NO = 0;
87
+ int VXI11_LINK_COUNT[VXI11_MAX_CLIENTS];
88
+
89
+ /*****************************************************************************
90
+ * KEY USER FUNCTIONS - USE THESE FROM YOUR PROGRAMS OR INSTRUMENT LIBRARIES *
91
+ *****************************************************************************/
92
+
93
+ /* OPEN FUNCTIONS *
94
+ * ============== */
95
+
96
+ /* Use this function from user land to open a device and create a link. Can be
97
+ * used multiple times for the same device (the library will keep track).*/
98
+ int vxi11_open_device(const char *ip, CLINK *clink, char *device) {
99
+ int ret;
100
+ int l;
101
+ int device_no=-1;
102
+
103
+ // printf("before doing anything, clink->link = %ld\n", clink->link);
104
+ /* Have a look to see if we've already initialised an instrument with
105
+ * this IP address */
106
+ for (l=0; l<VXI11_MAX_CLIENTS; l++){
107
+ if (strcmp(ip,VXI11_IP_ADDRESS[l]) == 0 ) {
108
+ device_no=l;
109
+ // printf("Open function, search, found ip address %s, device no %d\n",ip,device_no);
110
+ }
111
+ }
112
+
113
+ /* Couldn't find a match, must be a new IP address */
114
+ if (device_no < 0) {
115
+ /* Uh-oh, we're out of storage space. Increase the #define
116
+ * for VXI11_MAX_CLIENTS in vxi11_user.h */
117
+ if (VXI11_DEVICE_NO >= VXI11_MAX_CLIENTS) {
118
+ printf("Error: maximum of %d clients allowed\n",VXI11_MAX_CLIENTS);
119
+ ret = -VXI11_MAX_CLIENTS;
120
+ }
121
+ /* Create a new client, keep a note of where the client pointer
122
+ * is, for this IP address. Because it's a new client, this
123
+ * must be link number 1. Keep track of how many devices we've
124
+ * opened so we don't run out of storage space. */
125
+ else {
126
+ ret = vxi11_open_device(ip, &(clink->client), &(clink->link), device);
127
+ strncpy(VXI11_IP_ADDRESS[VXI11_DEVICE_NO],ip,20);
128
+ VXI11_CLIENT_ADDRESS[VXI11_DEVICE_NO] = clink->client;
129
+ VXI11_LINK_COUNT[VXI11_DEVICE_NO]=1;
130
+ // printf("Open function, could not find ip address %s.\n",ip);
131
+ // printf("So now, VXI11_IP_ADDRESS[%d]=%s,\n",VXI11_DEVICE_NO,VXI11_IP_ADDRESS[VXI11_DEVICE_NO]);
132
+ // printf("VXI11_CLIENT_ADDRESS[%d]=%ld,\n",VXI11_DEVICE_NO,VXI11_CLIENT_ADDRESS[VXI11_DEVICE_NO]);
133
+ // printf(" clink->client=%ld,\n",clink->client);
134
+ // printf("VXI11_LINK_COUNT[%d]=%d.\n",VXI11_DEVICE_NO,VXI11_LINK_COUNT[VXI11_DEVICE_NO]);
135
+ VXI11_DEVICE_NO++;
136
+ }
137
+ }
138
+ /* already got a client for this IP address */
139
+ else {
140
+ /* Copy the client pointer address. Just establish a new link
141
+ * (not a new client). Add one to the link count */
142
+ clink->client = VXI11_CLIENT_ADDRESS[device_no];
143
+ ret = vxi11_open_link(ip, &(clink->client), &(clink->link), device);
144
+ // printf("Found an ip address, copying client from VXI11_CLIENT_ADDRESS[%d]\n",device_no);
145
+ VXI11_LINK_COUNT[device_no]++;
146
+ // printf("Have just incremented VXI11_LINK_COUNT[%d], it's now %d\n",device_no,VXI11_LINK_COUNT[device_no]);
147
+ }
148
+ // printf("after creating link, clink->link = %ld\n", clink->link);
149
+ return ret;
150
+ }
151
+
152
+ /* This is a wrapper function, used for the situations where there is only one
153
+ * "device" per client. This is the case for most (if not all) VXI11
154
+ * instruments; however, it is _not_ the case for devices such as LAN to GPIB
155
+ * gateways. These are single clients that communicate to many instruments
156
+ * (devices). In order to differentiate between them, we need to pass a device
157
+ * name. This gets used in the vxi11_open_link() fn, as the link_parms.device
158
+ * value. */
159
+ int vxi11_open_device(const char *ip, CLINK *clink) {
160
+ char device[6];
161
+ strncpy(device,"inst0",6);
162
+ return vxi11_open_device(ip, clink, device);
163
+ }
164
+
165
+
166
+
167
+ /* CLOSE FUNCTION *
168
+ * ============== */
169
+
170
+ /* Use this function from user land to close a device and/or sever a link. Can
171
+ * be used multiple times for the same device (the library will keep track).*/
172
+ int vxi11_close_device(const char *ip, CLINK *clink) {
173
+ int l,ret;
174
+ int device_no = -1;
175
+
176
+ /* Which instrument are we referring to? */
177
+ for (l=0; l<VXI11_MAX_CLIENTS; l++){
178
+ if (strcmp(ip,VXI11_IP_ADDRESS[l]) == 0 ) {
179
+ device_no=l;
180
+ }
181
+ }
182
+ /* Something's up if we can't find the IP address! */
183
+ if (device_no == -1) {
184
+ printf("vxi11_close_device: error: I have no record of you ever opening device\n");
185
+ printf(" with IP address %s\n",ip);
186
+ ret = -4;
187
+ }
188
+ else { /* Found the IP, there's more than one link to that instrument,
189
+ * so keep track and just close the link */
190
+ if (VXI11_LINK_COUNT[device_no] > 1 ) {
191
+ ret = vxi11_close_link(ip,clink->client, clink->link);
192
+ VXI11_LINK_COUNT[device_no]--;
193
+ }
194
+ /* Found the IP, it's the last link, so close the device (link
195
+ * AND client) */
196
+ else {
197
+ ret = vxi11_close_device(ip, clink->client, clink->link);
198
+ /* Remove the IP address, so that if we re-open the same device
199
+ * we do it properly */
200
+ memset(VXI11_IP_ADDRESS[device_no], 0, 20);
201
+ }
202
+ }
203
+ return ret;
204
+ }
205
+
206
+
207
+ /* SEND FUNCTIONS *
208
+ * ============== */
209
+
210
+ /* A _lot_ of the time we are sending text strings, and can safely rely on
211
+ * strlen(cmd). */
212
+ int vxi11_send(CLINK *clink, const char *cmd) {
213
+ return vxi11_send(clink, cmd, strlen(cmd));
214
+ }
215
+
216
+ /* We still need the version of the function where the length is set explicitly
217
+ * though, for when we are sending fixed length data blocks. */
218
+ int vxi11_send(CLINK *clink, const char *cmd, unsigned long len) {
219
+ return vxi11_send(clink->client, clink->link, cmd, len);
220
+ }
221
+
222
+
223
+ /* RECEIVE FUNCTIONS *
224
+ * ================= */
225
+
226
+ /* Lazy wrapper for when I can't be bothered to specify a read timeout */
227
+ long vxi11_receive(CLINK *clink, char *buffer, unsigned long len) {
228
+ return vxi11_receive(clink, buffer, len, VXI11_READ_TIMEOUT);
229
+ }
230
+
231
+ long vxi11_receive(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout) {
232
+ return vxi11_receive(clink->client, clink->link, buffer, len, timeout);
233
+ }
234
+
235
+
236
+
237
+ /*****************************************************************************
238
+ * USEFUL ADDITIONAL HIGHER LEVER USER FUNCTIONS - USE THESE FROM YOUR *
239
+ * PROGRAMS OR INSTRUMENT LIBRARIES *
240
+ *****************************************************************************/
241
+
242
+ /* SEND FIXED LENGTH DATA BLOCK FUNCTION *
243
+ * ===================================== */
244
+ int vxi11_send_data_block(CLINK *clink, const char *cmd, char *buffer, unsigned long len) {
245
+ char *out_buffer;
246
+ int cmd_len=strlen(cmd);
247
+ int ret;
248
+
249
+ out_buffer=new char[cmd_len+10+len];
250
+ sprintf(out_buffer,"%s#8%08lu",cmd,len);
251
+ memcpy(out_buffer+cmd_len+10,buffer,(unsigned long) len);
252
+ ret = vxi11_send(clink, out_buffer, (unsigned long) (cmd_len+10+len));
253
+ delete[] out_buffer;
254
+ return ret;
255
+ }
256
+
257
+
258
+ /* RECEIVE FIXED LENGTH DATA BLOCK FUNCTION *
259
+ * ======================================== */
260
+
261
+ /* This function reads a response in the form of a definite-length block, such
262
+ * as when you ask for waveform data. The data is returned in the following
263
+ * format:
264
+ * #800001000<1000 bytes of data>
265
+ * ||\______/
266
+ * || |
267
+ * || \---- number of bytes of data
268
+ * |\--------- number of digits that follow (in this case 8, with leading 0's)
269
+ * \---------- always starts with #
270
+ */
271
+ long vxi11_receive_data_block(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout) {
272
+ /* I'm not sure what the maximum length of this header is, I'll assume it's
273
+ * 11 (#9 + 9 digits) */
274
+ unsigned long necessary_buffer_size;
275
+ char *in_buffer;
276
+ int ret;
277
+ int ndigits;
278
+ unsigned long returned_bytes;
279
+ int l;
280
+ char scan_cmd[20];
281
+ necessary_buffer_size=len+12;
282
+ in_buffer=new char[necessary_buffer_size];
283
+ ret=vxi11_receive(clink, in_buffer, necessary_buffer_size, timeout);
284
+ if (ret < 0) return ret;
285
+ if (in_buffer[0] != '#') {
286
+ printf("vxi11_user: data block error: data block does not begin with '#'\n");
287
+ printf("First 20 characters received were: '");
288
+ for(l=0;l<20;l++) {
289
+ printf("%c",in_buffer[l]);
290
+ }
291
+ printf("'\n");
292
+ return -3;
293
+ }
294
+
295
+ /* first find out how many digits */
296
+ sscanf(in_buffer,"#%1d",&ndigits);
297
+ /* some instruments, if there is a problem acquiring the data, return only "#0" */
298
+ if (ndigits > 0) {
299
+ /* now that we know, we can convert the next <ndigits> bytes into an unsigned long */
300
+ sprintf(scan_cmd,"#%%1d%%%dlu",ndigits);
301
+ sscanf(in_buffer,scan_cmd,&ndigits,&returned_bytes);
302
+ memcpy(buffer, in_buffer+(ndigits+2), returned_bytes);
303
+ delete[] in_buffer;
304
+ return (long) returned_bytes;
305
+ }
306
+ else return 0;
307
+ }
308
+
309
+
310
+ /* SEND AND RECEIVE FUNCTION *
311
+ * ========================= */
312
+
313
+ /* This is mainly a useful function for the overloaded vxi11_obtain_value()
314
+ * fn's, but is also handy and useful for user and library use */
315
+ long vxi11_send_and_receive(CLINK *clink, const char *cmd, char *buf, unsigned long buf_len, unsigned long timeout) {
316
+ int ret;
317
+ long bytes_returned;
318
+ do {
319
+ ret = vxi11_send(clink, cmd);
320
+ if (ret != 0) {
321
+ if (ret != -VXI11_NULL_WRITE_RESP) {
322
+ printf("Error: vxi11_send_and_receive: could not send cmd.\n");
323
+ printf(" The function vxi11_send returned %d. ",ret);
324
+ return -1;
325
+ }
326
+ else printf("(Info: VXI11_NULL_WRITE_RESP in vxi11_send_and_receive, resending query)\n");
327
+ }
328
+
329
+ bytes_returned = vxi11_receive(clink, buf, buf_len, timeout);
330
+ if (bytes_returned <= 0) {
331
+ if (bytes_returned >-VXI11_NULL_READ_RESP) {
332
+ printf("Error: vxi11_send_and_receive: problem reading reply.\n");
333
+ printf(" The function vxi11_receive returned %ld. ",bytes_returned);
334
+ return -2;
335
+ }
336
+ else printf("(Info: VXI11_NULL_READ_RESP in vxi11_send_and_receive, resending query)\n");
337
+ }
338
+ } while (bytes_returned == -VXI11_NULL_READ_RESP || ret == -VXI11_NULL_WRITE_RESP);
339
+ return bytes_returned;
340
+ }
341
+
342
+
343
+ /* FUNCTIONS TO RETURN A LONG INTEGER VALUE SENT AS RESPONSE TO A QUERY *
344
+ * ==================================================================== */
345
+ long vxi11_obtain_long_value(CLINK *clink, const char *cmd, unsigned long timeout) {
346
+ char buf[50]; /* 50=arbitrary length... more than enough for one number in ascii */
347
+ memset(buf, 0, 50);
348
+ if (vxi11_send_and_receive(clink, cmd, buf, 50, timeout) != 0) {
349
+ printf("Returning 0\n");
350
+ return 0;
351
+ }
352
+ return strtol(buf, (char **)NULL, 10);
353
+ }
354
+
355
+ /* Lazy wrapper function with default read timeout */
356
+ long vxi11_obtain_long_value(CLINK *clink, const char *cmd) {
357
+ return vxi11_obtain_long_value(clink, cmd, VXI11_READ_TIMEOUT);
358
+ }
359
+
360
+
361
+ /* FUNCTIONS TO RETURN A DOUBLE FLOAT VALUE SENT AS RESPONSE TO A QUERY *
362
+ * ==================================================================== */
363
+ double vxi11_obtain_double_value(CLINK *clink, const char *cmd, unsigned long timeout) {
364
+ char buf[50]; /* 50=arbitrary length... more than enough for one number in ascii */
365
+ double val;
366
+ memset(buf, 0, 50);
367
+ if (vxi11_send_and_receive(clink, cmd, buf, 50, timeout) != 0) {
368
+ printf("Returning 0.0\n");
369
+ return 0.0;
370
+ }
371
+ val = strtod(buf, (char **)NULL);
372
+ return val;
373
+ }
374
+
375
+ /* Lazy wrapper function with default read timeout */
376
+ double vxi11_obtain_double_value(CLINK *clink, const char *cmd) {
377
+ return vxi11_obtain_double_value(clink, cmd, VXI11_READ_TIMEOUT);
378
+ }
379
+
380
+
381
+ /*****************************************************************************
382
+ * CORE FUNCTIONS - YOU SHOULDN'T NEED TO USE THESE FROM YOUR PROGRAMS OR *
383
+ * INSTRUMENT LIBRARIES *
384
+ *****************************************************************************/
385
+
386
+ /* OPEN FUNCTIONS *
387
+ * ============== */
388
+ int vxi11_open_device(const char *inputip, CLIENT **client, VXI11_LINK **link, char *device) {
389
+
390
+ #ifdef __APPLE__
391
+ char ip[strlen(inputip)];
392
+ strcpy(ip, inputip);
393
+ #else
394
+ const char *ip = inputip;
395
+ #endif
396
+ char tmp_tcp[] = "tcp";
397
+ *client = clnt_create(ip, DEVICE_CORE, DEVICE_CORE_VERSION, tmp_tcp);
398
+
399
+ if (*client == NULL) {
400
+ clnt_pcreateerror(ip);
401
+ return -1;
402
+ }
403
+
404
+ return vxi11_open_link(ip, client, link, device);
405
+ }
406
+
407
+ int vxi11_open_link(const char *inputip, CLIENT **client, VXI11_LINK **link, char *device) {
408
+
409
+ Create_LinkParms link_parms;
410
+ #ifdef __APPLE__
411
+ char ip[strlen(inputip)];
412
+ strcpy(ip, inputip);
413
+ #else
414
+ const char *ip = inputip;
415
+ #endif
416
+
417
+ /* Set link parameters */
418
+ link_parms.clientId = (long) *client;
419
+ link_parms.lockDevice = 0;
420
+ link_parms.lock_timeout = VXI11_DEFAULT_TIMEOUT;
421
+ link_parms.device = device;
422
+
423
+ *link = (Create_LinkResp *) calloc(1, sizeof(Create_LinkResp));
424
+
425
+ #ifdef __APPLE__
426
+ Create_LinkResp * tmp;
427
+ if ((tmp = create_link_1(&link_parms, *client)) == NULL) {
428
+ #else
429
+ if (create_link_1(&link_parms, *link, *client) != RPC_SUCCESS) {
430
+ #endif
431
+ clnt_perror(*client, ip);
432
+ return -2;
433
+ }
434
+ #ifdef __APPLE__
435
+ memcpy(*link, tmp, sizeof(*tmp));
436
+ #endif
437
+ return 0;
438
+ }
439
+
440
+
441
+ /* CLOSE FUNCTIONS *
442
+ * =============== */
443
+ int vxi11_close_device(const char *ip, CLIENT *client, VXI11_LINK *link) {
444
+ int ret;
445
+
446
+ ret = vxi11_close_link(ip, client, link);
447
+
448
+ clnt_destroy(client);
449
+
450
+ return ret;
451
+ }
452
+
453
+ int vxi11_close_link(const char *inputip, CLIENT *client, VXI11_LINK *link) {
454
+ Device_Error dev_error;
455
+ #ifdef __APPLE__
456
+ char ip[strlen(inputip)];
457
+ strcpy(ip, inputip);
458
+ #else
459
+ const char *ip = inputip;
460
+ #endif
461
+
462
+
463
+ memset(&dev_error, 0, sizeof(dev_error));
464
+
465
+ #ifdef __APPLE__
466
+ if (destroy_link_1(&link->lid, client) == NULL) {
467
+ #else
468
+ if (destroy_link_1(&link->lid, &dev_error, client) != RPC_SUCCESS) {
469
+ #endif
470
+ clnt_perror(client,ip);
471
+ return -1;
472
+ }
473
+
474
+ return 0;
475
+ }
476
+
477
+
478
+ /* SEND FUNCTIONS *
479
+ * ============== */
480
+
481
+ /* A _lot_ of the time we are sending text strings, and can safely rely on
482
+ * strlen(cmd). */
483
+ int vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd) {
484
+ return vxi11_send(client, link, cmd, strlen(cmd));
485
+ }
486
+
487
+ /* We still need the version of the function where the length is set explicitly
488
+ * though, for when we are sending fixed length data blocks. */
489
+ int vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd, unsigned long len) {
490
+ Device_WriteParms write_parms;
491
+ unsigned int bytes_left = len;
492
+ char *send_cmd;
493
+
494
+ send_cmd = new char[len];
495
+ memcpy(send_cmd, cmd, len);
496
+
497
+ write_parms.lid = link->lid;
498
+ write_parms.io_timeout = VXI11_DEFAULT_TIMEOUT;
499
+ write_parms.lock_timeout = VXI11_DEFAULT_TIMEOUT;
500
+
501
+ /* We can only write (link->maxRecvSize) bytes at a time, so we sit in a loop,
502
+ * writing a chunk at a time, until we're done. */
503
+
504
+ do {
505
+ Device_WriteResp write_resp;
506
+ memset(&write_resp, 0, sizeof(write_resp));
507
+
508
+ if (bytes_left <= link->maxRecvSize) {
509
+ write_parms.flags = 8;
510
+ write_parms.data.data_len = bytes_left;
511
+ }
512
+ else {
513
+ write_parms.flags = 0;
514
+ /* We need to check that maxRecvSize is a sane value (ie >0). Believe it
515
+ * or not, on some versions of Agilent Infiniium scope firmware the scope
516
+ * returned "0", which breaks Rule B.6.3 of the VXI-11 protocol. Nevertheless
517
+ * we need to catch this, otherwise the program just hangs. */
518
+ if (link->maxRecvSize > 0) {
519
+ write_parms.data.data_len = link->maxRecvSize;
520
+ }
521
+ else {
522
+ write_parms.data.data_len = 4096; /* pretty much anything should be able to cope with 4kB */
523
+ }
524
+ }
525
+ write_parms.data.data_val = send_cmd + (len - bytes_left);
526
+
527
+ #ifdef __APPLE__
528
+ Device_WriteResp *tmp;
529
+ if((tmp = device_write_1(&write_parms, client)) == NULL) {
530
+ #else
531
+ if(device_write_1(&write_parms, &write_resp, client) != RPC_SUCCESS) {
532
+ #endif
533
+ delete[] send_cmd;
534
+ return -VXI11_NULL_WRITE_RESP; /* The instrument did not acknowledge the write, just completely
535
+ dropped it. There was no vxi11 comms error as such, the
536
+ instrument is just being rude. Usually occurs when the instrument
537
+ is busy. If we don't check this first, then the following
538
+ line causes a seg fault */
539
+ }
540
+ #ifdef __APPLE__
541
+ memcpy(&write_resp, tmp, sizeof(*tmp));
542
+ #endif
543
+ if (write_resp.error != 0) {
544
+ printf("vxi11_user: write error: %d\n", (int)write_resp.error);
545
+ delete[] send_cmd;
546
+ return -(write_resp.error);
547
+ }
548
+ bytes_left -= write_resp.size;
549
+ } while (bytes_left > 0);
550
+
551
+ delete[] send_cmd;
552
+ return 0;
553
+ }
554
+
555
+
556
+ /* RECEIVE FUNCTIONS *
557
+ * ================= */
558
+
559
+ // It appeared that this function wasn't correctly dealing with more data available than specified in len.
560
+ // This patch attempts to fix this issue. RDP 2007/8/13
561
+
562
+ /* wrapper, for default timeout */ long vxi11_receive(CLIENT *client, VXI11_LINK *link, char *buffer, unsigned long len) { return vxi11_receive(client, link, buffer, len, VXI11_READ_TIMEOUT);
563
+ }
564
+
565
+ #define RCV_END_BIT 0x04 // An end indicator has been read
566
+ #define RCV_CHR_BIT 0x02 // A termchr is set in flags and a character which matches termChar is transferred
567
+ #define RCV_REQCNT_BIT 0x01 // requestSize bytes have been transferred. This includes a request size of zero.
568
+
569
+ long vxi11_receive(CLIENT *client, VXI11_LINK *link, char *buffer, unsigned long len, unsigned long timeout) {
570
+ Device_ReadParms read_parms;
571
+ Device_ReadResp read_resp;
572
+ unsigned long curr_pos = 0;
573
+
574
+ read_parms.lid = link->lid;
575
+ read_parms.requestSize = len;
576
+ read_parms.io_timeout = timeout; /* in ms */
577
+ read_parms.lock_timeout = timeout; /* in ms */
578
+ read_parms.flags = 0;
579
+ read_parms.termChar = 0;
580
+
581
+ do {
582
+ memset(&read_resp, 0, sizeof(read_resp));
583
+
584
+ read_parms.requestSize = len - curr_pos; // Never request more total data than originally specified in len
585
+
586
+ #ifdef __APPLE__
587
+ Device_ReadResp *tmp;
588
+ if((tmp = device_read_1(&read_parms, client)) == NULL) {
589
+ #else
590
+ read_resp.data.data_val = buffer + curr_pos;
591
+ if(device_read_1(&read_parms, &read_resp, client) != RPC_SUCCESS) {
592
+ #endif
593
+ return -VXI11_NULL_READ_RESP; /* there is nothing to read. Usually occurs after sending a query
594
+ which times out on the instrument. If we don't check this first,
595
+ then the following line causes a seg fault */
596
+ }
597
+ #ifdef __APPLE__
598
+ memcpy(&read_resp, tmp, sizeof(*tmp));
599
+ memcpy(buffer + curr_pos, read_resp.data.data_val, read_resp.data.data_len);
600
+ #endif
601
+ if (read_resp.error != 0) {
602
+ /* Read failed for reason specified in error code.
603
+ * (From published VXI-11 protocol, section B.5.2)
604
+ * 0 no error
605
+ * 1 syntax error
606
+ * 3 device not accessible
607
+ * 4 invalid link identifier
608
+ * 5 parameter error
609
+ * 6 channel not established
610
+ * 8 operation not supported
611
+ * 9 out of resources
612
+ * 11 device locked by another link
613
+ * 12 no lock held by this link
614
+ * 15 I/O timeout
615
+ * 17 I/O error
616
+ * 21 invalid address
617
+ * 23 abort
618
+ * 29 channel already established
619
+ */
620
+
621
+ printf("vxi11_user: read error: %d\n", (int)read_resp.error);
622
+ return -(read_resp.error);
623
+ }
624
+
625
+ if((curr_pos + read_resp.data.data_len) <= len) {
626
+ curr_pos += read_resp.data.data_len;
627
+ }
628
+ if( (read_resp.reason & RCV_END_BIT) || (read_resp.reason & RCV_CHR_BIT) ) {
629
+ break;
630
+ }
631
+ else if( curr_pos == len ) {
632
+ printf("xvi11_user: read error: buffer too small. Read %d bytes without hitting terminator.\n", (int)curr_pos );
633
+ return -100;
634
+ }
635
+ } while(1);
636
+ return (curr_pos); /*actual number of bytes received*/
637
+
638
+ }
639
+