Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line | 
|---|---|---|---|
| 145 | 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 |