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