API
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 
16 static int za_verbose = 1;
17 
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 
44 int 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");
64  return Z_ERROR_SYSTEM_ERROR;
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 
101 int za_disconnect(z_port port)
102 {
103  SYSCALL(CloseHandle(port));
104  return Z_SUCCESS;
105 }
106 
107 int 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.");
116  return Z_ERROR_NULL_PARAMETER;
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 */
130  return Z_ERROR_SYSTEM_ERROR;
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. */
150 int 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);
164  return Z_ERROR_SYSTEM_ERROR;
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.");
188  return Z_ERROR_SYSTEM_ERROR;
189  }
190  if (destination != NULL)
191  {
192  destination[nread] = '\0'; /* chomp the "\r\n" */
193  }
194  return nread;
195  }
196  }
197 }
198 
199 int 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  */
226 int 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 
281 int 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.");
288  return Z_ERROR_NULL_PARAMETER;
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 
327 int 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  */
340 int 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.");
349  return Z_ERROR_NULL_PARAMETER;
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);
364  return Z_ERROR_SYSTEM_ERROR;
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 
376 int 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);
390  return Z_ERROR_SYSTEM_ERROR;
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.");
414  return Z_ERROR_SYSTEM_ERROR;
415  }
416  if (destination != NULL)
417  {
418  destination[nread] = '\0'; /* chomp the "\r\n" */
419  }
420  return nread;
421  }
422  }
423 }
424 
425 int 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 
460 int 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. */
489 static 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 
505 static 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 
609 static 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 
685 static 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  */
756 int 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.");
764  return Z_ERROR_NULL_PARAMETER;
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 
GeneratorWrapper< T > value(T &&value)
Definition: catch.hpp:4001
#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
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
#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
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
static int decode_reply(struct za_reply *destination, char *reply)
Definition: za_serial.c:505
static size_t copy_until_delim(char *destination, const char *source, const char delim, size_t destination_length)
Definition: za_serial.c:489
static int decode_alert(struct za_reply *destination, char *reply)
Definition: za_serial.c:609
static int za_verbose
Definition: za_serial.c:16
static int decode_info(struct za_reply *destination, char *reply)
Definition: za_serial.c:685
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