Subversion Repositories f9daq

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
146 f9daq 1
/* Revision history: */
2
/* $Id: vxi11_user.cc,v 1.17 2008/10/20 07:59:54 sds Exp $ */
3
/*
4
 * $Log: vxi11_user.cc,v $
5
 * Revision 1.17  2008/10/20 07:59:54  sds
6
 * Removed Manfred's surname at his request from the comments/acknowledgments.
7
 *
8
 * Revision 1.16  2008/09/03 14:30:13  sds
9
 * added sanity check for link->maxRecvSize to make sure it's >0.
10
 * This got around a bug in some versions of the Agilent Infiniium
11
 * scope software.
12
 *
13
 * Revision 1.15  2007/10/30 12:55:15  sds
14
 * changed the erroneous strncpy() to memcpy() in vxi11_send,
15
 * as we could be sending binary data (not just strings).
16
 *
17
 * Revision 1.14  2007/10/30 12:46:48  sds
18
 * changed a lot of char *'s to const char *'s in an attempt to get
19
 * rid of pedantic gcc compiler warnings.
20
 *
21
 * Revision 1.13  2007/10/09 08:42:57  sds
22
 * Minor change to vxi11_receive_data_block(), this fn now
23
 * copes with instruments that return just "#0" (for whatever
24
 * reason). Suggestion by Jarek Sadowski.
25
 *
26
 * Revision 1.12  2007/08/31 10:32:39  sds
27
 * Bug fix in vxi11_receive(), to ensure that no more than "len"
28
 * bytes are ever received (and so avoiding a segmentation fault).
29
 * This was a bug introduced in release 1.04 (RCS 1.10) whilst
30
 * making some other changes to the vxi11_receive() fn. Many thanks
31
 * to Rob Penny for spotting the bug and providing a patch.
32
 *
33
 * Revision 1.11  2007/07/10 13:49:18  sds
34
 * Changed the vxi11_open_device() fn to accept a third argument, char *device.
35
 * This gets passed to the core vxi11_open_device() fn (the one that deals with
36
 * separate clients and links), and the core vxi11_open_link() fn; these two
37
 * core functions have also had an extra parameter added accordingly. In order
38
 * to not break the API, a wrapper function is provided in the form of the
39
 * original vxi11_open_device() fn, that just takes 2 arguments
40
 * (char *ip, CLINK *clink), this then passes "inst0" as the device argument.
41
 * Backwards-compatible wrappers for the core functions have NOT been provided.
42
 * These are generally not used from userland anyway. Hopefully this won't
43
 * upset anyone!
44
 *
45
 * Revision 1.10  2007/07/10 11:12:12  sds
46
 * Patches provided by Robert Larice. This basically solves the problem
47
 * of having to recreate a link each time you change client. In the words
48
 * of Robert:
49
 *
50
 * ---------
51
 * In the source code there were some strange comments, suggesting
52
 * you had trouble to get more than one link working.
53
 *
54
 * I think thats caused by some misuse of the rpcgen generated subroutines.
55
 * 1) those rpcgen generated *_1 functions returned pointers to
56
 *      statically allocated temporary structs.
57
 *    those where meant to be instantly copied to the user's space,
58
 *    which wasn't done, thus instead of
59
 *        Device_ReadResp  *read_resp;
60
 *        read_resp = device_read_1(...)
61
 *    one should have written someting like:
62
 *        Device_ReadResp  *read_resp;
63
 *        read_resp = malloc(...)
64
 *        memcpy(read_resp, device_read_1(...), ...)
65
 * 2) but a better fix is to use the rpcgen -M Flag
66
 *   which allows to pass the memory space as a third argument
67
 * so one can write
68
 *       Device_ReadResp  *read_resp;
69
 *        read_resp = malloc(...)
70
 *        device_read_1(..., read_resp, ...)
71
 *      furthermore this is now automatically thread save
72
 * 3) the rpcgen function device_read_1
73
 * expects a target buffer to be passed via read_resp
74
 * which was not done.
75
 * 4) the return value of vxi11_receive() was computed incorrectly
76
 * 5) minor,  Makefile typo's
77
 * ---------
78
 * So big thanks to Robert Larice for the patch! I've tested it
79
 * (briefly) on more than one scope, and with multiple links per
80
 * client, and it seems to work fine. I've thus removed all references
81
 * to VXI11_ENABLE_MULTIPLE_CLIENTS, and deleted the vxi11_open_link()
82
 * function that WASN'T passed an ip address (that was only called
83
 * from the vxi11_send() fn, when there was more than one client).
84
 *
85
 * Revision 1.9  2006/12/08 12:06:58  ijc
86
 * Basically the same changes as revision 1.8, except replace all
87
 * references to "vxi11_receive" with "vxi11_send" and all references
88
 * to "-VXI11_NULL_READ_RESP" with "-VXI11_NULL_WRITE_RESP".
89
 *
90
 * Revision 1.8  2006/12/07 12:22:20  sds
91
 * Couple of changes, related.
92
 * (1) added extra check in vxi11_receive() to see if read_resp==NULL.
93
 * read_resp can apparently be NULL if (eg) you send an instrument a
94
 * query, but the instrument is so busy with something else for so long
95
 * that it forgets the original query. So this extra check is for that
96
 * situation, and vxi11_receive returns -VXI11_NULL_READ_RESP to the
97
 * calling function.
98
 * (2) vxi11_send_and_receive() is now aware of the possibility of
99
 * being returned -VXI11_NULL_READ_RESP. If so, it re-sends the query,
100
 * until either getting a "regular" read error (read_resp->error!=0) or
101
 * a successful read.
102
 *
103
 * Revision 1.7  2006/12/06 16:27:47  sds
104
 * removed call to ANSI free() fn in vxi11_receive, which according to
105
 * Manfred S. "is not necessary and wrong (crashes)".
106
 *
107
 * Revision 1.6  2006/08/25 13:45:12  sds
108
 * Major improvements to the vxi11_send function. Now takes
109
 * link->maxRecvSize into account, and writes a chunk at a time
110
 * until the entire message is sent. Important for sending large
111
 * data sets, because the data you want to send may be larger than
112
 * the instrument's "input buffer."
113
 *
114
 * Revision 1.5  2006/08/25 13:06:44  sds
115
 * tidied up some of the return values, and made sure that if a
116
 * sub-function returned an error value, this would also be
117
 * returned by the calling function.
118
 *
119
 * Revision 1.4  2006/07/06 13:04:59  sds
120
 * Lots of changes this revision.
121
 * Found I was having problems talking to multiple links on the same
122
 * client, if I created a different client for each one. So introduced
123
 * a few global variables to keep track of all the ip addresses of
124
 * clients that the library is asked to create, and only creating new
125
 * clients if the ip address is different. This puts a limit of how
126
 * many unique ip addresses (clients) a single process can connect to.
127
 * Set this value at 256 (should hopefully be enough!).
128
 * Next I found that talking to different clients on different ip
129
 * addresses didn't work. It turns out that create_link_1() creates
130
 * a static structure. This this link is associated with a given
131
 * client (and hence a given IP address), then the only way I could
132
 * think of making things work was to add a call to an
133
 * vxi11_open_link() function before each send command (no idea what
134
 * this adds to overheads and it's very messy!) - at least I was
135
 * able to get this to only happen when we are using more than one
136
 * client/ip address.
137
 * Also, while I was at it, I re-ordered the functions a little -
138
 * starts with core user functions, extra user functions, then core
139
 * library functions at the end. Added a few more comments. Tidied
140
 * up. Left some debugging info in, but commented out.
141
 *
142
 * Revision 1.3  2006/06/26 12:40:56  sds
143
 * Introduced a new CLINK structure, to reduce the number of arguments
144
 * passed to functions. Wrote wrappers for open(), close(), send()
145
 * and receieve() functions, then adjusted all the other functions built
146
 * on those to make use of the CLINK structure.
147
 *
148
 * Revision 1.2  2006/06/26 10:29:48  sds
149
 * Added GNU GPL and copyright notices.
150
 *
151
 */
152
 
153
/* vxi11_user.cc
154
 * Copyright (C) 2006 Steve D. Sharples
155
 *
156
 * User library for opening, closing, sending to and receiving from
157
 * a device enabled with the VXI11 RPC ethernet protocol. Uses the files
158
 * generated by rpcgen vxi11.x.
159
 *
160
 * This program is free software; you can redistribute it and/or
161
 * modify it under the terms of the GNU General Public License
162
 * as published by the Free Software Foundation; either version 2
163
 * of the License, or (at your option) any later version.
164
 *
165
 * This program is distributed in the hope that it will be useful,
166
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
167
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
168
 * GNU General Public License for more details.
169
 *
170
 * You should have received a copy of the GNU General Public License
171
 * along with this program; if not, write to the Free Software
172
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
173
 *
174
 * The author's email address is steve.sharples@nottingham.ac.uk
175
 */
176
 
177
#include "vxi11_user.h"
178
 
179
/*****************************************************************************
180
 * GENERAL NOTES
181
 *****************************************************************************
182
 *
183
 * There are four functions at the heart of this library:
184
 *
185
 * int  vxi11_open_device(char *ip, CLIENT **client, VXI11_LINK **link)
186
 * int  vxi11_close_device(char *ip, CLIENT *client, VXI11_LINK *link)
187
 * int  vxi11_send(CLIENT *client, VXI11_LINK *link, char *cmd, unsigned long len)
188
 * long vxi11_receive(CLIENT *client, VXI11_LINK *link, char *buffer, unsigned long len, unsigned long timeout)
189
 *
190
 * Note that all 4 of these use separate client and link structures. All the
191
 * other functions are built on these four core functions, and the first layer
192
 * of abstraction is to combine the CLIENT and VXI11_LINK structures into a
193
 * single entity, which I've called a CLINK. For the send and receive
194
 * functions, this is just a simple wrapper. For the open and close functions
195
 * it's a bit more complicated, because we somehow have to keep track of
196
 * whether we've already opened a device with the same IP address before (in
197
 * which case we need to recycle a previously created client), or whether
198
 * we've still got any other links to a given IP address left when we are
199
 * asked to close a clink (in which case we can sever the link, but have to
200
 * keep the client open). This is so the person using this library from
201
 * userland does not have to keep track of whether they are talking to a
202
 * different physical instrument or not each time they establish a connection.
203
 *
204
 * So the base functions that the user will probably want to use are:
205
 *
206
 * int  vxi11_open_device(char *ip, CLINK *clink)
207
 * int  vxi11_close_device(char *ip, CLINK *clink)
208
 * int  vxi11_send(CLINK *clink, char *cmd, unsigned long len)
209
 *    --- or --- (if sending just text)
210
 * int  vxi11_send(CLINK *clink, char *cmd)
211
 * long vxi11_receive(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout)
212
 *
213
 * There are then useful (to me, anyway) more specific functions built on top
214
 * of these:
215
 *
216
 * int  vxi11_send_data_block(CLINK *clink, char *cmd, char *buffer, unsigned long len)
217
 * long vxi11_receive_data_block(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout)
218
 * long vxi11_send_and_receive(CLINK *clink, char *cmd, char *buf, unsigned long buf_len, unsigned long timeout)
219
 * long vxi11_obtain_long_value(CLINK *clink, char *cmd, unsigned long timeout)
220
 * double vxi11_obtain_double_value(CLINK *clink, char *cmd, unsigned long timeout)
221
 *
222
 * (then there are some shorthand wrappers for the above without specifying
223
 * the timeout due to sheer laziness---explore yourself)
224
 */
225
 
226
 
227
/* Global variables. Keep track of multiple links per client. We need this
228
 * because:
229
 * - we'd like the library to be able to cope with multiple links to a given
230
 *   client AND multiple links to multiple clients
231
 * - we'd like to just refer to a client/link ("clink") as a single
232
 *   entity from user land, we don't want to worry about different
233
 *   initialisation procedures, depending on whether it's an instrument
234
 *   with the same IP address or not
235
 */
236
char    VXI11_IP_ADDRESS[VXI11_MAX_CLIENTS][20];
237
CLIENT  *VXI11_CLIENT_ADDRESS[VXI11_MAX_CLIENTS];
238
int     VXI11_DEVICE_NO = 0;
239
int     VXI11_LINK_COUNT[VXI11_MAX_CLIENTS];
240
 
241
/*****************************************************************************
242
 * KEY USER FUNCTIONS - USE THESE FROM YOUR PROGRAMS OR INSTRUMENT LIBRARIES *
243
 *****************************************************************************/
244
 
245
/* OPEN FUNCTIONS *
246
 * ============== */
247
 
248
/* Use this function from user land to open a device and create a link. Can be
249
 * used multiple times for the same device (the library will keep track).*/
250
int     vxi11_open_device(const char *ip, CLINK *clink, char *device) {
251
int     ret;
252
int     l;
253
int     device_no=-1;
254
 
255
//      printf("before doing anything, clink->link = %ld\n", clink->link);
256
        /* Have a look to see if we've already initialised an instrument with
257
         * this IP address */
258
        for (l=0; l<VXI11_MAX_CLIENTS; l++){
259
                if (strcmp(ip,VXI11_IP_ADDRESS[l]) == 0 ) {
260
                        device_no=l;
261
//                      printf("Open function, search, found ip address %s, device no %d\n",ip,device_no);
262
                        }
263
                }
264
 
265
        /* Couldn't find a match, must be a new IP address */
266
        if (device_no < 0) {
267
                /* Uh-oh, we're out of storage space. Increase the #define
268
                 * for VXI11_MAX_CLIENTS in vxi11_user.h */
269
                if (VXI11_DEVICE_NO >= VXI11_MAX_CLIENTS) {
270
                        printf("Error: maximum of %d clients allowed\n",VXI11_MAX_CLIENTS);
271
                        ret = -VXI11_MAX_CLIENTS;
272
                        }
273
                /* Create a new client, keep a note of where the client pointer
274
                 * is, for this IP address. Because it's a new client, this
275
                 * must be link number 1. Keep track of how many devices we've
276
                 * opened so we don't run out of storage space. */
277
                else {
278
                        ret = vxi11_open_device(ip, &(clink->client), &(clink->link), device);
279
                        strncpy(VXI11_IP_ADDRESS[VXI11_DEVICE_NO],ip,20);
280
                        VXI11_CLIENT_ADDRESS[VXI11_DEVICE_NO] = clink->client;
281
                        VXI11_LINK_COUNT[VXI11_DEVICE_NO]=1;
282
//                      printf("Open function, could not find ip address %s.\n",ip);
283
//                      printf("So now, VXI11_IP_ADDRESS[%d]=%s,\n",VXI11_DEVICE_NO,VXI11_IP_ADDRESS[VXI11_DEVICE_NO]);
284
//                      printf("VXI11_CLIENT_ADDRESS[%d]=%ld,\n",VXI11_DEVICE_NO,VXI11_CLIENT_ADDRESS[VXI11_DEVICE_NO]);
285
//                      printf("          clink->client=%ld,\n",clink->client);
286
//                      printf("VXI11_LINK_COUNT[%d]=%d.\n",VXI11_DEVICE_NO,VXI11_LINK_COUNT[VXI11_DEVICE_NO]);
287
                        VXI11_DEVICE_NO++;
288
                        }
289
                }
290
        /* already got a client for this IP address */
291
        else {
292
                /* Copy the client pointer address. Just establish a new link
293
                 * (not a new client). Add one to the link count */
294
                clink->client = VXI11_CLIENT_ADDRESS[device_no];
295
                ret = vxi11_open_link(ip, &(clink->client), &(clink->link), device);
296
//              printf("Found an ip address, copying client from VXI11_CLIENT_ADDRESS[%d]\n",device_no);
297
                VXI11_LINK_COUNT[device_no]++;
298
//              printf("Have just incremented VXI11_LINK_COUNT[%d], it's now %d\n",device_no,VXI11_LINK_COUNT[device_no]);
299
                }
300
//      printf("after creating link, clink->link = %ld\n", clink->link);
301
        return ret;
302
        }
303
 
304
/* This is a wrapper function, used for the situations where there is only one
305
 * "device" per client. This is the case for most (if not all) VXI11
306
 * instruments; however, it is _not_ the case for devices such as LAN to GPIB
307
 * gateways. These are single clients that communicate to many instruments
308
 * (devices). In order to differentiate between them, we need to pass a device
309
 * name. This gets used in the vxi11_open_link() fn, as the link_parms.device
310
 * value. */
311
int     vxi11_open_device(const char *ip, CLINK *clink) {
312
        char device[6];
313
        strncpy(device,"inst0",6);
314
        return vxi11_open_device(ip, clink, device);
315
        }
316
 
317
 
318
 
319
/* CLOSE FUNCTION *
320
 * ============== */
321
 
322
/* Use this function from user land to close a device and/or sever a link. Can
323
 * be used multiple times for the same device (the library will keep track).*/
324
int     vxi11_close_device(const char *ip, CLINK *clink) {
325
int     l,ret;
326
int     device_no = -1;
327
 
328
        /* Which instrument are we referring to? */
329
        for (l=0; l<VXI11_MAX_CLIENTS; l++){
330
                if (strcmp(ip,VXI11_IP_ADDRESS[l]) == 0 ) {
331
                        device_no=l;
332
                        }
333
                }
334
        /* Something's up if we can't find the IP address! */
335
        if (device_no == -1) {
336
                printf("vxi11_close_device: error: I have no record of you ever opening device\n");
337
                printf("                    with IP address %s\n",ip);
338
                ret = -4;
339
                }
340
        else {  /* Found the IP, there's more than one link to that instrument,
341
                 * so keep track and just close the link */
342
                if (VXI11_LINK_COUNT[device_no] > 1 ) {
343
                        ret = vxi11_close_link(ip,clink->client, clink->link);
344
                        VXI11_LINK_COUNT[device_no]--;
345
                        }
346
                /* Found the IP, it's the last link, so close the device (link
347
                 * AND client) */
348
                else {
349
                        ret = vxi11_close_device(ip, clink->client, clink->link);
350
                        }
351
                }
352
        return ret;
353
        }
354
 
355
 
356
/* SEND FUNCTIONS *
357
 * ============== */
358
 
359
/* A _lot_ of the time we are sending text strings, and can safely rely on
360
 * strlen(cmd). */
361
int     vxi11_send(CLINK *clink, const char *cmd) {
362
        return vxi11_send(clink, cmd, strlen(cmd));
363
        }
364
 
365
/* We still need the version of the function where the length is set explicitly
366
 * though, for when we are sending fixed length data blocks. */
367
int     vxi11_send(CLINK *clink, const char *cmd, unsigned long len) {
368
        return vxi11_send(clink->client, clink->link, cmd, len);
369
        }
370
 
371
 
372
/* RECEIVE FUNCTIONS *
373
 * ================= */
374
 
375
/* Lazy wrapper for when I can't be bothered to specify a read timeout */
376
long    vxi11_receive(CLINK *clink, char *buffer, unsigned long len) {
377
        return vxi11_receive(clink, buffer, len, VXI11_READ_TIMEOUT);
378
        }
379
 
380
long    vxi11_receive(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout) {
381
        return vxi11_receive(clink->client, clink->link, buffer, len, timeout);
382
        }
383
 
384
 
385
 
386
/*****************************************************************************
387
 * USEFUL ADDITIONAL HIGHER LEVER USER FUNCTIONS - USE THESE FROM YOUR       *
388
 * PROGRAMS OR INSTRUMENT LIBRARIES                                          *
389
 *****************************************************************************/
390
 
391
/* SEND FIXED LENGTH DATA BLOCK FUNCTION *
392
 * ===================================== */
393
int     vxi11_send_data_block(CLINK *clink, const char *cmd, char *buffer, unsigned long len) {
394
char    *out_buffer;
395
int     cmd_len=strlen(cmd);
396
int     ret;
397
 
398
        out_buffer=new char[cmd_len+10+len];
399
        sprintf(out_buffer,"%s#8%08lu",cmd,len);
400
        memcpy(out_buffer+cmd_len+10,buffer,(unsigned long) len);
401
        ret = vxi11_send(clink, out_buffer, (unsigned long) (cmd_len+10+len));
402
        delete[] out_buffer;
403
        return ret;
404
        }
405
 
406
 
407
/* RECEIVE FIXED LENGTH DATA BLOCK FUNCTION *
408
 * ======================================== */
409
 
410
/* This function reads a response in the form of a definite-length block, such
411
 * as when you ask for waveform data. The data is returned in the following
412
 * format:
413
 *   #800001000<1000 bytes of data>
414
 *   ||\______/
415
 *   ||    |
416
 *   ||    \---- number of bytes of data
417
 *   |\--------- number of digits that follow (in this case 8, with leading 0's)
418
 *   \---------- always starts with #
419
 */
420
long    vxi11_receive_data_block(CLINK *clink, char *buffer, unsigned long len, unsigned long timeout) {
421
/* I'm not sure what the maximum length of this header is, I'll assume it's
422
 * 11 (#9 + 9 digits) */
423
unsigned long   necessary_buffer_size;
424
char            *in_buffer;
425
int             ret;
426
int             ndigits;
427
unsigned long   returned_bytes;
428
int             l;
429
char            scan_cmd[20];
430
        necessary_buffer_size=len+12;
431
        in_buffer=new char[necessary_buffer_size];
432
        ret=vxi11_receive(clink, in_buffer, necessary_buffer_size, timeout);
433
        if (ret < 0) return ret;
434
        if (in_buffer[0] != '#') {
435
                printf("vxi11_user: data block error: data block does not begin with '#'\n");
436
                printf("First 20 characters received were: '");
437
                for(l=0;l<20;l++) {
438
                        printf("%c",in_buffer[l]);
439
                        }
440
                printf("'\n");
441
                return -3;
442
                }
443
 
444
        /* first find out how many digits */
445
        sscanf(in_buffer,"#%1d",&ndigits);
446
        /* some instruments, if there is a problem acquiring the data, return only "#0" */
447
        if (ndigits > 0) {
448
                /* now that we know, we can convert the next <ndigits> bytes into an unsigned long */
449
                sprintf(scan_cmd,"#%%1d%%%dlu",ndigits);
450
                sscanf(in_buffer,scan_cmd,&ndigits,&returned_bytes);
451
                memcpy(buffer, in_buffer+(ndigits+2), returned_bytes);
452
                delete[] in_buffer;
453
                return (long) returned_bytes;
454
                }
455
        else return 0;
456
        }
457
 
458
 
459
/* SEND AND RECEIVE FUNCTION *
460
 * ========================= */
461
 
462
/* This is mainly a useful function for the overloaded vxi11_obtain_value()
463
 * fn's, but is also handy and useful for user and library use */
464
long    vxi11_send_and_receive(CLINK *clink, const char *cmd, char *buf, unsigned long buf_len, unsigned long timeout) {
465
int     ret;
466
long    bytes_returned;
467
        do {
468
                ret = vxi11_send(clink, cmd);
469
                if (ret != 0) {
470
                        if (ret != -VXI11_NULL_WRITE_RESP) {
471
                                printf("Error: vxi11_send_and_receive: could not send cmd.\n");
472
                                printf("       The function vxi11_send returned %d. ",ret);
473
                                return -1;
474
                                }
475
                        else printf("(Info: VXI11_NULL_WRITE_RESP in vxi11_send_and_receive, resending query)\n");
476
                        }
477
 
478
                bytes_returned = vxi11_receive(clink, buf, buf_len, timeout);
479
                if (bytes_returned <= 0) {
480
                        if (bytes_returned >-VXI11_NULL_READ_RESP) {
481
                                printf("Error: vxi11_send_and_receive: problem reading reply.\n");
482
                                printf("       The function vxi11_receive returned %ld. ",bytes_returned);
483
                                return -2;
484
                                }
485
                        else printf("(Info: VXI11_NULL_READ_RESP in vxi11_send_and_receive, resending query)\n");
486
                        }
487
                } while (bytes_returned == -VXI11_NULL_READ_RESP || ret == -VXI11_NULL_WRITE_RESP);
488
        return 0;
489
        }
490
 
491
 
492
/* FUNCTIONS TO RETURN A LONG INTEGER VALUE SENT AS RESPONSE TO A QUERY *
493
 * ==================================================================== */
494
long    vxi11_obtain_long_value(CLINK *clink, const char *cmd, unsigned long timeout) {
495
char    buf[50]; /* 50=arbitrary length... more than enough for one number in ascii */
496
        memset(buf, 0, 50);
497
        if (vxi11_send_and_receive(clink, cmd, buf, 50, timeout) != 0) {
498
                printf("Returning 0\n");
499
                return 0;
500
                }
501
        return strtol(buf, (char **)NULL, 10);
502
        }
503
 
504
/* Lazy wrapper function with default read timeout */
505
long    vxi11_obtain_long_value(CLINK *clink, const char *cmd) {
506
        return vxi11_obtain_long_value(clink, cmd, VXI11_READ_TIMEOUT);
507
        }
508
 
509
 
510
/* FUNCTIONS TO RETURN A DOUBLE FLOAT VALUE SENT AS RESPONSE TO A QUERY *
511
 * ==================================================================== */
512
double  vxi11_obtain_double_value(CLINK *clink, const char *cmd, unsigned long timeout) {
513
char    buf[50]; /* 50=arbitrary length... more than enough for one number in ascii */
514
double  val;
515
        memset(buf, 0, 50);
516
        if (vxi11_send_and_receive(clink, cmd, buf, 50, timeout) != 0) {
517
                printf("Returning 0.0\n");
518
                return 0.0;
519
                }
520
        val = strtod(buf, (char **)NULL);
521
        return val;
522
        }
523
 
524
/* Lazy wrapper function with default read timeout */
525
double  vxi11_obtain_double_value(CLINK *clink, const char *cmd) {
526
        return vxi11_obtain_double_value(clink, cmd, VXI11_READ_TIMEOUT);
527
        }
528
 
529
 
530
/*****************************************************************************
531
 * CORE FUNCTIONS - YOU SHOULDN'T NEED TO USE THESE FROM YOUR PROGRAMS OR    *
532
 * INSTRUMENT LIBRARIES                                                      *
533
 *****************************************************************************/
534
 
535
/* OPEN FUNCTIONS *
536
 * ============== */
537
int     vxi11_open_device(const char *ip, CLIENT **client, VXI11_LINK **link, char *device) {
538
 
539
        *client = clnt_create(ip, DEVICE_CORE, DEVICE_CORE_VERSION, "tcp");
540
 
541
        if (*client == NULL) {
542
                clnt_pcreateerror(ip);
543
                return -1;
544
                }
545
 
546
        return vxi11_open_link(ip, client, link, device);
547
        }
548
 
549
int     vxi11_open_link(const char *ip, CLIENT **client, VXI11_LINK **link, char *device) {
550
 
551
Create_LinkParms link_parms;
552
 
553
        /* Set link parameters */
554
        link_parms.clientId     = (long) *client;
555
        link_parms.lockDevice   = 0;
556
        link_parms.lock_timeout = VXI11_DEFAULT_TIMEOUT;
557
        link_parms.device       = device;
558
 
559
        *link = (Create_LinkResp *) calloc(1, sizeof(Create_LinkResp));
560
 
561
        if (create_link_1(&link_parms, *link, *client) != RPC_SUCCESS) {
562
                clnt_perror(*client, ip);
563
                return -2;
564
                }
565
        return 0;
566
        }
567
 
568
 
569
/* CLOSE FUNCTIONS *
570
 * =============== */
571
int     vxi11_close_device(const char *ip, CLIENT *client, VXI11_LINK *link) {
572
int     ret;
573
 
574
        ret = vxi11_close_link(ip, client, link);
575
 
576
        clnt_destroy(client);
577
 
578
        return ret;
579
        }
580
 
581
int     vxi11_close_link(const char *ip, CLIENT *client, VXI11_LINK *link) {
582
Device_Error dev_error;
583
        memset(&dev_error, 0, sizeof(dev_error));
584
 
585
        if (destroy_link_1(&link->lid, &dev_error, client) != RPC_SUCCESS) {
586
                clnt_perror(client,ip);
587
                return -1;
588
                }
589
 
590
        return 0;
591
        }
592
 
593
 
594
/* SEND FUNCTIONS *
595
 * ============== */
596
 
597
/* A _lot_ of the time we are sending text strings, and can safely rely on
598
 * strlen(cmd). */
599
int     vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd) {
600
        return vxi11_send(client, link, cmd, strlen(cmd));
601
        }
602
 
603
/* We still need the version of the function where the length is set explicitly
604
 * though, for when we are sending fixed length data blocks. */
605
int     vxi11_send(CLIENT *client, VXI11_LINK *link, const char *cmd, unsigned long len) {
606
Device_WriteParms write_parms;
607
int     bytes_left = (int)len;
608
char    *send_cmd;
609
 
610
        send_cmd = new char[len];
611
        memcpy(send_cmd, cmd, len);
612
 
613
        write_parms.lid                 = link->lid;
614
        write_parms.io_timeout          = VXI11_DEFAULT_TIMEOUT;
615
        write_parms.lock_timeout        = VXI11_DEFAULT_TIMEOUT;
616
 
617
/* We can only write (link->maxRecvSize) bytes at a time, so we sit in a loop,
618
 * writing a chunk at a time, until we're done. */
619
 
620
        do {
621
                Device_WriteResp write_resp;
622
                memset(&write_resp, 0, sizeof(write_resp));
623
 
624
                if (bytes_left <= link->maxRecvSize) {
625
                        write_parms.flags               = 8;
626
                        write_parms.data.data_len       = bytes_left;
627
                        }
628
                else {
629
                        write_parms.flags               = 0;
630
                        /* We need to check that maxRecvSize is a sane value (ie >0). Believe it
631
                         * or not, on some versions of Agilent Infiniium scope firmware the scope
632
                         * returned "0", which breaks Rule B.6.3 of the VXI-11 protocol. Nevertheless
633
                         * we need to catch this, otherwise the program just hangs. */
634
                        if (link->maxRecvSize > 0) {
635
                                write_parms.data.data_len       = link->maxRecvSize;
636
                                }
637
                        else {
638
                                write_parms.data.data_len       = 4096; /* pretty much anything should be able to cope with 4kB */
639
                                }
640
                        }
641
                write_parms.data.data_val       = send_cmd + (len - bytes_left);
642
 
643
                if(device_write_1(&write_parms, &write_resp, client) != RPC_SUCCESS) {
644
                        delete[] send_cmd;
645
                        return -VXI11_NULL_WRITE_RESP; /* The instrument did not acknowledge the write, just completely
646
                                                          dropped it. There was no vxi11 comms error as such, the
647
                                                          instrument is just being rude. Usually occurs when the instrument
648
                                                          is busy. If we don't check this first, then the following
649
                                                          line causes a seg fault */
650
                        }
651
                if (write_resp . error != 0) {
652
                        printf("vxi11_user: write error: %d\n",write_resp . error);
653
                        delete[] send_cmd;
654
                        return -(write_resp . error);
655
                        }
656
                bytes_left -= write_resp . size;
657
                } while (bytes_left > 0);
658
 
659
        delete[] send_cmd;
660
        return 0;
661
        }
662
 
663
 
664
/* RECEIVE FUNCTIONS *
665
 * ================= */
666
 
667
// It appeared that this function wasn't correctly dealing with more data available than specified in len.
668
// This patch attempts to fix this issue.       RDP 2007/8/13
669
 
670
/* 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);
671
        }
672
 
673
#define RCV_END_BIT     0x04    // An end indicator has been read
674
#define RCV_CHR_BIT     0x02    // A termchr is set in flags and a character which matches termChar is transferred
675
#define RCV_REQCNT_BIT  0x01    // requestSize bytes have been transferred.  This includes a request size of zero.
676
 
677
long    vxi11_receive(CLIENT *client, VXI11_LINK *link, char *buffer, unsigned long len, unsigned long timeout) {
678
Device_ReadParms read_parms;
679
Device_ReadResp  read_resp;
680
long    curr_pos = 0;
681
 
682
        read_parms.lid                  = link->lid;
683
        read_parms.requestSize          = len;
684
        read_parms.io_timeout           = timeout;      /* in ms */
685
        read_parms.lock_timeout         = timeout;      /* in ms */
686
        read_parms.flags                = 0;
687
        read_parms.termChar             = 0;
688
 
689
        do {
690
                memset(&read_resp, 0, sizeof(read_resp));
691
 
692
                read_resp.data.data_val = buffer + curr_pos;
693
                read_parms.requestSize = len    - curr_pos;     // Never request more total data than originally specified in len
694
 
695
                if(device_read_1(&read_parms, &read_resp, client) != RPC_SUCCESS) {
696
                        return -VXI11_NULL_READ_RESP; /* there is nothing to read. Usually occurs after sending a query
697
                                                         which times out on the instrument. If we don't check this first,
698
                                                         then the following line causes a seg fault */
699
                        }
700
                if (read_resp . error != 0) {
701
                        /* Read failed for reason specified in error code.
702
                        *  0     no error
703
                        *  4     invalid link identifier
704
                        *  11    device locked by another link
705
                        *  15    I/O timeout
706
                        *  17    I/O error
707
                        *  23    abort
708
                        */
709
 
710
                        printf("vxi11_user: read error: %d\n",read_resp . error);
711
                        return -(read_resp . error);
712
                        }
713
 
714
                if((curr_pos + read_resp . data.data_len) <= len) {
715
                        curr_pos += read_resp . data.data_len;
716
                        }
717
                if( (read_resp.reason & RCV_END_BIT) || (read_resp.reason & RCV_CHR_BIT) ) {
718
                        break;
719
                        }
720
                else if( curr_pos == len ) {
721
                        printf("xvi11_user: read error: buffer too small. Read %d bytes without hitting terminator.\n", curr_pos );
722
                        return -100;
723
                        }
724
                } while(1);
725
        return (curr_pos); /*actual number of bytes received*/
726
 
727
        }
728