ARPDAS_QNX6 1.0
SerSelector.cc
Go to the documentation of this file.
00001 #include <errno.h>
00002 #include <string.h>
00003 #include <stdarg.h>
00004 #include <stdlib.h>
00005 #include <ctype.h>
00006 #include <termios.h>
00007 #include "SerSelector.h"
00008 #include "nortlib.h"
00009 #include "nl_assert.h"
00010 #include "msg.h"
00011 
00012 TM_Selectee::TM_Selectee(const char *name, void *data,
00013       unsigned short size) : Selectee() {
00014   init(name, data, size);
00015 }
00016 
00017 TM_Selectee::TM_Selectee() : Selectee() {
00018   TMid = 0;
00019 }
00020 
00021 void TM_Selectee::init(const char *name, void *data,
00022       unsigned short size) {
00023   TMid = Col_send_init( name, data, size, 0 );
00024   if ( TMid ) {
00025     fd = TMid->fd;
00026     flags = Selector::Sel_Write;
00027   }
00028 }
00029 
00030 /**
00031  * Issues Col_send_reset()
00032  */
00033 TM_Selectee::~TM_Selectee() {
00034   Col_send_reset(TMid);
00035   fd = -1;
00036 }
00037 
00038 /**
00039  * Calls Col_send() and sets gflag(0)
00040  */
00041 int TM_Selectee::ProcessData(int flag) {
00042   Col_send(TMid);
00043   Stor->set_gflag(0);
00044   return 0;
00045 }
00046 
00047 /**
00048  * Opens the specified channel using tm_dev_name() and
00049  * tm_open_name(). By default, tm_open_name will produce a
00050  * fatal error if the resulting path is not found.
00051  */
00052 Cmd_Selectee::Cmd_Selectee( const char *name ) :
00053     Ser_Sel(tm_dev_name(name), O_RDONLY, 0) {}
00054 Cmd_Selectee::Cmd_Selectee( const char *name, int bufsz ) :
00055     Ser_Sel(tm_dev_name(name), O_RDONLY, bufsz) {}
00056 
00057 int Cmd_Selectee::ProcessData(int flag) {
00058   return 1;
00059 }
00060 
00061 /**
00062  * @param path The full path to the serial device. If path == NULL,
00063  * the fd will not be opened.
00064  * @param open_flags Flags from <fcntl.h> passed to open()
00065  * @param bufsz The size buffer to be allocated.
00066  */
00067 Ser_Sel::Ser_Sel(const char *path, int open_flags, int bufsz )
00068     : Selectee() {
00069   sersel_init();
00070   init(path, open_flags, bufsz);
00071 }
00072 
00073 Ser_Sel::Ser_Sel() : Selectee() {
00074   sersel_init();
00075 }
00076 
00077 /**
00078  * @param path The full path to the serial device. If path == NULL,
00079  * the fd will not be opened.
00080  * @param open_flags Flags from <fcntl.h> passed to open()
00081  * @param bufsz The size buffer to be allocated.
00082  */
00083 void Ser_Sel::init(const char *path, int open_flags, int bufsz) {
00084   if (path == 0) {
00085     fd = -1;
00086   } else {
00087     fd = tm_open_name(path, NULL, open_flags);
00088     switch (open_flags & O_ACCMODE) {
00089       case O_RDWR:
00090       case O_RDONLY:
00091         flags |= Selector::Sel_Read;
00092         break;
00093     }
00094   }
00095   if (bufsz > 0) {
00096     buf = (unsigned char *)new_memory(bufsz);
00097     bufsize = bufsz;
00098   }
00099 }
00100 
00101 void Ser_Sel::sersel_init() {
00102   fd = -1;
00103   buf = 0;
00104   bufsize = 0;
00105   nc = cp = 0;
00106   n_fills = n_empties = 0;
00107   n_eagain = n_eintr = 0;
00108   total_errors = 0;
00109   total_suppressed = 0;
00110   n_errors = 0;
00111   n_suppressed = 0;
00112 }
00113 
00114 /**
00115  * Frees the allocated buffer and reports statistics.
00116  */
00117 Ser_Sel::~Ser_Sel() {
00118   if (buf) free_memory(buf);
00119   buf = 0;
00120   nl_error( 0, "n_fills: %d  n_empties: %d "
00121     "total_errors: %d total_suppressed: %d",
00122     n_fills, n_empties, total_errors, total_suppressed );
00123   nl_error( 0, "n_eagain: %d n_eintr: %d", n_eagain, n_eintr);
00124 }
00125 
00126 /**
00127  * Initializes the serial parameters for the device. The min
00128  * and time parameters can be used to optimize reads. See
00129  * tcsetattr VMIN and VTIME parameters for more information.
00130  * @param baud The desired baud rate
00131  * @param bits number of data bits (5-8)
00132  * @param par 'n', 'e', 'o', 'm', 's' for none, even, odd, mark or space.
00133  * @param stopbits The number of stop bits: 1 or 2
00134  * @param min The minimum number of characters to respond to
00135  * @param time The time gap value
00136  */
00137 void Ser_Sel::setup( int baud, int bits, char par, int stopbits,
00138                 int min, int time ) {
00139   struct termios termios_p;
00140   int bitsflag;
00141 
00142   if ( fd < 0 ) return;
00143   if ( tcgetattr( fd, &termios_p) ) {
00144     nl_error( 2, "Error on tcgetattr: %s", strerror(errno) );
00145     return;
00146   }
00147   termios_p.c_iflag = 0;
00148   termios_p.c_lflag &= ~(ECHO|ICANON|ISIG|ECHOE|ECHOK|ECHONL);
00149   termios_p.c_cflag = CLOCAL|CREAD;
00150   termios_p.c_oflag &= ~(OPOST);
00151   termios_p.c_ispeed = termios_p.c_ospeed = baud;
00152   switch (bits) {
00153     case 5: bitsflag = CS5; break;
00154     case 6: bitsflag = CS6; break;
00155     case 7: bitsflag = CS7; break;
00156     case 8: bitsflag = CS8; break;
00157     default:
00158       nl_error( 3, "Invalid bits value: %d", bits );
00159   }
00160   termios_p.c_cflag |= bitsflag;
00161   switch (par) {
00162     case 'n': bitsflag = 0; break;
00163     case 'e': bitsflag = PARENB; break;
00164     case 'o': bitsflag = PARENB|PARODD; break;
00165     case 'm': bitsflag = PARENB|PARODD|PARSTK; break;
00166     case 's': bitsflag = PARENB|PARSTK; break;
00167     default:
00168       nl_error( 3, "Invalid parity selector: '%c'", par );
00169   }
00170   termios_p.c_cflag |= bitsflag;
00171   switch (stopbits) {
00172     case 1: break;
00173     case 2: termios_p.c_cflag |= CSTOPB; break;
00174     default:
00175       nl_error(3, "Invalid number of stop bits: %d", stopbits );
00176   }
00177   termios_p.c_cc[VMIN] = min;
00178   termios_p.c_cc[VTIME] = time;
00179   if ( tcsetattr(fd, TCSANOW, &termios_p) )
00180     nl_error( 2, "Error on tcsetattr: %s", strerror(errno) );
00181 }
00182 
00183 /**
00184  * Reads characters from the device, reporting any errors.
00185  * Guarantees that buf is NUL-terminated, and sets nc to the
00186  * total number of characters. Each call to fillbuf() increments
00187  * the n_fills counter, which is reported at termination
00188  * @return non-zero on error.
00189  */
00190 int Ser_Sel::fillbuf() {
00191   int i;
00192   if (!buf) nl_error(4, "Ser_Sel::fillbuf with no buffer");
00193   ++n_fills;
00194   i = read( fd, &buf[nc], bufsize - 1 - nc );
00195   if ( i < 0 ) {
00196     if ( errno == EAGAIN ) {
00197       ++n_eagain;
00198     } else if (errno == EINTR) {
00199       ++n_eintr;
00200     } else {
00201       nl_error( 2, "Error %d on read from serial port", errno );
00202       return 1;
00203     }
00204     return 0;
00205   }
00206   nc += i;
00207   buf[nc] = '\0';
00208   return 0;
00209 }
00210 
00211 /**
00212  * Each call to consume() increments the n_empties counter which is
00213  * reported at termination. If n_fills is much greater than n_empties,
00214  * you may need to adjust your min and time settings for more efficient
00215  * operation.
00216  * @param nchars number of characters to remove from front of buffer
00217  */
00218 void Ser_Sel::consume(int nchars) {
00219   if ( nchars > 0 ) {
00220     ++n_empties;
00221     if ( nchars < nc ) {
00222       int nb = nc - nchars;
00223       memmove(&buf[0], &buf[nchars], nb+1);
00224       nc = nb;
00225     } else {
00226       nc = 0;
00227     }
00228     cp = 0;
00229   }
00230 }
00231 
00232 /**
00233  * Invokes fillbuf() until there is no input remaining.
00234  */
00235 void Ser_Sel::flush_input() {
00236   do {
00237     nc = cp = 0;
00238     if (fillbuf()) return;
00239   } while (nc > 0);
00240 }
00241 
00242 #define QERR_THRESHOLD 5
00243 /**
00244  * Reports the error message, provided the qualified error count
00245  * is not above the limit. report_ok() will decrement the qualified
00246  * error count. It is assumed that all messages are of severity
00247  * MSG_ERR. Messages at other levels, either more or less severe,
00248  * should be sent directly to msg().
00249  */
00250 void Ser_Sel::report_err( const char *fmt, ... ) {
00251   ++total_errors;
00252   if ( n_errors < QERR_THRESHOLD )
00253     ++n_errors;
00254   if ( n_suppressed == 0 && n_errors < QERR_THRESHOLD ) {
00255     va_list args;
00256 
00257     va_start(args, fmt);
00258     msgv( 2, fmt, args );
00259     va_end(args);
00260     if (nc)
00261       msg( 2, "Input was: '%s'", ascii_escape((char*)buf, nc) );
00262   } else {
00263     if ( !n_suppressed )
00264       msg( 2, "Error threshold reached: suppressing errors" );
00265     ++n_suppressed;
00266     ++total_suppressed;
00267   }
00268 }
00269 
00270 /**
00271  * Indicate that data has successfully been received.
00272  */
00273 void Ser_Sel::report_ok() {
00274   if ( n_errors > 0 ) {
00275     if ( --n_errors <= 0 && n_suppressed ) {
00276       msg( 0, "Error recovery: %d error messages suppressed", n_suppressed );
00277       n_suppressed = 0;
00278     }
00279   }
00280 }
00281 
00282 /**
00283  * Parsing utility function that searches forward in the buffer for the
00284  * specified start character. Updates cp to point just past the start
00285  * char. If the character is not found, the buffer is emptied.
00286  * @param c The search character
00287  * @return zero if the character is found.
00288  */
00289 int Ser_Sel::not_found(unsigned char c) {
00290   while ( cp < nc ) {
00291     if ( buf[cp++] == c )
00292       return 0;
00293   }
00294   if ( nc ) {
00295     report_err( "Synch char '%c' not found", c );
00296     nc = cp = 0;
00297   }
00298   return 1;
00299 }
00300 
00301 /**
00302  * Parsing utility function to read in a hex integer starting
00303  * at the current position. Integer may be proceeded by optional
00304  * whitespace.
00305  * @param[out] hexval The integer value
00306  * @return zero if an integer was converted, non-zero if the current char is not a digit.
00307  */
00308 int Ser_Sel::not_hex( unsigned short &hexval ) {
00309   while (cp < nc && isspace(buf[cp]))
00310     ++cp;
00311   if (! isxdigit(buf[cp])) {
00312     if (cp < nc)
00313       report_err("No hex digits at col %d", cp);
00314     return 1;
00315   }
00316   while ( isxdigit(buf[cp]) ) {
00317     unsigned short digval = isdigit(buf[cp]) ? ( buf[cp] - '0' ) :
00318            ( tolower(buf[cp]) - 'a' + 10 );
00319     hexval = hexval * 16 + digval;
00320     ++cp;
00321   }
00322   return 0;
00323 }
00324 
00325 /**
00326  * Parsing utility function to read in a decimal integer starting
00327  * at the current position. Integer may be proceeded by optional
00328  * whitespace and an optional sign.
00329  * @param[out] val The integer value
00330  * @return zero if an integer was converted, non-zero if the current char is not a digit.
00331  */
00332 int Ser_Sel::not_int( int &val ) {
00333   bool negative = false;
00334   // fillbuf() guarantees the buffer will be NUL-terminated, so any check
00335   // that will fail on a NUL is OK without checking the cp < nc
00336   while (isspace(buf[cp]))
00337     ++cp;
00338   if (buf[cp] == '-') {
00339     negative = true;
00340     ++cp;
00341   } else if (buf[cp] == '+') ++cp;
00342   if ( isdigit(buf[cp]) ) {
00343     val = buf[cp++] - '0';
00344     while ( isdigit(buf[cp]) ) {
00345       val = 10*val + buf[cp++] - '0';
00346     }
00347     if (negative) val = -val;
00348     return 0;
00349   } else {
00350     if ( cp < nc )
00351       report_err( "Expected int at column %d", cp );
00352     return 1;
00353   }
00354 }
00355 
00356 /**
00357  * Parsing utility function to check that the string matches the
00358  * input at the current position. On success, advances cp to just
00359  * after the matched string. On failure, cp points to the first
00360  * character that does not match. If only a partial record was
00361  * received, that could be the NUL at the end of the buffer.
00362  * @param str The comparison string.
00363  * @return zero if the string matches the input buffer.
00364  */
00365 int Ser_Sel::not_str( const char *str_in, unsigned int len ) {
00366   unsigned int start_cp = cp;
00367   unsigned int i;
00368   const unsigned char *str = (const unsigned char *)str_in;
00369   if ( cp < 0 || cp > nc || nc < 0 || buf == 0 )
00370     nl_error( 4, "Ser_Sel precondition failed: "
00371       "cp = %d, nc = %d, buf %s",
00372       cp, nc, buf ? "not NULL" : "is NULL" );
00373   for (i = 0; i < len; ++i) {
00374     if ( str[i] != buf[start_cp+i] ) {
00375       if ( cp < nc )
00376         report_err( "Expected string '%s' at column %d",
00377           ascii_escape(str_in, len), start_cp );
00378       return 1;
00379     }
00380     ++cp;
00381   }
00382   return 0;
00383 }
00384 
00385 int Ser_Sel::not_str( const char *str ) {
00386   return not_str(str, strlen(str));
00387 }
00388 
00389 int Ser_Sel::not_str(const std::string &s) {
00390   return not_str(s.c_str(), s.length());
00391 }
00392 
00393 
00394 /**
00395  * Parsing utility function to convert a string in the input
00396  * buffer to a float value. Updates cp to point just after the
00397  * converted string on success.
00398  * @param val[out] The converted value
00399  * @return zero if the conversion succeeded.
00400  */
00401 int Ser_Sel::not_float( float &val ) {
00402   char *endptr;
00403   int ncf;
00404   if ( cp < 0 || cp > nc || nc < 0 || nc >= bufsize || buf == 0 )
00405     msg( 4, "Ser_Sel precondition failed: "
00406       "cp = %d, nc = %d, bufsize = %d, buf %s",
00407       cp, nc, bufsize, buf ? "not NULL" : "is NULL" );
00408   val = strtof( (char*)&buf[cp], &endptr );
00409   ncf = endptr - (char*)&buf[cp];
00410   if ( ncf == 0 ) {
00411     if ( cp < nc )
00412       report_err( "Expected float at column %d", cp );
00413     return 1;
00414   } else {
00415     nl_assert( ncf > 0 && cp + ncf <= nc );
00416     cp += ncf;
00417     return 0;
00418   }
00419 }
00420 
00421 const char *ascii_escape(const char *ibuf, int len) {
00422   static std::string s;
00423   char snbuf[8];
00424   int ix = 0, nb;
00425   s.clear();
00426   while (ix < len ) {
00427     unsigned char c = ibuf[ix++];
00428     if ( isprint(c) ) {
00429       s.push_back(c);
00430     } else {
00431       switch ( c ) {
00432         case '\n':
00433           s.push_back('\\');
00434           s.push_back('n');
00435           break;
00436         case '\r':
00437           s.push_back('\\');
00438           s.push_back('r');
00439           break;
00440         case '\t':
00441           s.push_back('\\');
00442           s.push_back('t');
00443           break;
00444         default:
00445           nb = snprintf( snbuf, 8, "\\x%02x", c);
00446           s.append(snbuf);
00447           break;
00448       }
00449     }
00450   }
00451   return s.c_str();
00452 }
00453 
00454 const char *ascii_escape(const std::string &s) {
00455   return ascii_escape(s.c_str(), s.length());
00456 }
00457 
00458 /**
00459  * Named differently to disambiguate from C version
00460  * in nortlib2. Invokes the C++ versions, which
00461  * have no inherent length limitation and can deal
00462  * with embedded NULs.
00463  */
00464 const char *ascii_esc(const char *str) {
00465   return ascii_escape(str, strlen(str));
00466 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Defines