API
 
Loading...
Searching...
No Matches
za_serial.c
Go to the documentation of this file.
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
16static int za_verbose = 1;
17
18void za_set_verbose(int value)
19{
20 za_verbose = value;
21}
22
23#if defined(_WIN32)
24#pragma warning( disable : 4996 ) /* don't warn that strncpy is deprecated */
25/* These macros save us a lot of repetition. Call the specified function,
26 * and complain and return early if things go badly. */
27#if defined(NDEBUG)
28#define PRINT_ERROR(M)
29#define PRINTF_ERROR(M, ...)
30#define PRINT_SYSCALL_ERROR(M)
31#else
32#define PRINT_ERROR(M) do { if (za_verbose) { fprintf(stderr, "(%s: %d) " M\
33 "\n", __FILE__, __LINE__); } } while(0)
34#define PRINTF_ERROR(M, ...) do { if (za_verbose) { fprintf(stderr,\
35 "(%s: %d) " M "\n", __FILE__, __LINE__, __VA_ARGS__); } } while(0)
36#define PRINT_SYSCALL_ERROR(M) do { if (za_verbose) { fprintf(stderr,\
37 "(%s: %d) [ERROR] " M " failed with error code %d.\n",\
38 __FILE__, __LINE__, GetLastError()); } } while(0)
39#endif
40#define SYSCALL(F) do { if ((F) == 0) {\
41 PRINT_SYSCALL_ERROR(#F); return Z_ERROR_SYSTEM_ERROR; } } while(0)
42#define MIN(A, B) (((A) < (B)) ? (A) : (B))
43
44int za_connect(z_port *port, const char *port_name)
45{
46 DCB dcb = { 0 };
47 COMMTIMEOUTS timeouts;
48
49 if (port_name == NULL)
50 {
51 PRINT_ERROR("[ERROR] port name cannot be NULL.");
53 }
54 *port = CreateFileA(port_name,
55 GENERIC_READ | GENERIC_WRITE,
56 0,
57 NULL,
58 OPEN_EXISTING,
59 FILE_ATTRIBUTE_NORMAL,
60 0);
61 if (*port == INVALID_HANDLE_VALUE)
62 {
63 PRINT_SYSCALL_ERROR("CreateFileA");
65 }
66
67 SYSCALL(GetCommState(*port, &dcb));
68 dcb.DCBlength = sizeof(DCB);
69 dcb.BaudRate = 115200;
70 dcb.fBinary = TRUE; /* Binary Mode (skip EOF check) */
71 dcb.fParity = FALSE; /* Disable parity checking */
72 dcb.fOutxCtsFlow = FALSE; /* No CTS handshaking on output */
73 dcb.fOutxDsrFlow = FALSE; /* No DSR handshaking on output */
74 dcb.fDtrControl = DTR_CONTROL_DISABLE; /* Disable DTR Flow control */
75 dcb.fDsrSensitivity = FALSE; /* No DSR Sensitivity */
76 dcb.fTXContinueOnXoff = TRUE; /* Continue TX when Xoff sent */
77 dcb.fOutX = FALSE; /* Disable output X-ON/X-OFF */
78 dcb.fInX = FALSE; /* Disable input X-ON/X-OFF */
79 dcb.fErrorChar = FALSE; /* Disable Err Replacement */
80 dcb.fNull = FALSE; /* Disable Null stripping */
81 dcb.fRtsControl = RTS_CONTROL_DISABLE; /* Disable Rts Flow control */
82 dcb.fAbortOnError = FALSE; /* Do not abort all reads and writes on Error */
83 dcb.wReserved = 0; /* Not currently used, but must be set to 0 */
84 dcb.XonLim = 0; /* Transmit X-ON threshold */
85 dcb.XoffLim = 0; /* Transmit X-OFF threshold */
86 dcb.ByteSize = 8; /* Number of bits/byte, 4-8 */
87 dcb.Parity = NOPARITY; /* 0-4=None,Odd,Even,Mark,Space */
88 dcb.StopBits = ONESTOPBIT; /* 0,1,2 = 1, 1.5, 2 */
89 SYSCALL(SetCommState(*port, &dcb));
90
91 timeouts.ReadIntervalTimeout = MAXDWORD;
92 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
93 timeouts.ReadTotalTimeoutConstant = READ_TIMEOUT; /* #defined in header */
94 timeouts.WriteTotalTimeoutMultiplier = 0;
95 timeouts.WriteTotalTimeoutConstant = 100;
96 SYSCALL(SetCommTimeouts(*port, &timeouts));
97
98 return Z_SUCCESS;
99}
100
101int za_disconnect(z_port port)
102{
103 SYSCALL(CloseHandle(port));
104 return Z_SUCCESS;
105}
106
107int za_send(z_port port, const char *command)
108{
109 DWORD nlast,
110 length;
111 int written = 0;
112
113 if (command == NULL)
114 {
115 PRINT_ERROR("[ERROR] command cannot be NULL.");
117 }
118
119 if (command[0] != '/')
120 { /* send a '/' if they forgot it */
121 SYSCALL(WriteFile(port, "/", 1, &nlast, NULL));
122 written += nlast;
123 }
124
125 length = (DWORD) strlen(command);
126 SYSCALL(WriteFile(port, command, length, &nlast, NULL));
127 written += nlast;
128 if (nlast != length)
129 { /* WriteFile can return a short write: test for it */
131 }
132
133 if (length == 0 || command[length-1] != '\n')
134 { /* send a newline if they forgot it */
135 SYSCALL(WriteFile(port, "\n", 1, &nlast, NULL));
136 written += nlast;
137 }
138
139 return written;
140}
141
142/* Reads 1 byte at a time so that we can stop after reading '\n'.
143 * Though in theory it makes sense to read as much as possible (ie. length),
144 * the input buffer is likely to have multiple messages waiting, and we only
145 * want one at a time. Therefore we sacrifice speed (20+ system calls per
146 * message read) for reliability, and to not have to maintain our own buffer.
147 *
148 * Premature optimization opportunity: read the smallest number of bytes in a
149 * typical message, then read 1-at-a-time after that. Only a little silly. */
150int za_receive(z_port port, char *destination, int length)
151{
152 DWORD nlast;
153 char c;
154 int nread = 0; /* total bytes read */
155
156 for (;;)
157 {
158 SYSCALL(ReadFile(port, &c, 1, &nlast, NULL));
159
160 if (nlast == 0) /* timed out */
161 {
162 PRINTF_ERROR("[INFO] Read timed out after reading %d "
163 "bytes.", nread);
165 }
166
167 if (destination != NULL)
168 {
169 destination[nread] = c;
170 }
171 nread += nlast;
172
173 if (nread == length)
174 {
175 PRINTF_ERROR("[ERROR] Read destination buffer not large "
176 "enough. Recommended size: 256B. Your size: %dB.",
177 length);
179 }
180
181 if (c == '\n')
182 {
183 nread -= 2; /* prepare to cut off the "\r\n" */
184 if (nread < 0)
185 {
186 PRINT_ERROR("[ERROR] Reply too short. It is likely that "
187 "only a partial reply was read.");
189 }
190 if (destination != NULL)
191 {
192 destination[nread] = '\0'; /* chomp the "\r\n" */
193 }
194 return nread;
195 }
196 }
197}
198
199int za_setbaud(z_port port, int baud)
200{
201 DCB dcb;
202
203 if (baud != 9600 && baud != 19200 && baud != 38400 && baud != 57600
204 && baud != 115200)
205 {
206 PRINTF_ERROR("[ERROR] Invalid baud rate. Valid rates are "
207 "9600, 19200, 38400, 57600, and 115200 (default).\n"
208 "Your rate: %d.", baud);
210 }
211
212 SYSCALL(GetCommState(port, &dcb));
213 dcb.BaudRate = baud;
214 SYSCALL(SetCommState(port, &dcb));
215
216 return Z_SUCCESS;
217}
218
219/* First flushes the input buffer, then tries to read until nothing comes in.
220 * This unfortunately guarantees that this function will take at least 100ms
221 * to complete, as this function waits 100ms for additional input to be
222 * available before returning. This is necessary, as without it, this function
223 * would be essentially useless: it would frequently chop any incoming message
224 * in half, leaving the second half to be read later.
225 */
226int za_drain(z_port port)
227{
228 char c;
229 DWORD nread,
230 old_timeout;
231 COMMTIMEOUTS timeouts;
232
233 SYSCALL(PurgeComm(port, PURGE_RXCLEAR));
234 SYSCALL(GetCommTimeouts(port, &timeouts));
235 old_timeout = timeouts.ReadTotalTimeoutConstant;
236 timeouts.ReadTotalTimeoutConstant = 100;
237 SYSCALL(SetCommTimeouts(port, &timeouts));
238
239 do
240 {
241 SYSCALL(ReadFile(port, &c, 1, &nread, NULL));
242 }
243 while (nread == 1);
244
245 timeouts.ReadTotalTimeoutConstant = old_timeout;
246 SYSCALL(SetCommTimeouts(port, &timeouts));
247
248 return Z_SUCCESS;
249}
250
251#elif defined(__unix__) || defined(__APPLE__) /* end of if defined(_WIN32) */
252#include <fcntl.h>
253#include <sys/stat.h>
254#include <sys/types.h>
255#include <termios.h>
256#include <unistd.h>
257
258#if defined(NDEBUG)
259#define PRINT_ERROR(M)
260#define PRINTF_ERROR(M, ...)
261#define PRINT_SYSCALL_ERROR(M)
262#else
263#define PRINT_ERROR(M) do { if (za_verbose) { fprintf(stderr, "(%s: %d) " M\
264 "\n", __FILE__, __LINE__); } } while(0)
265#define PRINTF_ERROR(M, ...) do { if (za_verbose) { fprintf(stderr,\
266 "(%s: %d) " M "\n", __FILE__, __LINE__, __VA_ARGS__); } } while(0)
267#include <errno.h>
268#define PRINT_SYSCALL_ERROR(M) do { if (za_verbose) {\
269 fprintf(stderr, "(%s: %d) [ERROR] " M " failed: %s.\n",\
270 __FILE__, __LINE__, strerror(errno)); } } while(0)
271#endif
272/* A little sugar for checking return values from system calls.
273 * I would have liked to use GNU/GCC's "statement expressions" so that one
274 * do something like "z_port port = SYSCALL(open([parameters]))", but they're
275 * a GNU extension, and therefore unfriendly to non-GCC compilers.
276 * Workaround to avoid dependence on statement expressions for SYSCALL macro:
277 * use SYSCALL on result of assignment instead. */
278#define SYSCALL(F) do { if ((F) < 0) { PRINT_SYSCALL_ERROR(#F);\
279 return Z_ERROR_SYSTEM_ERROR; } } while(0)
280
281int za_connect(z_port *port, const char *port_name)
282{
283 struct termios tio, orig_tio;
284
285 if (port == NULL || port_name == NULL)
286 {
287 PRINT_ERROR("[ERROR] port and port_name cannot be NULL.");
289 }
290
291 /* blocking read/write */
292 SYSCALL(*port = open(port_name, O_RDWR | O_NOCTTY));
293 SYSCALL(tcgetattr(*port, &orig_tio));
294 memcpy(&tio, &orig_tio, sizeof(struct termios)); /* copy padding too */
295
296 /* cfmakeraw() without cfmakeraw() for cygwin compatibility */
297 tio.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR
298 | IGNCR | ICRNL | IXON);
299 tio.c_oflag &= ~OPOST;
300 tio.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
301 tio.c_cflag &= ~(CSIZE | PARENB);
302 /* end cfmakeraw() */
303 tio.c_cflag = CS8|CREAD|CLOCAL;
304
305 /* READ_TIMEOUT is defined in za_serial.h */
306 if (READ_TIMEOUT % 100 != 0)
307 {
308 tio.c_cc[VTIME] = READ_TIMEOUT / 100 + 1; /* Round up */
309 }
310 else
311 {
312 tio.c_cc[VTIME] = READ_TIMEOUT / 100;
313 }
314 tio.c_cc[VMIN] = 0;
315
316 SYSCALL(cfsetospeed(&tio, B115200) & cfsetispeed(&tio, B115200));
317
318 while(memcmp(&orig_tio, &tio, sizeof(struct termios)) != 0)
319 { /* memcmp is only OK here because we used memcpy above */
320 SYSCALL(tcsetattr(*port, TCSAFLUSH, &tio));
321 SYSCALL(tcgetattr(*port, &orig_tio));
322 }
323
324 return Z_SUCCESS;
325}
326
327int za_disconnect(z_port port)
328{
329 SYSCALL(close(port));
330 return Z_SUCCESS;
331}
332
333/* a subtle feature of za_send() that is implied but not explicitly documented:
334 * if you pass a pointer to a 0-length string (ie. just a \0) for command,
335 * za_send() will send the minimal command of "/\n" automagically, as it will
336 * assume you are sending a command without content, and that you have
337 * forgotten the leading '/' and trailing '\n'. Mention of this is probably
338 * best left out of the official docs since it's a hacky way to use za_send().
339 */
340int za_send(z_port port, const char *command)
341{
342 int nlast,
343 length,
344 written = 0;
345
346 if (command == NULL)
347 {
348 PRINT_ERROR("[ERROR] command cannot be NULL.");
350 }
351
352 if (command[0] != '/')
353 { /* send a '/' if they forgot it */
354 SYSCALL(nlast = write(port, "/", 1));
355 written += nlast;
356 }
357 length = strlen(command);
358 SYSCALL(nlast = write(port, command, length));
359 written += nlast;
360 if (nlast != length)
361 { /* write can return a short write: test for it */
362 PRINTF_ERROR("[ERROR] write did not write entire message: "
363 "could only write %d bytes of %d.", nlast, length);
365 }
366
367 if (length == 0 || command[length-1] != '\n')
368 { /* send a newline if they forgot it */
369 SYSCALL(nlast = write(port, "\n", 1));
370 written += nlast;
371 }
372
373 return written;
374}
375
376int za_receive(z_port port, char *destination, int length)
377{
378 int nread = 0,
379 nlast;
380 char c;
381
382 for (;;)
383 {
384 SYSCALL(nlast = (int) read(port, &c, 1));
385
386 if (nlast == 0) /* timed out */
387 {
388 PRINTF_ERROR("[INFO] Read timed out after reading %d "
389 "bytes.", nread);
391 }
392
393 if (destination != NULL)
394 {
395 destination[nread] = c;
396 }
397 nread += nlast;
398
399 if (nread == length)
400 {
401 PRINTF_ERROR("[ERROR] Read destination buffer not large "
402 "enough. Recommended size: 256B. Your size: %dB.",
403 length);
405 }
406
407 if (c == '\n')
408 {
409 nread -= 2; /* prepare to cut off the "\r\n" */
410 if (nread < 0)
411 {
412 PRINT_ERROR("[ERROR] Reply too short. It is likely that "
413 "only a partial reply was read.");
415 }
416 if (destination != NULL)
417 {
418 destination[nread] = '\0'; /* chomp the "\r\n" */
419 }
420 return nread;
421 }
422 }
423}
424
425int za_setbaud(z_port port, int baud)
426{
427 struct termios tio;
428 speed_t tbaud;
429 switch (baud)
430 {
431 case 9600:
432 tbaud = B9600;
433 break;
434 case 19200:
435 tbaud = B19200;
436 break;
437 case 38400:
438 tbaud = B38400;
439 break;
440 case 57600:
441 tbaud = B57600;
442 break;
443 case 115200:
444 tbaud = B115200;
445 break;
446 default:
447 PRINTF_ERROR("[ERROR] Invalid baud rate. Valid rates are "
448 "9600, 19200, 38400, 57600, and 115200 (default).\n"
449 "Your rate: %d.", baud);
451 }
452
453 SYSCALL(tcgetattr(port, &tio));
454 SYSCALL(cfsetospeed(&tio, tbaud) & cfsetispeed(&tio, tbaud));
455 SYSCALL(tcsetattr(port, TCSAFLUSH, &tio));
456
457 return Z_SUCCESS;
458}
459
460int za_drain(z_port port)
461{
462 struct termios tio;
463 int old_timeout;
464 char c;
465
466 /* set timeout to 0.1s */
467 SYSCALL(tcgetattr(port, &tio));
468 old_timeout = tio.c_cc[VTIME];
469 tio.c_cc[VTIME] = 1;
470 SYSCALL(tcsetattr(port, TCSANOW, &tio));
471
472 /* flush and read whatever else comes in */
473 SYSCALL(tcflush(port, TCIFLUSH));
474 while(read(port, &c, 1) > 0);
475
476 /* set timeout back to what it was */
477 tio.c_cc[VTIME] = old_timeout;
478 SYSCALL(tcsetattr(port, TCSANOW, &tio));
479
480 return Z_SUCCESS;
481}
482
483#endif /* if defined(__unix__) || defined(__APPLE__) -- OS detection */
484
485/* A helper for za_decode. Copies from source to destination, until delim or
486 * a '\0' is found, then null-terminates destination.
487 *
488 * Returns the number of bytes copied, including the added null-terminator. */
489static size_t copy_until_delim(char *destination, const char *source,
490 const char delim, size_t destination_length)
491{
492 size_t i;
493
494 for(i = 0; source[i] != delim && source[i] != '\0'
495 && i < destination_length - 1; i++)
496 {
497 destination[i] = source[i];
498 }
499
500 destination[i] = '\0'; /* null-terminate instead of copying delim */
501
502 return i + 1;
503}
504
505static int decode_reply(struct za_reply *destination, char *reply)
506{
507 char buffer[8]; /* needs to be at least 5B: set to 8 because
508 it will be padded to 8 from 5 anyway. */
509 size_t offset,
510 length;
511
512 if (strlen(reply) < 18)
513 {
514 PRINTF_ERROR("[ERROR] Reply could not be decoded: shorter than expected. "
515 "It is likely that only a partial reply was read.\n"
516 "Reply value: %s", reply);
518 }
519
520 destination->message_type = '@';
521
522 /* device address: 2 digits, 00-99
523 *
524 * The device address is part of the same "token" as the message type,
525 * so we call strtol on the same token, skipping the first char.
526 *
527 * We don't check the length here for future-proofing:
528 * if we add support for more device addresses (ie. more digits),
529 * this should handle it gracefully. */
530 offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
531 destination->device_address = (int) strtol(buffer + 1, NULL, 10);
532 reply += offset;
533
534 /* axis number: 1 digit, 0-9
535 *
536 * The axis number may be 2 digits (or more) in the future, but it's
537 * unlikely to go over 2^31 - 1 any time soon, so we convert to
538 * standard int and use that. */
539 offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
540 destination->axis_number = (int) strtol(buffer, NULL, 10);
541 reply += offset;
542
543 /* reply flags: 2 letters
544 *
545 * Only replies have reply flags. Value is either "OK" or "RJ". */
546 offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
547 if (offset > sizeof(destination->reply_flags))
548 {
549 PRINTF_ERROR("[ERROR] Reply could not be decoded: reply flags too "
550 "long. Maximum length: %lu. Your length: %lu. Reply flags "
551 "value: %s\n.", sizeof(destination->reply_flags), offset,
552 buffer);
554 }
555 strcpy(destination->reply_flags, buffer);
556 reply += offset;
557
558 /* device status: 4 letters
559 *
560 * Replies and alerts have a "status" field. Value is either "IDLE" or
561 * "BUSY", depending on what the device is doing at the time. */
562 offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
563 if (offset > sizeof(destination->device_status))
564 {
565 PRINTF_ERROR("[ERROR] Reply could not be decoded: device status too "
566 "long. Expected length: %lu. Your length: %lu. Device status "
567 "value: %s\n.", sizeof(destination->device_status), offset,
568 buffer);
570 }
571 strcpy(destination->device_status, buffer);
572 reply += offset;
573
574 /* warning flags: 2 letters
575 *
576 * Replies and alerts have warning flags. Warning flags are typically
577 * "--". All other possible warning flag values should be two
578 * consecutive capital letters. */
579 offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
580 if (offset > sizeof(destination->warning_flags))
581 {
582 PRINTF_ERROR("[ERROR] Reply could not be decoded: warning flags too "
583 "long. Expected length: %lu. Your length: %lu. Warning flags "
584 "value: %s\n.", sizeof(destination->warning_flags), offset,
585 buffer);
587 }
588 strcpy(destination->warning_flags, buffer);
589 reply += offset;
590
591 /* data: 1+ characters, probably less than 128 characters
592 *
593 * Replies and info have data. This can be anything, including spaces,
594 * numbers, and human-readable text. */
595 length = strlen(reply); /* get length of remaining data */
596 if (length > sizeof(destination->response_data))
597 {
598 PRINTF_ERROR("[ERROR] Reply could not be decoded: response data too "
599 "long. Maximum length: %lu. Your length: %lu. Data: %s.\n",
600 sizeof(destination->response_data), length, reply);
602 }
603 strcpy(destination->response_data, reply);
604
605 return Z_SUCCESS;
606
607}
608
609static int decode_alert(struct za_reply *destination, char *reply)
610{
611 size_t offset;
612 char buffer[8]; /* needs to be at least 5B: set to 8 because
613 it will be padded to 8 from 5 anyway. */
614
615 if (strlen(reply) < 13)
616 {
617 PRINTF_ERROR("[ERROR] Reply could not be decoded: shorter than expected. "
618 "It is likely that only a partial reply was read.\n"
619 "Reply value: %s", reply);
621 }
622
623 destination->message_type = '!';
624
625 /* device address: 2 digits, 00-99
626 *
627 * The device address is part of the same "token" as the message type,
628 * so we call strtol on the same token, skipping the first char.
629 *
630 * We don't check the length here for future-proofing:
631 * if we add support for more device addresses (ie. more digits),
632 * this should handle it gracefully. */
633 offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
634 destination->device_address = (int) strtol(buffer + 1, NULL, 10);
635 reply += offset;
636
637 /* axis number: 1 digit, 0-9
638 *
639 * The axis number may be 2 digits (or more) in the future, but it's
640 * unlikely to go over 2^31 - 1 any time soon, so we convert to
641 * standard int and use that. */
642 offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
643 destination->axis_number = (int) strtol(buffer, NULL, 10);
644 reply += offset;
645
646 destination->reply_flags[0] = '\0';
647
648 /* device status: 4 letters
649 *
650 * Replies and alerts have a "status" field. Value is either "IDLE" or
651 * "BUSY", depending on what the device is doing at the time. */
652 offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
653 if (offset > sizeof(destination->device_status))
654 {
655 PRINTF_ERROR("[ERROR] Reply could not be decoded: device status too "
656 "long. Expected length: %lu. Your length: %lu. Device status "
657 "value: %s\n.", sizeof(destination->device_status), offset,
658 buffer);
660 }
661 strcpy(destination->device_status, buffer);
662 reply += offset;
663
664 /* warning flags: 2 letters
665 *
666 * Replies and alerts have warning flags. Warning flags are typically
667 * "--". All other possible warning flag values should be two
668 * consecutive capital letters. */
669 offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
670 if (offset > sizeof(destination->warning_flags))
671 {
672 PRINTF_ERROR("[ERROR] Reply could not be decoded: warning flags too "
673 "long. Expected length: %lu. Your length: %lu. Warning flags "
674 "value: %s\n.", sizeof(destination->warning_flags), offset,
675 buffer);
677 }
678 strcpy(destination->warning_flags, buffer);
679
680 destination->response_data[0] = '\0';
681
682 return Z_SUCCESS;
683}
684
685static int decode_info(struct za_reply *destination, char *reply)
686{
687 size_t length,
688 offset;
689 char buffer[8]; /* needs to be at least 5B: set to 8 because
690 it will be padded to 8 from 5 anyway. */
691
692 if (strlen(reply) < 7)
693 {
694 PRINTF_ERROR("[ERROR] Reply could not be decoded: shorter than expected. "
695 "It is likely that only a partial reply was read.\n"
696 "Reply value: %s", reply);
698 }
699
700 destination->message_type = '#';
701
702 /* device address: 2 digits, 00-99
703 *
704 * The device address is part of the same "token" as the message type,
705 * so we call strtol on the same token, skipping the first char.
706 *
707 * We don't check the length here for future-proofing:
708 * if we add support for more device addresses (ie. more digits),
709 * this should handle it gracefully. */
710 offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
711 destination->device_address = (int) strtol(buffer + 1, NULL, 10);
712 reply += offset;
713
714 /* axis number: 1 digit, 0-9
715 *
716 * The axis number may be 2 digits (or more) in the future, but it's
717 * unlikely to go over 2^31 - 1 any time soon, so we convert to
718 * standard int and use that. */
719 offset = copy_until_delim(buffer, reply, ' ', sizeof(buffer));
720 destination->axis_number = (int) strtol(buffer, NULL, 10);
721 reply += offset;
722
723 destination->reply_flags[0] = '\0';
724 destination->device_status[0] = '\0';
725 destination->warning_flags[0] = '\0';
726
727 /* data: 1+ characters, probably less than 128 characters
728 *
729 * Replies and info have data. This can be anything, including spaces,
730 * numbers, and human-readable text. */
731 length = strlen(reply); /* get length of remaining data */
732 if (length > sizeof(destination->response_data))
733 {
734 PRINTF_ERROR("[ERROR] Reply could not be decoded: response data too "
735 "long. Maximum length: %lu. Your length: %lu. Data: %s.\n",
736 sizeof(destination->response_data), length, reply);
738 }
739 strcpy(destination->response_data, reply);
740
741 return Z_SUCCESS;
742}
743
744/* This function tokenizes using the above helper function to copy
745 * token-by-token from reply into buffer, or directly into destination,
746 * depending on the field in destination we are populating. It could probably
747 * be prettier, more clever, or more efficient, but it's written to be
748 * readable, reliable, and robust first.
749 *
750 * Note that we can safely use strcpy here because the above helper function is
751 * guaranteed to write a NUL at the end of its destination string.
752 *
753 * See http://www.zaber.com/wiki/Manuals/ASCII_Protocol_Manual#Replies for
754 * more info on replies.
755 */
756int za_decode(struct za_reply *destination, char *reply)
757{
758 char message_type;
759
760 if (destination == NULL || reply == NULL)
761 {
762 PRINT_ERROR("[ERROR] decoding requires both a non-NULL destination "
763 "and reply to decode.");
765 }
766
767 if (strlen(reply) == 0)
768 {
769 PRINT_ERROR("[ERROR] Reply could not be decoded: no data.");
771 }
772 message_type = reply[0];
773
774 /* most replies are 18 chars or longer: info and alerts can be shorter */
775 switch(message_type)
776 {
777 case '@':
778 return decode_reply(destination, reply);
779 case '!':
780 return decode_alert(destination, reply);
781 case '#':
782 return decode_info(destination, reply);
783 default:
784 PRINTF_ERROR("[ERROR] Reply could not be decoded: unexpected "
785 "message type. Valid types are '@' (reply), '!' (alert), "
786 "and '#' (info). Your type: '%c'. \n", message_type);
788 }
789}
790
#define READ_TIMEOUT
Definition z_common.h:53
@ Z_ERROR_COULD_NOT_DECODE
Definition z_common.h:76
@ Z_ERROR_SYSTEM_ERROR
Definition z_common.h:67
@ Z_ERROR_NULL_PARAMETER
Definition z_common.h:71
@ Z_SUCCESS
Definition z_common.h:65
@ Z_ERROR_INVALID_BAUDRATE
Definition z_common.h:73
@ Z_ERROR_BUFFER_TOO_SMALL
Definition z_common.h:69
#define SYSCALL(F)
Definition za_serial.c:50
static size_t copy_until_delim(char *destination, const char *source, const char delim, size_t destination_length)
Definition za_serial.c:263
static int decode_reply(struct za_reply *destination, const char *reply, size_t sMaxSz)
Definition za_serial.c:279
int za_connect(z_port *port, const char *port_name)
Definition za_serial.c:53
int za_setbaud(z_port port, int baud)
Definition za_serial.c:200
#define PRINT_ERROR(M)
Definition za_serial.c:35
void za_set_verbose(int value)
Definition za_serial.c:18
static int za_verbose
Definition za_serial.c:16
static int decode_info(struct za_reply *destination, const char *reply, size_t sMaxSz)
Definition za_serial.c:465
#define PRINT_SYSCALL_ERROR(M)
Definition za_serial.c:40
int za_receive(z_port port, char *destination, int length)
Definition za_serial.c:151
int za_decode(struct za_reply *destination, const char *reply, size_t sMaxSz)
Definition za_serial.c:539
static int decode_alert(struct za_reply *destination, const char *reply, size_t sMaxSz)
Definition za_serial.c:386
int za_send(z_port port, const char *command, size_t sMaxSz)
Definition za_serial.c:112
int za_drain(z_port port)
Definition za_serial.c:235
#define PRINTF_ERROR(M,...)
Definition za_serial.c:37
int za_disconnect(z_port port)
Definition za_serial.c:99
Provides a set of functions for interacting with Zaber devices in the ASCII protocol.
char device_status[5]
Definition za_serial.h:44
char message_type
Definition za_serial.h:35
int device_address
Definition za_serial.h:37
char reply_flags[3]
Definition za_serial.h:42
char warning_flags[3]
Definition za_serial.h:47
int axis_number
Definition za_serial.h:40
char response_data[128]
Definition za_serial.h:50