Subversion Repositories f9daq

Rev

Blame | Last modification | View Log | RSS feed

  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.  
  729.