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 |