Line data Source code
1 : /*
2 : * \file za_serial.c
3 : * \author Eric Dand
4 : * \version 1.0
5 : * \date 28 November 2014
6 : * \copyright Apache Software License Version 2.0
7 : *
8 : * Implementation file for ASCII portion of the Zaber Serial API in C.
9 : * See za_serial.h for documentation.
10 : */
11 : #include <string.h>
12 : #include <stdio.h>
13 : #include <stdlib.h>
14 : #include "za_serial.h"
15 :
16 : static int za_verbose = 1;
17 :
18 0 : void za_set_verbose(int value)
19 : {
20 0 : za_verbose = value;
21 0 : }
22 :
23 :
24 : #include <fcntl.h>
25 : #include <sys/stat.h>
26 : #include <sys/types.h>
27 : #include <termios.h>
28 : #include <unistd.h>
29 :
30 : #if defined(NDEBUG)
31 : #define PRINT_ERROR(M)
32 : #define PRINTF_ERROR(M, ...)
33 : #define PRINT_SYSCALL_ERROR(M)
34 : #else
35 : #define PRINT_ERROR(M) do { if (za_verbose) { fprintf(stderr, "(%s: %d) " M\
36 : "\n", __FILE__, __LINE__); } } while(0)
37 : #define PRINTF_ERROR(M, ...) do { if (za_verbose) { fprintf(stderr,\
38 : "(%s: %d) " M "\n", __FILE__, __LINE__, __VA_ARGS__); } } while(0)
39 : #include <errno.h>
40 : #define PRINT_SYSCALL_ERROR(M) do { if (za_verbose) {\
41 : fprintf(stderr, "(%s: %d) [ERROR] " M " failed: %s.\n",\
42 : __FILE__, __LINE__, strerror(errno)); } } while(0)
43 : #endif
44 : /* A little sugar for checking return values from system calls.
45 : * I would have liked to use GNU/GCC's "statement expressions" so that one
46 : * do something like "z_port port = SYSCALL(open([parameters]))", but they're
47 : * a GNU extension, and therefore unfriendly to non-GCC compilers.
48 : * Workaround to avoid dependence on statement expressions for SYSCALL macro:
49 : * use SYSCALL on result of assignment instead. */
50 : #define SYSCALL(F) do { if ((F) < 0) { PRINT_SYSCALL_ERROR(#F);\
51 : return Z_ERROR_SYSTEM_ERROR; } } while(0)
52 :
53 0 : int za_connect(z_port *port, const char *port_name)
54 : {
55 : struct termios tio, orig_tio;
56 :
57 0 : if (port == NULL || port_name == NULL)
58 : {
59 : PRINT_ERROR("[ERROR] port and port_name cannot be NULL.");
60 0 : return Z_ERROR_NULL_PARAMETER;
61 : }
62 :
63 : /* blocking read/write */
64 0 : SYSCALL(*port = open(port_name, O_RDWR | O_NOCTTY));
65 0 : SYSCALL(tcgetattr(*port, &orig_tio));
66 0 : memcpy(&tio, &orig_tio, sizeof(struct termios)); /* copy padding too */
67 :
68 : /* cfmakeraw() without cfmakeraw() for cygwin compatibility */
69 0 : tio.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR
70 : | IGNCR | ICRNL | IXON);
71 0 : tio.c_oflag &= ~OPOST;
72 0 : tio.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
73 0 : tio.c_cflag &= ~(CSIZE | PARENB);
74 : /* end cfmakeraw() */
75 0 : tio.c_cflag = CS8|CREAD|CLOCAL;
76 :
77 : /* READ_TIMEOUT is defined in za_serial.h */
78 : if (READ_TIMEOUT % 100 != 0)
79 : {
80 : tio.c_cc[VTIME] = READ_TIMEOUT / 100 + 1; /* Round up */
81 : }
82 : else
83 : {
84 0 : tio.c_cc[VTIME] = READ_TIMEOUT / 100;
85 : }
86 0 : tio.c_cc[VMIN] = 0;
87 :
88 0 : SYSCALL(cfsetospeed(&tio, B115200) & cfsetispeed(&tio, B115200));
89 :
90 0 : while(memcmp(&orig_tio, &tio, sizeof(struct termios)) != 0)
91 : { /* memcmp is only OK here because we used memcpy above */
92 0 : SYSCALL(tcsetattr(*port, TCSAFLUSH, &tio));
93 0 : SYSCALL(tcgetattr(*port, &orig_tio));
94 : }
95 :
96 0 : return Z_SUCCESS;
97 : }
98 :
99 0 : int za_disconnect(z_port port)
100 : {
101 0 : SYSCALL(close(port));
102 0 : return Z_SUCCESS;
103 : }
104 :
105 : /* a subtle feature of za_send() that is implied but not explicitly documented:
106 : * if you pass a pointer to a 0-length string (ie. just a \0) for command,
107 : * za_send() will send the minimal command of "/\n" automagically, as it will
108 : * assume you are sending a command without content, and that you have
109 : * forgotten the leading '/' and trailing '\n'. Mention of this is probably
110 : * best left out of the official docs since it's a hacky way to use za_send().
111 : */
112 0 : int za_send( z_port port,
113 : const char *command,
114 : size_t sMaxSz
115 : )
116 : {
117 : int nlast,
118 : length,
119 0 : written = 0;
120 :
121 0 : if (command == NULL)
122 : {
123 : PRINT_ERROR("[ERROR] command cannot be NULL.");
124 0 : return Z_ERROR_NULL_PARAMETER;
125 : }
126 :
127 0 : if (command[0] != '/')
128 : { /* send a '/' if they forgot it */
129 0 : SYSCALL(nlast = write(port, "/", 1));
130 0 : written += nlast;
131 : }
132 0 : length = strnlen(command, sMaxSz);
133 0 : SYSCALL(nlast = write(port, command, length));
134 0 : written += nlast;
135 0 : if (nlast != length)
136 : { /* write can return a short write: test for it */
137 : PRINTF_ERROR("[ERROR] write did not write entire message: "
138 : "could only write %d bytes of %d.", nlast, length);
139 0 : return Z_ERROR_SYSTEM_ERROR;
140 : }
141 :
142 0 : if (length == 0 || command[length-1] != '\n')
143 : { /* send a newline if they forgot it */
144 0 : SYSCALL(nlast = write(port, "\n", 1));
145 0 : written += nlast;
146 : }
147 :
148 0 : return written;
149 : }
150 :
151 0 : int za_receive(z_port port, char *destination, int length)
152 : {
153 0 : int nread = 0,
154 : nlast;
155 : char c;
156 :
157 : for (;;)
158 : {
159 0 : SYSCALL(nlast = (int) read(port, &c, 1));
160 :
161 0 : if (nlast == 0) /* timed out */
162 : {
163 : //PRINTF_ERROR("[INFO] Read timed out after reading %d "
164 : // "bytes.", nread);
165 0 : return Z_ERROR_TIMEOUT;
166 : }
167 :
168 0 : if (destination != NULL)
169 : {
170 0 : destination[nread] = c;
171 : }
172 0 : nread += nlast;
173 :
174 0 : if (nread == length)
175 : {
176 : PRINTF_ERROR("[ERROR] Read destination buffer not large "
177 : "enough. Recommended size: 256B. Your size: %dB.",
178 : length);
179 0 : return Z_ERROR_BUFFER_TOO_SMALL;
180 : }
181 :
182 0 : if (c == '\n')
183 : {
184 0 : nread -= 2; /* prepare to cut off the "\r\n" */
185 0 : if (nread < 0)
186 : {
187 : PRINT_ERROR("[ERROR] Reply too short. It is likely that "
188 : "only a partial reply was read.");
189 0 : return Z_ERROR_SYSTEM_ERROR;
190 : }
191 0 : if (destination != NULL)
192 : {
193 0 : destination[nread] = '\0'; /* chomp the "\r\n" */
194 : }
195 0 : return nread;
196 : }
197 : }
198 : }
199 :
200 0 : int za_setbaud(z_port port, int baud)
201 : {
202 : struct termios tio;
203 : speed_t tbaud;
204 0 : switch (baud)
205 : {
206 0 : case 9600:
207 0 : tbaud = B9600;
208 0 : break;
209 0 : case 19200:
210 0 : tbaud = B19200;
211 0 : break;
212 0 : case 38400:
213 0 : tbaud = B38400;
214 0 : break;
215 0 : case 57600:
216 0 : tbaud = B57600;
217 0 : break;
218 0 : case 115200:
219 0 : tbaud = B115200;
220 0 : break;
221 0 : default:
222 : PRINTF_ERROR("[ERROR] Invalid baud rate. Valid rates are "
223 : "9600, 19200, 38400, 57600, and 115200 (default).\n"
224 : "Your rate: %d.", baud);
225 0 : return Z_ERROR_INVALID_BAUDRATE;
226 : }
227 :
228 0 : SYSCALL(tcgetattr(port, &tio));
229 0 : SYSCALL(cfsetospeed(&tio, tbaud) & cfsetispeed(&tio, tbaud));
230 0 : SYSCALL(tcsetattr(port, TCSAFLUSH, &tio));
231 :
232 0 : return Z_SUCCESS;
233 : }
234 :
235 0 : int za_drain(z_port port)
236 : {
237 : struct termios tio;
238 : int old_timeout;
239 : char c;
240 :
241 : /* set timeout to 0.1s */
242 0 : SYSCALL(tcgetattr(port, &tio));
243 0 : old_timeout = tio.c_cc[VTIME];
244 0 : tio.c_cc[VTIME] = 1;
245 0 : SYSCALL(tcsetattr(port, TCSANOW, &tio));
246 :
247 : /* flush and read whatever else comes in */
248 0 : SYSCALL(tcflush(port, TCIFLUSH));
249 0 : while(read(port, &c, 1) > 0);
250 :
251 : /* set timeout back to what it was */
252 0 : tio.c_cc[VTIME] = old_timeout;
253 0 : SYSCALL(tcsetattr(port, TCSANOW, &tio));
254 :
255 0 : return Z_SUCCESS;
256 : }
257 :
258 :
259 : /* A helper for za_decode. Copies from source to destination, until delim or
260 : * a '\0' is found, then null-terminates destination.
261 : *
262 : * Returns the number of bytes copied, including the added null-terminator. */
263 0 : static size_t copy_until_delim(char *destination, const char *source,
264 : const char delim, size_t destination_length)
265 : {
266 : size_t i;
267 :
268 0 : for(i = 0; source[i] != delim && source[i] != '\0'
269 0 : && i < destination_length - 1; i++)
270 : {
271 0 : destination[i] = source[i];
272 : }
273 :
274 0 : destination[i] = '\0'; /* null-terminate instead of copying delim */
275 :
276 0 : return i + 1;
277 : }
278 :
279 0 : static int decode_reply( struct za_reply *destination,
280 : const char *reply,
281 : size_t sMaxSz
282 : )
283 : {
284 : char buffer[8]; /* needs to be at least 5B: set to 8 because
285 : it will be padded to 8 from 5 anyway. */
286 : size_t offset,
287 : length;
288 :
289 0 : if (strnlen(reply, sMaxSz) < 18)
290 : {
291 : PRINTF_ERROR("[ERROR] Reply could not be decoded: shorter than expected. "
292 : "It is likely that only a partial reply was read.\n"
293 : "Reply value: %s", reply);
294 0 : return Z_ERROR_COULD_NOT_DECODE;
295 : }
296 :
297 0 : destination->message_type = '@';
298 :
299 : /* device address: 2 digits, 00-99
300 : *
301 : * The device address is part of the same "token" as the message type,
302 : * so we call strtol on the same token, skipping the first char.
303 : *
304 : * We don't check the length here for future-proofing:
305 : * if we add support for more device addresses (ie. more digits),
306 : * this should handle it gracefully. */
307 0 : offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
308 0 : destination->device_address = (int) strtol(buffer + 1, NULL, 10);
309 0 : reply += offset;
310 :
311 : /* axis number: 1 digit, 0-9
312 : *
313 : * The axis number may be 2 digits (or more) in the future, but it's
314 : * unlikely to go over 2^31 - 1 any time soon, so we convert to
315 : * standard int and use that. */
316 0 : offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
317 0 : destination->axis_number = (int) strtol(buffer, NULL, 10);
318 0 : reply += offset;
319 :
320 : /* reply flags: 2 letters
321 : *
322 : * Only replies have reply flags. Value is either "OK" or "RJ". */
323 0 : offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
324 0 : if (offset > sizeof(destination->reply_flags))
325 : {
326 : PRINTF_ERROR("[ERROR] Reply could not be decoded: reply flags too "
327 : "long. Maximum length: %zu. Your length: %zu. Reply flags "
328 : "value: %s\n.", sizeof(destination->reply_flags), offset,
329 : buffer);
330 0 : return Z_ERROR_COULD_NOT_DECODE;
331 : }
332 0 : strcpy(destination->reply_flags, buffer);
333 0 : reply += offset;
334 :
335 : /* device status: 4 letters
336 : *
337 : * Replies and alerts have a "status" field. Value is either "IDLE" or
338 : * "BUSY", depending on what the device is doing at the time. */
339 0 : offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
340 0 : if (offset > sizeof(destination->device_status))
341 : {
342 : PRINTF_ERROR("[ERROR] Reply could not be decoded: device status too "
343 : "long. Expected length: %zu. Your length: %zu. Device status "
344 : "value: %s\n.", sizeof(destination->device_status), offset,
345 : buffer);
346 0 : return Z_ERROR_COULD_NOT_DECODE;
347 : }
348 0 : strcpy(destination->device_status, buffer);
349 0 : reply += offset;
350 :
351 : /* warning flags: 2 letters
352 : *
353 : * Replies and alerts have warning flags. Warning flags are typically
354 : * "--". All other possible warning flag values should be two
355 : * consecutive capital letters. */
356 0 : offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
357 0 : if (offset > sizeof(destination->warning_flags))
358 : {
359 : PRINTF_ERROR("[ERROR] Reply could not be decoded: warning flags too "
360 : "long. Expected length: %zu. Your length: %zu. Warning flags "
361 : "value: %s\n.", sizeof(destination->warning_flags), offset,
362 : buffer);
363 0 : return Z_ERROR_COULD_NOT_DECODE;
364 : }
365 0 : strcpy(destination->warning_flags, buffer);
366 0 : reply += offset;
367 :
368 : /* data: 1+ characters, probably less than 128 characters
369 : *
370 : * Replies and info have data. This can be anything, including spaces,
371 : * numbers, and human-readable text. */
372 0 : length = strnlen(reply, sMaxSz); /* get length of remaining data */
373 0 : if (length > sizeof(destination->response_data))
374 : {
375 : PRINTF_ERROR("[ERROR] Reply could not be decoded: response data too "
376 : "long. Maximum length: %zu. Your length: %zu. Data: %s.\n",
377 : sizeof(destination->response_data), length, reply);
378 0 : return Z_ERROR_COULD_NOT_DECODE;
379 : }
380 0 : strcpy(destination->response_data, reply);
381 :
382 0 : return Z_SUCCESS;
383 :
384 : }
385 :
386 0 : static int decode_alert( struct za_reply *destination,
387 : const char *reply,
388 : size_t sMaxSz
389 : )
390 : {
391 : size_t offset;
392 : char buffer[8]; /* needs to be at least 5B: set to 8 because
393 : it will be padded to 8 from 5 anyway. */
394 :
395 0 : if (strnlen(reply, sMaxSz) < 13)
396 : {
397 : PRINTF_ERROR("[ERROR] Reply could not be decoded: shorter than expected. "
398 : "It is likely that only a partial reply was read.\n"
399 : "Reply value: %s", reply);
400 0 : return Z_ERROR_COULD_NOT_DECODE;
401 : }
402 :
403 0 : destination->message_type = '!';
404 :
405 : /* device address: 2 digits, 00-99
406 : *
407 : * The device address is part of the same "token" as the message type,
408 : * so we call strtol on the same token, skipping the first char.
409 : *
410 : * We don't check the length here for future-proofing:
411 : * if we add support for more device addresses (ie. more digits),
412 : * this should handle it gracefully. */
413 0 : offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
414 0 : destination->device_address = (int) strtol(buffer + 1, NULL, 10);
415 0 : reply += offset;
416 :
417 : /* axis number: 1 digit, 0-9
418 : *
419 : * The axis number may be 2 digits (or more) in the future, but it's
420 : * unlikely to go over 2^31 - 1 any time soon, so we convert to
421 : * standard int and use that. */
422 0 : offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
423 0 : destination->axis_number = (int) strtol(buffer, NULL, 10);
424 0 : reply += offset;
425 :
426 0 : destination->reply_flags[0] = '\0';
427 :
428 : /* device status: 4 letters
429 : *
430 : * Replies and alerts have a "status" field. Value is either "IDLE" or
431 : * "BUSY", depending on what the device is doing at the time. */
432 0 : offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
433 0 : if (offset > sizeof(destination->device_status))
434 : {
435 : PRINTF_ERROR("[ERROR] Reply could not be decoded: device status too "
436 : "long. Expected length: %zu. Your length: %zu. Device status "
437 : "value: %s\n.", sizeof(destination->device_status), offset,
438 : buffer);
439 0 : return Z_ERROR_COULD_NOT_DECODE;
440 : }
441 0 : strcpy(destination->device_status, buffer);
442 0 : reply += offset;
443 :
444 : /* warning flags: 2 letters
445 : *
446 : * Replies and alerts have warning flags. Warning flags are typically
447 : * "--". All other possible warning flag values should be two
448 : * consecutive capital letters. */
449 0 : offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
450 0 : if (offset > sizeof(destination->warning_flags))
451 : {
452 : PRINTF_ERROR("[ERROR] Reply could not be decoded: warning flags too "
453 : "long. Expected length: %zu. Your length: %zu. Warning flags "
454 : "value: %s\n.", sizeof(destination->warning_flags), offset,
455 : buffer);
456 0 : return Z_ERROR_COULD_NOT_DECODE;
457 : }
458 0 : strcpy(destination->warning_flags, buffer);
459 :
460 0 : destination->response_data[0] = '\0';
461 :
462 0 : return Z_SUCCESS;
463 : }
464 :
465 0 : static int decode_info( struct za_reply *destination,
466 : const char *reply,
467 : size_t sMaxSz
468 : )
469 : {
470 : size_t length,
471 : offset;
472 : char buffer[8]; /* needs to be at least 5B: set to 8 because
473 : it will be padded to 8 from 5 anyway. */
474 :
475 0 : if (strnlen(reply, sMaxSz) < 7)
476 : {
477 : PRINTF_ERROR("[ERROR] Reply could not be decoded: shorter than expected. "
478 : "It is likely that only a partial reply was read.\n"
479 : "Reply value: %s", reply);
480 0 : return Z_ERROR_COULD_NOT_DECODE;
481 : }
482 :
483 0 : destination->message_type = '#';
484 :
485 : /* device address: 2 digits, 00-99
486 : *
487 : * The device address is part of the same "token" as the message type,
488 : * so we call strtol on the same token, skipping the first char.
489 : *
490 : * We don't check the length here for future-proofing:
491 : * if we add support for more device addresses (ie. more digits),
492 : * this should handle it gracefully. */
493 0 : offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
494 0 : destination->device_address = (int) strtol(buffer + 1, NULL, 10);
495 0 : reply += offset;
496 :
497 : /* axis number: 1 digit, 0-9
498 : *
499 : * The axis number may be 2 digits (or more) in the future, but it's
500 : * unlikely to go over 2^31 - 1 any time soon, so we convert to
501 : * standard int and use that. */
502 0 : offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
503 0 : destination->axis_number = (int) strtol(buffer, NULL, 10);
504 0 : reply += offset;
505 :
506 0 : destination->reply_flags[0] = '\0';
507 0 : destination->device_status[0] = '\0';
508 0 : destination->warning_flags[0] = '\0';
509 :
510 : /* data: 1+ characters, probably less than 128 characters
511 : *
512 : * Replies and info have data. This can be anything, including spaces,
513 : * numbers, and human-readable text. */
514 0 : length = strnlen(reply,sMaxSz); /* get length of remaining data */
515 0 : if (length > sizeof(destination->response_data))
516 : {
517 : PRINTF_ERROR("[ERROR] Reply could not be decoded: response data too "
518 : "long. Maximum length: %zu. Your length: %zu. Data: %s.\n",
519 : sizeof(destination->response_data), length, reply);
520 0 : return Z_ERROR_COULD_NOT_DECODE;
521 : }
522 0 : strcpy(destination->response_data, reply);
523 :
524 0 : return Z_SUCCESS;
525 : }
526 :
527 : /* This function tokenizes using the above helper function to copy
528 : * token-by-token from reply into buffer, or directly into destination,
529 : * depending on the field in destination we are populating. It could probably
530 : * be prettier, more clever, or more efficient, but it's written to be
531 : * readable, reliable, and robust first.
532 : *
533 : * Note that we can safely use strcpy here because the above helper function is
534 : * guaranteed to write a NUL at the end of its destination string.
535 : *
536 : * See http://www.zaber.com/wiki/Manuals/ASCII_Protocol_Manual#Replies for
537 : * more info on replies.
538 : */
539 0 : int za_decode( struct za_reply *destination,
540 : const char *reply,
541 : size_t sMaxSz
542 : )
543 : {
544 : char message_type;
545 :
546 0 : if (destination == NULL || reply == NULL)
547 : {
548 : PRINT_ERROR("[ERROR] decoding requires both a non-NULL destination "
549 : "and reply to decode.");
550 0 : return Z_ERROR_NULL_PARAMETER;
551 : }
552 :
553 0 : if (strnlen(reply, sMaxSz) == 0)
554 : {
555 : PRINT_ERROR("[ERROR] Reply could not be decoded: no data.");
556 0 : return Z_ERROR_COULD_NOT_DECODE;
557 : }
558 0 : message_type = reply[0];
559 :
560 : /* most replies are 18 chars or longer: info and alerts can be shorter */
561 0 : switch(message_type)
562 : {
563 0 : case '@':
564 0 : return decode_reply(destination, reply, sMaxSz);
565 0 : case '!':
566 0 : return decode_alert(destination, reply, sMaxSz);
567 0 : case '#':
568 0 : return decode_info(destination, reply, sMaxSz);
569 0 : default:
570 : PRINTF_ERROR("[ERROR] Reply could not be decoded: unexpected "
571 : "message type. Valid types are '@' (reply), '!' (alert), "
572 : "and '#' (info). Your type: '%c'. \n", message_type);
573 0 : return Z_ERROR_COULD_NOT_DECODE;
574 : }
575 : }
|