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