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