From: <rus...@us...> - 2008-02-16 06:25:12
|
Revision: 63 http://gearbox.svn.sourceforge.net/gearbox/?rev=63&view=rev Author: russo2503v Date: 2008-02-15 22:25:16 -0800 (Fri, 15 Feb 2008) Log Message: ----------- serial and sick submit Modified Paths: -------------- gearbox/trunk/submitted/CMakeLists.txt Added Paths: ----------- gearbox/trunk/submitted/gbxserialacfr/ gearbox/trunk/submitted/gbxserialacfr/CMakeLists.txt gearbox/trunk/submitted/gbxserialacfr/doc.dox gearbox/trunk/submitted/gbxserialacfr/gbxserialacfr.h gearbox/trunk/submitted/gbxserialacfr/lockfile/ gearbox/trunk/submitted/gbxserialacfr/lockfile/CMakeLists.txt gearbox/trunk/submitted/gbxserialacfr/lockfile/lockfile.cpp gearbox/trunk/submitted/gbxserialacfr/lockfile/lockfile.h gearbox/trunk/submitted/gbxserialacfr/lockfile/test/ gearbox/trunk/submitted/gbxserialacfr/lockfile/test/CMakeLists.txt gearbox/trunk/submitted/gbxserialacfr/lockfile/test/locktest.cpp gearbox/trunk/submitted/gbxserialacfr/serial.cpp gearbox/trunk/submitted/gbxserialacfr/serial.h gearbox/trunk/submitted/gbxserialacfr/test/ gearbox/trunk/submitted/gbxserialacfr/test/CMakeLists.txt gearbox/trunk/submitted/gbxserialacfr/test/serialechotest.cpp gearbox/trunk/submitted/gbxserialacfr/test/serialloopbacktest gearbox/trunk/submitted/gbxserialacfr/test/serialloopbacktest.cpp gearbox/trunk/submitted/gbxserialacfr/uncopyable.h gearbox/trunk/submitted/gbxsickacfr/ gearbox/trunk/submitted/gbxsickacfr/CMakeLists.txt gearbox/trunk/submitted/gbxsickacfr/doc.dox gearbox/trunk/submitted/gbxsickacfr/driver.cpp gearbox/trunk/submitted/gbxsickacfr/driver.h gearbox/trunk/submitted/gbxsickacfr/gbxiceutilacfr/ gearbox/trunk/submitted/gbxsickacfr/gbxiceutilacfr/CMakeLists.txt gearbox/trunk/submitted/gbxsickacfr/gbxiceutilacfr/buffer.h gearbox/trunk/submitted/gbxsickacfr/gbxiceutilacfr/gbxiceutilacfr.h gearbox/trunk/submitted/gbxsickacfr/gbxiceutilacfr/subsystemthread.cpp gearbox/trunk/submitted/gbxsickacfr/gbxiceutilacfr/subsystemthread.h gearbox/trunk/submitted/gbxsickacfr/gbxiceutilacfr/thread.cpp gearbox/trunk/submitted/gbxsickacfr/gbxiceutilacfr/thread.h gearbox/trunk/submitted/gbxsickacfr/gbxiceutilacfr/timer.cpp gearbox/trunk/submitted/gbxsickacfr/gbxiceutilacfr/timer.h gearbox/trunk/submitted/gbxsickacfr/gbxserialdeviceacfr/ gearbox/trunk/submitted/gbxsickacfr/gbxserialdeviceacfr/CMakeLists.txt gearbox/trunk/submitted/gbxsickacfr/gbxserialdeviceacfr/gbxserialdeviceacfr.h gearbox/trunk/submitted/gbxsickacfr/gbxserialdeviceacfr/serialdevicehandler.cpp gearbox/trunk/submitted/gbxsickacfr/gbxserialdeviceacfr/serialdevicehandler.h gearbox/trunk/submitted/gbxsickacfr/gbxutilacfr/ gearbox/trunk/submitted/gbxsickacfr/gbxutilacfr/CMakeLists.txt gearbox/trunk/submitted/gbxsickacfr/gbxutilacfr/exceptions.cpp gearbox/trunk/submitted/gbxsickacfr/gbxutilacfr/exceptions.h gearbox/trunk/submitted/gbxsickacfr/gbxutilacfr/gbxutilacfr.h gearbox/trunk/submitted/gbxsickacfr/gbxutilacfr/mathdefs.h gearbox/trunk/submitted/gbxsickacfr/gbxutilacfr/status.h gearbox/trunk/submitted/gbxsickacfr/gbxutilacfr/substatus.h gearbox/trunk/submitted/gbxsickacfr/gbxutilacfr/tracer.h gearbox/trunk/submitted/gbxsickacfr/gbxutilacfr/trivialstatus.cpp gearbox/trunk/submitted/gbxsickacfr/gbxutilacfr/trivialstatus.h gearbox/trunk/submitted/gbxsickacfr/gbxutilacfr/trivialtracer.cpp gearbox/trunk/submitted/gbxsickacfr/gbxutilacfr/trivialtracer.h gearbox/trunk/submitted/gbxsickacfr/messages.cpp gearbox/trunk/submitted/gbxsickacfr/messages.h gearbox/trunk/submitted/gbxsickacfr/serialhandler.cpp gearbox/trunk/submitted/gbxsickacfr/serialhandler.h gearbox/trunk/submitted/gbxsickacfr/sickdefines.cpp gearbox/trunk/submitted/gbxsickacfr/sickdefines.h gearbox/trunk/submitted/gbxsickacfr/test/ gearbox/trunk/submitted/gbxsickacfr/test/CMakeLists.txt gearbox/trunk/submitted/gbxsickacfr/test/test.cpp Modified: gearbox/trunk/submitted/CMakeLists.txt =================================================================== --- gearbox/trunk/submitted/CMakeLists.txt 2008-02-12 02:02:21 UTC (rev 62) +++ gearbox/trunk/submitted/CMakeLists.txt 2008-02-16 06:25:16 UTC (rev 63) @@ -10,7 +10,7 @@ # When adding new directories, please maintain order of inter-dependencies. # Otherwise, maintain alphabetical order. -# ADD_SUBDIRECTORY( gbxserialacfr ) -# ADD_SUBDIRECTORY( gbxsickacfr ) + ADD_SUBDIRECTORY( gbxserialacfr ) + ADD_SUBDIRECTORY( gbxsickacfr ) ENDIF ( GBX_BUILD_SUBMITTED ) \ No newline at end of file Added: gearbox/trunk/submitted/gbxserialacfr/CMakeLists.txt =================================================================== --- gearbox/trunk/submitted/gbxserialacfr/CMakeLists.txt (rev 0) +++ gearbox/trunk/submitted/gbxserialacfr/CMakeLists.txt 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,34 @@ +SET ( lib_name GbxSerialAcfr ) +GBX_ADD_LICENSE( LGPL ) + +SET ( build TRUE ) +GBX_REQUIRE_OPTION( build LIB ${lib_name} ON ) +GBX_REQUIRE_VAR( build LIB ${lib_name} GBX_OS_LINUX "only Linux OS is supported" ) + +SET( dep_libs GbxLockFileAcfr ) +# this is currently internal, so we don't have to check it +# GBX_REQUIRE_TARGETS( build LIB ${lib_name} ${dep_libs} ) + +IF ( build ) + + ADD_SUBDIRECTORY( lockfile ) + + INCLUDE( ${GBX_CMAKE_DIR}/UseBasicRules.cmake ) + + # for config.h + INCLUDE_DIRECTORIES( ${PROJECT_BINARY_DIR} ) + + FILE( GLOB hdrs *.h ) + FILE( GLOB srcs *.cpp ) + + GBX_ADD_LIBRARY( ${lib_name} SHARED ${srcs} ) + TARGET_LINK_LIBRARIES( ${lib_name} ${dep_libs} ) + + GBX_ADD_HEADERS( gbxserialacfr ${hdrs} ) + + IF ( GBX_BUILD_TESTS ) + ADD_SUBDIRECTORY ( test ) + ENDIF ( GBX_BUILD_TESTS ) + +ENDIF ( build ) + Added: gearbox/trunk/submitted/gbxserialacfr/doc.dox =================================================================== --- gearbox/trunk/submitted/gbxserialacfr/doc.dox (rev 0) +++ gearbox/trunk/submitted/gbxserialacfr/doc.dox 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,43 @@ +/* + * Orca-Robotics Project: Components for robotics + * http://orca-robotics.sf.net/ + * Copyright (c) 2004-2008 Mathew Ridley, Alex Brooks + * + * This distribution is licensed to you under the terms described in + * the LICENSE file included in this distribution. + * + */ +/*! +@ingroup gbx_libs +@ingroup gbx_cpp +@ingroup gbx_linux +@defgroup gbx_library_gbxserialacfr libGbxSerialAcfr +@brief Simple serial port interface + +@par General notes + +C++ class wrapping a serial port. For a full list of functions and classes see @ref gbxserialacfr. + +Header file: +@verbatim +#include <gbxserialacfr/serial.h> +@endverbatim + +Other serial libraries: +- http://libserial.sourceforge.net (GPL) +- http://qextserialport.sourceforge.net (requires Qt) + +@par Responsible Developer +Alex Brooks + +*/ + +/*! +@brief Simple serial port interface +@namespace gbxserialacfr + +This namespace is part of a library which contains C++ class for wrapping a serial port. + +@see @ref gbx_library_gbxserialacfr + +*/ \ No newline at end of file Added: gearbox/trunk/submitted/gbxserialacfr/gbxserialacfr.h =================================================================== --- gearbox/trunk/submitted/gbxserialacfr/gbxserialacfr.h (rev 0) +++ gearbox/trunk/submitted/gbxserialacfr/gbxserialacfr.h 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,15 @@ +/* + * Orca-Robotics Project: Components for robotics + * http://orca-robotics.sf.net/ + * Copyright (c) 2004-2008 Mathew Ridley, Alex Brooks + * + * This distribution is licensed to you under the terms described in + * the LICENSE file included in this distribution. + * + */ +#ifndef GBXSERIALACFR_SERIAL_GBXSERIALACFR_SERIAL_H_ +#define GBXSERIALACFR_SERIAL_GBXSERIALACFR_SERIAL_H_ + +#include <gbxserialacfr/serial.h> + +#endif Added: gearbox/trunk/submitted/gbxserialacfr/lockfile/CMakeLists.txt =================================================================== --- gearbox/trunk/submitted/gbxserialacfr/lockfile/CMakeLists.txt (rev 0) +++ gearbox/trunk/submitted/gbxserialacfr/lockfile/CMakeLists.txt 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,26 @@ +SET ( lib_name GbxLockFileAcfr ) +GBX_ADD_LICENSE( LGPL ) + +SET ( build TRUE ) +# don't give user an option +# GBX_REQUIRE_OPTION( build LIB ${lib_name} ON ) +# this was already tested in the dir above +# GBX_REQUIRE_VAR( build LIB ${lib_name} GBX_OS_LINUX "only Linux OS is supported" ) + +IF ( build ) + + INCLUDE( ${GBX_CMAKE_DIR}/UseBasicRules.cmake ) + + FILE( GLOB hdrs *.h ) + FILE( GLOB srcs *.cpp ) + + GBX_ADD_LIBRARY( ${lib_name} SHARED ${srcs} ) + TARGET_LINK_LIBRARIES( ${lib_name} ${dep_libs} ) + + GBX_ADD_HEADERS( gbxserialacfr/lockfile ${hdrs} ) + + IF ( GBX_BUILD_TESTS ) + ADD_SUBDIRECTORY ( test ) + ENDIF ( GBX_BUILD_TESTS ) + +ENDIF ( build ) Added: gearbox/trunk/submitted/gbxserialacfr/lockfile/lockfile.cpp =================================================================== --- gearbox/trunk/submitted/gbxserialacfr/lockfile/lockfile.cpp (rev 0) +++ gearbox/trunk/submitted/gbxserialacfr/lockfile/lockfile.cpp 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,183 @@ +/* + * Orca-Robotics Project: Components for robotics + * http://orca-robotics.sf.net/ + * Copyright (c) 2004-2008 Alex Brooks + * + * This distribution is licensed to you under the terms described in + * the LICENSE file included in this distribution. + * + */ +#include "lockfile.h" +#include <cstring> +#include <cstdlib> +#include <iostream> +#include <signal.h> +#include <sys/types.h> +#include <errno.h> +#include <sstream> +#include <libgen.h> + +using namespace std; + +namespace gbxserialacfr { +namespace lockfile { + +namespace { + + const char *LOCK_DIR = "/var/lock"; + + // This function is inefficient, but avoids + // all the weirdness of basename(). + std::string getBasename( const char *path ) + { + char *pathCopy = strdup( path ); + char *base = basename( pathCopy ); + std::string baseString = base; + free( pathCopy ); + return baseString; + } + + void + lock_dev(const char *dev, int lpid) + { + FILE *fd; + char pbuf[260], lbuf[260]; + int ret; + + std::string devBase = getBasename( dev ); + + sprintf(pbuf, "%s/PID..%d", LOCK_DIR, getpid()); + sprintf(lbuf, "%s/LCK..%s", LOCK_DIR, devBase.c_str()); + + // Create a file with our PID + fd = fopen(pbuf, "w"); + if (fd == 0) + { + stringstream ss; + ss << "Failed to open file '"<<pbuf<<"' for writing: "<<strerror(errno); + throw LockFileException( ss.str() ); + } + fprintf(fd, "%10d\n", lpid); + ret = fclose(fd); + if ( ret != 0 ) + { + stringstream ss; ss << "Problem closing lockfile '"<<pbuf<<"': " << strerror(errno); + throw LockFileException( ss.str() ); + } + + // Create a file for the device + if (link(pbuf, lbuf) < 0) + { + // A file already exists for the device... Have a look at the PID + fd = fopen(lbuf, "r"); + if ( fd == 0 ) + { + unlink(pbuf); + stringstream ss; + ss << "Couldn't open file '"<<lbuf<<"' for reading: " << strerror(errno); + throw LockFileException( ss.str() ); + } + + // Read the PID of the locker + int pidOfLocker; + ret = fscanf( fd, "%d", &pidOfLocker ); + fclose(fd); + + if ( ret != 1 ) + { + unlink(pbuf); + throw LockFileException( "Couldn't read PID of locker" ); + } + if ( pidOfLocker <= 0 ) + { + unlink(pbuf); + stringstream ss; ss << "Invalid PID of locker: " << pidOfLocker; + throw LockFileException( ss.str() ); + } + + if ( pidOfLocker == lpid ) + { + // I'm the locker ?!?! + unlink(pbuf); + stringstream ss; ss << "device " << dev << " is already locked by me! (pid "<<lpid<<")"; + throw LockFileException( ss.str() ); + } + + // Look for the process which owns the lock + if ( kill(pidOfLocker, 0) == 0 || errno == EPERM) + { + unlink(pbuf); + stringstream ss; ss << "device " << dev << " is already locked by process PID " << pidOfLocker; + throw LockFileException( ss.str() ); + } + else + { + // The process which owns the lock no longer exists. Clean up. + ret = unlink( lbuf ); + if ( ret != 0 ) + { + unlink(pbuf); + stringstream ss; ss << "Couldn't unlink " << lbuf << ": " << strerror(errno); + throw LockFileException( ss.str() ); + } + + // Now create our own + ret = link( pbuf, lbuf ); + if ( ret < 0 ) + { + unlink(pbuf); + stringstream ss; ss << "Couldn't link("<<pbuf<<","<<lbuf<<"): " << strerror(errno); + throw LockFileException( ss.str() ); + } + } + } + + ret = unlink(pbuf); + if ( ret < 0 ) + { + stringstream ss; + ss << "Couldn't unlink pbuf: " << strerror(errno); + throw LockFileException( ss.str() ); + } + } + + void + unlock_dev(const char *dev, int lpid) + { + FILE *fd; + char lbuf[260]; + int pidOfLocker = 0; + char *p; + + if ((p = strrchr(dev, '/'))) + dev = p + 1; + sprintf(lbuf, "%s/LCK..%s", LOCK_DIR, dev); + + fd = fopen(lbuf, "r"); + if (fd && fscanf(fd, "%d", &pidOfLocker) == 1 && pidOfLocker == lpid) + { + fclose(fd); + unlink(lbuf); + } + else if (fd) + fclose(fd); + } +} + +////////////////////////////////////////////////////////////////////// + +LockFile::LockFile( const std::string &dev, + int lockPid ) + : dev_(dev), + lockPid_(lockPid) +{ + lock_dev( dev_.c_str(), lockPid_ ); +} + +LockFile::~LockFile() +{ + unlock_dev( dev_.c_str(), lockPid_ ); +} + +} +} Added: gearbox/trunk/submitted/gbxserialacfr/lockfile/lockfile.h =================================================================== --- gearbox/trunk/submitted/gbxserialacfr/lockfile/lockfile.h (rev 0) +++ gearbox/trunk/submitted/gbxserialacfr/lockfile/lockfile.h 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,61 @@ +/* + * Orca-Robotics Project: Components for robotics + * http://orca-robotics.sf.net/ + * Copyright (c) 2004-2008 Alex Brooks + * + * This distribution is licensed to you under the terms described in + * the LICENSE file included in this distribution. + * + */ +#ifndef GBXSERIALACFR_LOCKFILE_H +#define GBXSERIALACFR_LOCKFILE_H + +#include <exception> +#include <string> + +namespace gbxserialacfr { +namespace lockfile { + +// +// @brief Exception thrown by lockfile functions +// +class LockFileException : public std::exception +{ + std::string message_; +public: + LockFileException(const char *message) + : message_(message) {} + LockFileException(const std::string &message) + : message_(message) {} + ~LockFileException()throw(){} + virtual const char* what() const throw() { return message_.c_str(); } +}; + +// +// Creates a lock-file which can be used to prevent multiple access to +// a unique resource (eg "/dev/xxx"). +// Stores the PID of the locking process (lockPid), so it can check for stale lock-files. +// +// Throws LockFileException's on errors (including 'device locked'). +// +// The destructor removes the lock-file (guarantees that no exceptions are thrown). +// +// For more info, see: http://tldp.org/HOWTO/Serial-HOWTO-14.html +// +class LockFile { +public: + + LockFile( const std::string &dev, + int lockPid = getpid() ); + ~LockFile(); + +private: + + const std::string dev_; + const int lockPid_; + +}; + +} +} +#endif Added: gearbox/trunk/submitted/gbxserialacfr/lockfile/test/CMakeLists.txt =================================================================== --- gearbox/trunk/submitted/gbxserialacfr/lockfile/test/CMakeLists.txt (rev 0) +++ gearbox/trunk/submitted/gbxserialacfr/lockfile/test/CMakeLists.txt 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,4 @@ +INCLUDE( ${GBX_CMAKE_DIR}/UseBasicRules.cmake ) + +ADD_EXECUTABLE( locktest locktest.cpp ) +TARGET_LINK_LIBRARIES( locktest GbxLockFileAcfr ) Added: gearbox/trunk/submitted/gbxserialacfr/lockfile/test/locktest.cpp =================================================================== --- gearbox/trunk/submitted/gbxserialacfr/lockfile/test/locktest.cpp (rev 0) +++ gearbox/trunk/submitted/gbxserialacfr/lockfile/test/locktest.cpp 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,37 @@ +#include <iostream> +#include <cstdlib> +#include <gbxserialacfr/lockfile/lockfile.h> + +using namespace std; + +int main( int argc, char **argv ) +{ + const char *dev = "/dev/ttyS0"; + + gbxserialacfr::lockfile::LockFile *lock; + + try { + lock = new gbxserialacfr::lockfile::LockFile( dev ); + } + catch ( const std::exception &e ) + { + cout<<"TRACE(locktest.cpp): Failed to lock: " << e.what() << endl; + exit(1); + } + + try { + gbxserialacfr::lockfile::LockFile secondLock( dev ); + + cerr << "Error: double-lock succeeded."; + exit(1); + } + catch ( const std::exception &e ) + { + cout<<"TRACE(locktest.cpp): Correctly caught exception: " << e.what() << endl; + } + + delete lock; + + cout<<"TRACE(locktest.cpp): test PASSED!" << endl; + return 0; +} Added: gearbox/trunk/submitted/gbxserialacfr/serial.cpp =================================================================== --- gearbox/trunk/submitted/gbxserialacfr/serial.cpp (rev 0) +++ gearbox/trunk/submitted/gbxserialacfr/serial.cpp 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,838 @@ +/* + * Orca-Robotics Project: Components for robotics + * http://orca-robotics.sf.net/ + * Copyright (c) 2004-2008 Mathew Ridley, Alex Brooks + * + * This distribution is licensed to you under the terms described in + * the LICENSE file included in this distribution. + * + */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> // read and write to ports +#include <string.h> +#include <iostream> +#include <iomanip> +#include <sstream> +#include <assert.h> +#include "serial.h" + +#if __linux +# include <linux/serial.h> +#endif + +#include <config.h> +#if HAVE_FILIO_H +// FIONREAD +# include <sys/filio.h> +#endif +// Ensure we have strnlen +// #include <hydroportability/strnlen.h> + +// copy the contents of this file below +// start of <hydroportability/strnlen.h> +// eg. Solaris doesn't define strnlen in string.h, so define it here. +#if !HAVE_STRNLEN + +#include <cstring> + +// inline the fucker to guard against multiple inclusion, without the +// hassle of a special lib. +inline size_t strnlen(const char *s, size_t maxlen) +{ + char *p; + if (s == NULL) { + return maxlen; + } + p = (char *)memchr(s, 0, maxlen); + if (p == NULL) { + return maxlen; + } + return ((p - s) + 1); +} +#endif +// end of <hydroportability/strnlen.h> + +// define for a debug compile +#define DEBUG + + +using namespace std; + +namespace gbxserialacfr { + + namespace { + + //Used for calls to waitForDataOrTimeout() + enum{TIMED_OUT=-1, GOT_DATA}; + + + // Converts an integer baud-rate into a c-style '#define'd baudrate + int cBaudrate( int baudRate ) + { + switch(baudRate) + { + case 0: + return B0; + case 50: + return B50; + case 75: + return B75; + case 110: + return B110; + case 134: + return B134; + case 150: + return B150; + case 200: + return B200; + case 300: + return B300; + case 600: + return B600; + case 1200: + return B1200; + case 1800: + return B1800; + case 2400: + return B2400; + case 4800: + return B4800; + case 9600: + return B9600; + case 19200: + return B19200; + case 38400: + return B38400; + case 57600: + return B57600; + case 115200: + return B115200; +#ifdef __linux + case 230400: + return B230400; + case 460800: + return B460800; + case 500000: + return B500000; + case 576000: + return B576000; + case 921600: + return B921600; + case 1000000: + return B1000000; + case 1152000: + return B1152000; + case 1500000: + return B1500000; + case 2000000: + return B2000000; + case 2500000: + return B2500000; + case 3000000: + return B3000000; + case 3500000: + return B3500000; + case 4000000: + return B4000000; +#endif + default: + stringstream ss; + ss << "Serial::baud() Invalid baud rate: " << baudRate; + throw SerialException( ss.str() ); + } + } + + int iBaudrate( int baudRate ) + { + switch(baudRate) + { + case B0: + return 0; + case B50: + return 50; + case B75: + return 75; + case B110: + return 110; + case B134: + return 134; + case B150: + return 150; + case B200: + return 200; + case B300: + return 300; + case B600: + return 600; + case B1200: + return 1200; + case B1800: + return 1800; + case B2400: + return 2400; + case B4800: + return 4800; + case B9600: + return 9600; + case B19200: + return 19200; + case B38400: + return 38400; + case B57600: + return 57600; + case B115200: + return 115200; + case B230400: + return 230400; + case B460800: + return 460800; + case B500000: + return 500000; + case B576000: + return 576000; + case B921600: + return 921600; + case B1000000: + return 1000000; + case B1152000: + return 1152000; + case B1500000: + return 1500000; + case B2000000: + return 2000000; + case B2500000: + return 2500000; + case B3000000: + return 3000000; + case B3500000: + return 3500000; + case B4000000: + return 4000000; + default: + stringstream ss; + ss << "Serial::baud() Invalid baud rate: " << baudRate; + throw SerialException( ss.str() ); + } + } + + //Allow streaming of the term status structure type + std::ostream &operator<<( std::ostream &s, struct termios &stat ) + { + s.setf(ios::hex,ios::basefield); + s.fill('0'); + + s << "c_iflag -> 0x" << setw(4) << stat.c_iflag << endl; + s << "c_oflag -> 0x" << setw(4) << stat.c_oflag << endl; + s << "c_cflag -> 0x" << setw(4) << stat.c_cflag << endl; + s << "c_lflag -> 0x" << setw(4) << stat.c_lflag << endl; + + s << "c_cc_array ->" << endl; + s << "VINTR 0x" << setw(2) << static_cast<int>(stat.c_cc[VINTR]) + << ", VQUIT 0x" << setw(2) << static_cast<int>(stat.c_cc[VQUIT]) + << ", VERASE 0x" << setw(2) << static_cast<int>(stat.c_cc[VERASE]) + << ", VKILL 0x" << setw(2) << static_cast<int>(stat.c_cc[VKILL]) << endl ; + s << "VEOF 0x" << setw(2) << static_cast<int>(stat.c_cc[VEOF]) + << ", VEOL 0x" << setw(2) << static_cast<int>(stat.c_cc[VEOL]) + << ", VEOL2 0x" << setw(2) << static_cast<int>(stat.c_cc[VEOL2]) + << ", VSTART 0x" << setw(2) << static_cast<int>(stat.c_cc[VSTART]) << endl ; + s << "VSTOP 0x" << setw(2) << static_cast<int>(stat.c_cc[VSTOP]) + << ", VSUSP 0x" << setw(2) << static_cast<int>(stat.c_cc[VSUSP]) + << ", VREPRINT 0x" << setw(2) << static_cast<int>(stat.c_cc[VREPRINT]) + << ", VLNEXT 0x" << setw(2) << static_cast<int>(stat.c_cc[VLNEXT]) << endl; + s << "VMIN 0x" << setw(2) << static_cast<int>(stat.c_cc[VMIN]) + << ", VTIME 0x" << setw(2) << static_cast<int>(stat.c_cc[VTIME]) << endl; + + + s << "c_iflag, Bits set" << endl; + if (stat.c_iflag & IGNBRK) { s << "IGNBRK,";} + if (stat.c_iflag & BRKINT) { s << "BRKINT,";} + if (stat.c_iflag & IGNPAR) { s << "IGNPAR,";} + if (stat.c_iflag & PARMRK) { s << "PARMRK,";} + if (stat.c_iflag & INPCK) { s << "INPCK,";} + if (stat.c_iflag & ISTRIP) { s << "ISTRIP,";} + if (stat.c_iflag & INLCR) { s << "INLCR,";} + if (stat.c_iflag & IGNCR) { s << "IGNCR,";} + if (stat.c_iflag & ICRNL) { s << "ICRNL,";} + if (stat.c_iflag & IUCLC) { s << "IUCLC,";} + if (stat.c_iflag & IXON) { s << "IXON,";} + if (stat.c_iflag & IXANY) { s << "IXANY,";} + if (stat.c_iflag & IXOFF) { s << "IXOFF,";} + if (stat.c_iflag & IMAXBEL){ s << "IMAXBEL,";} + s << endl; + + s << "c_oflag, Bits set" << endl; + if (stat.c_oflag & OPOST){ s << "OPOST,";} + if (stat.c_oflag & OLCUC){ s << "OLCUC,";} + if (stat.c_oflag & ONLCR){ s << "ONLCR,";} + if (stat.c_oflag & OCRNL){ s << "OCRNL,";} + if (stat.c_oflag & ONOCR){ s << "ONOCR,";} + if (stat.c_oflag & ONLRET){ s << "ONLRET,";} + if (stat.c_oflag & OFILL){ s << "OFILL,";} + if (stat.c_oflag & OFDEL){ s << "OFDEL,";} + if (stat.c_oflag & NLDLY){ s << "NLDLY,";} + if (stat.c_oflag & CRDLY){ s << "CRDLY,";} + if (stat.c_oflag & TABDLY){ s << "TABDLY,";} + if (stat.c_oflag & BSDLY){ s << "BSDLY,";} + if (stat.c_oflag & VTDLY){ s << "VTDLY,";} + if (stat.c_oflag & FFDLY){ s << "FFDLY,";} + s << endl; + + s << "c_cflag, Bits set" << endl; + if (stat.c_cflag & CSTOPB){ s << "CSTOPB,";} + if (stat.c_cflag & CREAD){ s << "CREAD,";} + if (stat.c_cflag & PARENB){ s << "PARENB,";} + if (stat.c_cflag & PARODD){ s << "PARODD,";} + if (stat.c_cflag & HUPCL){ s << "HUPCL,";} + if (stat.c_cflag & CLOCAL){ s << "CLOCAL,";} + if (stat.c_cflag & CIBAUD){ s << "CIBAUD,";} + s << endl; + + + s << "c_lflag, Bits set" << endl; + if (stat.c_lflag & ISIG){ s << "ISIG,";} + if (stat.c_lflag & ICANON){ s << "ICANON,";} + if (stat.c_lflag & PARENB){ s << "PARENB,";} + if (stat.c_lflag & XCASE){ s << "XCASE,";} + if (stat.c_lflag & NOFLSH){ s << "NOFLSH,";} + if (stat.c_lflag & FLUSHO){ s << "FLUSHO,";} + if (stat.c_lflag & PENDIN){ s << "PENDIN,";} + if (stat.c_lflag & IEXTEN){ s << "IEXTEN,";} + if (stat.c_lflag & TOSTOP){ s << "TOSTOP,";} + if (stat.c_lflag & ECHO){ s << "ECHO,";} + if (stat.c_lflag & ECHOE){ s << "ECHOE,";} + if (stat.c_lflag & ECHOPRT){ s << "ECHOPRT,";} + if (stat.c_lflag & ECHOKE){ s << "ECHOKE,";} + if (stat.c_lflag & NOFLSH){ s << "ECHONL,";} + if (stat.c_lflag & FLUSHO){ s << "ECHOCTL,";} + s << endl; + + s << endl; + s.setf(ios::dec,ios::basefield); + return s; + } + + + std::ostream &operator<<( std::ostream &s, struct serial_struct &serinfo ) + { + s.setf(ios::hex,ios::basefield); + + s << "TIOCGSERIAL:-> " << endl; + s << "Flags: 0x" << setw(2) << serinfo.flags + << ", Type: 0x" << setw(2) << serinfo.type + << ", Line: 0x" << setw(2) << serinfo.line << endl; + s << "Port: 0x" << setw(2) << serinfo.port + << ", IRQ: 0x" << setw(2) << serinfo.irq + << ", xmit_fifo_size: 0x" << setw(2) << serinfo.xmit_fifo_size << endl; + s << "Baud_base: 0x" << setw(2) << serinfo.baud_base + << ", Custom_divisor: 0x" << setw(2) << serinfo.custom_divisor + << ", Io_type: 0x" << setw(2) << serinfo.io_type << endl; + + s.setf(ios::dec,ios::basefield); + return s; + } + + } + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// + +Serial::Serial( const std::string &dev, + int baudRate, + bool enableTimeouts, + int debuglevel, + bool useLockFile ) + : dev_(dev), + portFd_(-1), + timeoutSec_(0), + timeoutUSec_(0), + timeoutsEnabled_(enableTimeouts), + debugLevel_(debuglevel) +{ + if ( useLockFile ) + { + try { + lockFile_ = new lockfile::LockFile( dev ); + } + catch ( const lockfile::LockFileException &e ) + { + stringstream ss; + ss << "Couldn't get lock for device " << dev << ": " << e.what(); + throw SerialException( ss.str() ); + } + } + + try { + open(); + setBaudRate( baudRate ); + + if(debugLevel_ > 2){ + cout << "At end of Serial::Serial: " << getStatusString(); + } + } + catch ( const SerialException &e ) + { + if ( lockFile_ ) delete lockFile_; + throw; + } +} + +Serial::~Serial() +{ + close(); + if ( lockFile_ ) delete lockFile_; +} + +void +Serial::setTimeout(int sec, int usec) +{ + if ( !timeoutsEnabled_ ) + { + stringstream s; + s << "setTimeout() called for port" << dev_ <<" but timeouts not enabled!"; + throw SerialException( s.str() ); + } + + timeoutSec_=sec; timeoutUSec_=usec; +} + +void +Serial::close() +{ + if ( debugLevel_ > 0 ) + cout<<"TRACE(serial.cpp): close()" << endl; + + assert( portFd_ != -1 ); + + if(tcdrain(portFd_)) + { + perror("Serial::close():tcdrain()"); + } + if (::close(portFd_)) + { + perror("Serial::close():close()"); + } + + //Make sure that we force this back to an invalid state + portFd_ = -1; +} + +void +Serial::setBaudRate(int baud) +{ + struct termios localOptions; + + if ( debugLevel_ > 0 ) + cout<<"TRACE(serial.cpp): setBaudRate("<<baud<<")" << endl; + + if(portFd_==-1) + { + throw SerialException( "Serial:baud() no valid device open" ); + } + if(tcgetattr(portFd_, &localOptions) == -1) + { + stringstream ss; + ss << "Serial::baud():tcgetattr() Error reading attr: " << strerror(errno); + throw SerialException( ss.str() ); + } + + cfsetispeed(&localOptions, cBaudrate(baud)); + cfsetospeed(&localOptions, cBaudrate(baud)); + + // + // AlexB: This code doesn't seem to work for USB devices... + // See: http://ozlabs.org/pipermail/linuxppc-embedded/2005-February/016848.html + // + if ( strstr( dev_.c_str(), "USB" ) == 0 ) + { + // + // AlexB: For reasons I don't fully understand, this chunk is required to make the + // laser work at standard baud rates. + // + struct serial_struct serinfo; + serinfo.reserved_char[0] = 0; + if (ioctl(portFd_, TIOCGSERIAL, &serinfo) < 0) + { + stringstream ss; + ss << "Serial::setBaudRate("<<baud<<"): error calling 'ioctl(portFd_, TIOCGSERIAL, &serinfo)': "<<strerror(errno); + throw SerialException( ss.str() ); + } + + serinfo.flags &= ~ASYNC_SPD_CUST; + serinfo.custom_divisor = 0; + + if (ioctl(portFd_, TIOCSSERIAL, &serinfo) < 0) + { + stringstream ss; + ss << "Serial::setBaudRate("<<baud<<"): error calling 'ioctl(portFd_, TIOCSSERIAL, &serinfo)': "<<strerror(errno); + throw SerialException( ss.str() ); + } + } + + if ( tcsetattr(portFd_, TCSAFLUSH, &localOptions) == -1 ) + { + stringstream ss; + ss << "Serial::baud():tcsetattr() Error setting attr: " << strerror(errno); + throw SerialException( ss.str() ); + } +} + +void +Serial::open(int flags) +{ + struct termios localOptions; + + if ( timeoutsEnabled_ ) + flags |= O_NONBLOCK; + + portFd_ = ::open(dev_.c_str(), flags|O_RDWR|O_NOCTTY); + if ( portFd_ == -1 ) + { + stringstream ss; + ss << "Serial::open(): failed to open '"<<dev_<<"': "<<strerror(errno); + throw SerialException( ss.str() ); + } + + + if(tcgetattr(portFd_, &localOptions) == -1) + { + close(); + stringstream ss; + ss << "Serial::open(): tcgetattr() failed for '"<<dev_<<"': "<<strerror(errno); + throw SerialException( ss.str() ); + } + + if(debugLevel_ > 2){ + cout << "At beginning of open(): "<<getStatusString()<<endl; + } + + + + // enable receiver & ignore control lines + localOptions.c_cflag |= (CLOCAL | CREAD) ; + + // set 8 data bits + localOptions.c_cflag &= ~CSIZE; + localOptions.c_cflag |= CS8; + + // set parity to none with no stop bit + localOptions.c_cflag &= ~PARENB; + localOptions.c_cflag &= ~CSTOPB; + + // disable hardware flow control + localOptions.c_cflag &= ~CRTSCTS; + +#if defined(__sun) + // http://www.sunmanagers.org/pipermail/summaries/2005-October/006871.html + localOptions.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + localOptions.c_oflag &= ~OPOST; + localOptions.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + localOptions.c_cflag &= ~(CSIZE|PARENB); + localOptions.c_cflag |= CS8; +#else + cfmakeraw(&localOptions); +#endif + + if(tcsetattr(portFd_, TCSAFLUSH, &localOptions) == -1) + { + close(); + stringstream ss; + ss << "Serial::open(): tcsetattr() failed for '"<<dev_<<"': "<<strerror(errno); + throw SerialException( ss.str() ); + } + + if ( debugLevel_ > 2 ){ + cout << "At end of open():" << getStatusString(); + } + +} + +int +Serial::read(void *buf, int count) +{ + if ( debugLevel_ > 0 ) + cout<<"TRACE(serial.cpp): read()" << endl; + + int got = ::read(portFd_, buf, count); + if ( got < 0 ) + { + throw SerialException( string("Serial::read(): ") + strerror(errno) ); + } + + if ( debugLevel_ > 1 ) + { + cout<<"TRACE(serial.cpp): read: '"; + for ( int i=0; i < count; i++ ) + cout << ((char*)(buf))[i]; + cout << "'" << endl; + } + + return got; +} + + + +int +Serial::readFull(void *buf, int count) +{ + if ( debugLevel_ > 0 ) + cout<<"TRACE(serial.cpp): readFull(): count=" << count << endl; + + char* bufPtr = static_cast<char*>(buf); + + int got=0; + while ( got < count ) + { + char *offset = bufPtr + got; + int ret = ::read(portFd_, offset, count-got); + if ( ret >= 0 ) + { + got += ret; + } + + else if (timeoutsEnabled_ && (errno == EAGAIN) ) + { + if ( waitForDataOrTimeout() == TIMED_OUT ) + { + // select timed out: no data + return -1; + } + } + + else + { + throw SerialException( std::string("Serial::readFullWithTimeout: read(): ")+strerror(errno) ); + } + + } + + return got; //The number of bytes that we read. +} + + + + +int +Serial::readLine(void *buf, int count, char termchar) +{ + if ( debugLevel_ > 0 ){ + cout<<"TRACE(serial.cpp): readLine "; + if(timeoutsEnabled_){ + cout << "timeouts enabled"<<endl; + }else{ + cout << "timeouts not enabled"<<endl; + } + } + + //There must be at least room for a terminating char and NULL terminator! + assert (count >= 2); + + char* dataPtr = static_cast<char*>(buf); + const char* bufPtr = static_cast<char*>(buf); + char nextChar = 0; + + do{ + //Check for buf overrun Must leave room for NULL terminator + if ( dataPtr >= bufPtr + (count - 1) ) + { + throw SerialException( "Serial::readLine: Not enough room in buffer" ); + } + + int ret = ::read( portFd_, &nextChar, 1 ); + if (ret == 1) + { + *(dataPtr++) = nextChar; //got data let's store it... + } + else + { + //If timeouts enabled and no data, wait and then go again + if( timeoutsEnabled_ && (ret == -1) && (errno == EAGAIN) ) + { + if(waitForDataOrTimeout() == GOT_DATA) + { + continue; + }else{ + *dataPtr = 0x00; //Timed out. terminate string just incase it's used anyway + return -1; + } + } + + //If we get here then it was a more serious error + throw SerialException( std::string( "Serial::readLine(): ")+strerror(errno) ); + } + + } while (nextChar != termchar); + + // It's a string. It must be NULL terminated... + *dataPtr = 0x00; + + // Return the number of chars not including the NULL + return ( (int) (dataPtr - bufPtr) ); + + //TODO: Duncan! I think that this should cope with any <CR><LF> pair gracefully! +} + + +int +Serial::bytesAvailable() +{ + int n_read; + int ret = ioctl(portFd_,FIONREAD,&n_read); + + if(ret==-1) + { + throw SerialException( std::string("Serial::bytesAvailable(): ")+strerror(errno) ); + } + return n_read; +} + +int +Serial::bytesAvailableWait() +{ + if ( waitForDataOrTimeout() == TIMED_OUT){ + return -1; + } + + return bytesAvailable(); +} + + +int +Serial::waitForDataOrTimeout() +{ + fd_set rfds; + struct timeval tv; + FD_ZERO(&rfds); + FD_SET(portFd_, &rfds); + tv.tv_sec = timeoutSec_; + tv.tv_usec = timeoutUSec_; + int selval = select(portFd_+1, &rfds, NULL, NULL, &tv); + if(selval==0) + { + // select timed out: no data + return TIMED_OUT; + } + if(selval<0) + { + throw SerialException( std::string("Serial::waitForTimeout: select(): ")+strerror(errno) ); + } + + return GOT_DATA; +} + + +int +Serial::writeString(const char *str) +{ + if ( debugLevel_ > 0 ) + cout<<"TRACE(serial.cpp): writeString(): writing '"<<str<<"'" << endl; + + int put; + put = ::write(portFd_, str, strlen(str) ); + if ( put < 0 ) + { + throw SerialException( string("Serial::write(): ")+strerror(errno) ); + } + else if ( put == 0 ) + { + throw SerialException( "Serial::writeString(): ::write() returned 0" ); + } + else if ( put < (int)(strlen(str)) ) + { + // AlexB: Not sure what to do here... This can happen eg if the buffer is full. + // I'm not convinced that we want to throw an exception, but chances are + // lots of users won't check the return code. + cout << "WARNING: Serial::writeString: only wrote " << put << " of " << strlen(str) << " bytes." << endl; + } + + if ( debugLevel_ > 1 ) + { + cout<<"TRACE(serial.cpp): wrote " << put << " bytes" << endl; + } + + return put; +} + + +std::string +Serial::getStatusString() +{ + struct termios status; + if(tcgetattr(portFd_, &status) == -1) + { + close(); + throw SerialException( std::string("Serial::getStatusString(): tcgetattr():")+strerror(errno) ); + } + + stringstream ss; + ss << endl << " Device " << dev_ << " Status:-" << endl; + ss << "In baud_rate: " << iBaudrate(cfgetispeed(&status)) + << " Out baud_rate: " << iBaudrate(cfgetospeed(&status)) << endl; + ss << status; + + struct serial_struct serinfo; + if (ioctl(portFd_, TIOCGSERIAL, &serinfo) < 0) + { + stringstream ss; + ss << "Serial::getStatusString(): error calling 'ioctl(portFd_, TIOCGSERIAL, &serinfo)': "<<strerror(errno); + throw SerialException( ss.str() ); + } + ss << serinfo; + + return ss.str(); +} + + +void +Serial::flush() +{ + int ret = tcflush(portFd_,TCIOFLUSH); + if ( ret < 0 ) + { + throw SerialException( std::string("Serial::flush(): ")+strerror(errno) ); + } +} + +void +Serial::drain() +{ + // wait till all output sent + if(tcdrain(portFd_)) + { + throw SerialException( std::string("Serial::drain(): tcdrain: ")+strerror(errno) ); + } +} + +int +Serial::write(const void *buf, int count) +{ + if ( debugLevel_ > 0 ) + cout<<"TRACE(serial.cpp): write()" << endl; + + if ( count == 0 ) + throw SerialException( "Serial::write() was called with zero bytes" ); + + int put = ::write(portFd_, buf, count); + if ( put < 0 ) + { + throw SerialException( string("Serial::write(): ")+strerror(errno) ); + } + else if ( put == 0 ) + { + throw SerialException( "Serial::write(): ::write() returned 0" ); + } + if ( debugLevel_ > 1 ) + { + cout<<"TRACE(serial.cpp): wrote " << put << " bytes: '"; + for ( int i=0; i < count; i++ ) cout << ((char*)(buf))[i]; + cout << "'" << endl; + } + + return put; +} + +} // namespace Added: gearbox/trunk/submitted/gbxserialacfr/serial.h =================================================================== --- gearbox/trunk/submitted/gbxserialacfr/serial.h (rev 0) +++ gearbox/trunk/submitted/gbxserialacfr/serial.h 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,172 @@ +/* + * Orca-Robotics Project: Components for robotics + * http://orca-robotics.sf.net/ + * Copyright (c) 2004-2008 Mathew Ridley, Alex Brooks + * + * This distribution is licensed to you under the terms described in + * the LICENSE file included in this distribution. + * + */ +#ifndef GBXSERIALACFR_SERIAL_H +#define GBXSERIALACFR_SERIAL_H + +#include <termios.h> +#include <sys/types.h> +#include <fcntl.h> +#include <string> +#include <gbxserialacfr/uncopyable.h> +#include <gbxserialacfr/lockfile/lockfile.h> + +namespace gbxserialacfr { + +//! +//! @brief Exception thrown by Serial. +//! +class SerialException : public std::exception +{ + std::string message_; +public: + SerialException(const char *message) + : message_(message) {} + SerialException(const std::string &message) + : message_(message) {} + ~SerialException()throw(){} + virtual const char* what() const throw() { return message_.c_str(); } +}; + +//! +//! @brief Encapsulates a serial port. +//! +//! This class hard-codes some options, such as: +//! - 8 data bits +//! - no handshaking +//! +//! Warning: this thing is _NOT_ thread-safe. +//! +//! @author Matthew Ridley, Alex Brooks +//! +class Serial : public Uncopyable +{ +public: + + //! Opens a device @ref dev. + //! Throws exceptions on error. + //! If useLockFile is set to true, Serial will use the file-system to + //! prevent concurrent access to a serial device by multiple instances of Serial. + Serial( const std::string &dev, + int baudRate, + bool enableTimeouts, + int debuglevel = 0, + bool useLockFile = true ); + + //! Destructor closes serial port + ~Serial(); + + //! turn on/off debug messages + void setDebugLevel( int debugLevel ) { debugLevel_ = debugLevel; } + + //! Sets the baud rate. Flushes any data. + void setBaudRate(int baud); + + //! Sets timeout (timeouts must be enabled) + void setTimeout(int sec, int usec); + + //! Reads up to @ref count bytes into buffer @ref buf. + //! Returns the number of bytes read. + //! Will never return <0 -- throws exceptions instead. + //! If timeouts are not enabled, blocks till it gets something. + //! If timeouts are enabled, throws an exception if data isn't available. + int read(void *buf, int count); + + //! Tries to read exactly @ref count bytes into @ref buf. + //! Returns the number of bytes read, or throws an exception. + //! + //! If timeouts are not enabled we might block forever, waiting for the number of bytes we want or an error. + //! + //! If timeouts are enabled we won't block more than the timeout specified. + //! Returns -1 if it timed out. + //! NOTE: The timeout applies for each individual read() call. We might have to make lots of them, + //! so the total time for which this function blocks might be longer than the specified timeout. + //! + int readFull(void *buf, int count); + + //! Reads a line of data up to @ref count bytes-1 (including @ref termchar), terminated by @ref termchar. + //! Returns the number of bytes read. + //! After reading the line then the string will be NULL terminated. + //! + //! Example: if you expect to read the string "1234\n", you need something like: + //! char buf[6]; + //! serial.readLine( buf, 6 ); + //! + //! where the two extra characters are for the '\n' and the terminating '\0'. + //! + //! If timeouts are not enabled we might block forever, waiting for the number of bytes we want or an error. + //! + //! If timeouts are enabled we won't block more than the timeout specified. + //! Returns -1 if it timed out. + //! NOTE: The timeout applies for each individual read() call. We might have to make lots of them, + //! so the total time for which this function blocks might be longer than the specified timeout. + //! + int readLine(void *buf, int count, char termchar='\n'); + + //! Returns the number of bytes available for reading (non-blocking). + int bytesAvailable(); + + //! Returns the number of bytes available for reading. Waits according to the timeout. + //! Returns: + //! - <0 : timed out + //! - >0 : data ready + int bytesAvailableWait(); + + //! Writes some data. Returns the number of bytes written. + int write(const void *buf, int count); + + //! Writes a ('\0'-terminated) string. + //! Returns the number of bytes written. + int writeString(const char *buf); + inline int writeString(const std::string &s) { + return writeString(s.c_str()); + } + + //! Flushs both input and output buffers. + //! This discards all data in buffers. + void flush(); + + //! Finishes transmission from output buffers and drains input buffers. + void drain(); + + //! This gives direct access to the file descriptor: be careful with this... + int fileDescriptor() { return portFd_; } + + //! Print some diagnostic information about the current status of the port to cout. + std::string getStatusString(); + + +private: + + // Utility function to wait up to the timeout for data to appear. + // Returns: + // TIMED_OUT: timed out + // GOT_DATA : data available + int waitForDataOrTimeout(void); + + // Opens a device @ref dev. + void open(int flags=0); + + // Won't throw exceptions. + void close(); + + const std::string dev_; + int portFd_; + int timeoutSec_; + int timeoutUSec_; + bool timeoutsEnabled_; + + int debugLevel_; + + lockfile::LockFile *lockFile_; +}; + +} + +#endif Added: gearbox/trunk/submitted/gbxserialacfr/test/CMakeLists.txt =================================================================== --- gearbox/trunk/submitted/gbxserialacfr/test/CMakeLists.txt (rev 0) +++ gearbox/trunk/submitted/gbxserialacfr/test/CMakeLists.txt 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,7 @@ +INCLUDE( ${GBX_CMAKE_DIR}/UseBasicRules.cmake ) + +LINK_LIBRARIES( GbxSerialAcfr ) + +GBX_ADD_EXECUTABLE( serialechotest serialechotest.cpp ) + +GBX_ADD_EXECUTABLE( serialloopbacktest serialloopbacktest.cpp ) Added: gearbox/trunk/submitted/gbxserialacfr/test/serialechotest.cpp =================================================================== --- gearbox/trunk/submitted/gbxserialacfr/test/serialechotest.cpp (rev 0) +++ gearbox/trunk/submitted/gbxserialacfr/test/serialechotest.cpp 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,66 @@ +#include <iostream> +#include <gbxserialacfr/serial.h> +#include <cstdlib> +#include <string> +#include <sstream> +#include <vector> +#include <iomanip> + +using namespace std; + +// +// A simple utility program for testing serial ports. +// + +std::string +toHexString( const std::vector<unsigned char> &data ) +{ + stringstream ss; + ss << "[ "; + for ( size_t i=0; i < data.size(); i++ ) + { + ss <<hex<<std::setfill('0')<<std::setw(2)<<(int)(data[i])<<" "; + } + ss << "]"; + return ss.str(); +} + +int main( int argc, char **argv ) +{ + if ( argc < 3 ) + { + cout << "USAGE: " << argv[0] << " <dev> <baudrate>" << endl; + exit(1); + } + + const bool enableTimeouts = true; + gbxserialacfr::Serial serial( argv[1], atoi(argv[2]), enableTimeouts ); + serial.setTimeout( 1, 0 ); + + std::vector<unsigned char> data; + while ( true ) + { + cout<<"TRACE(serialechotest.cpp): Waiting for data..." << endl; + size_t nBytes = serial.bytesAvailableWait(); + + if ( nBytes > 0 ) + { + cout<<"TRACE(serialechotest.cpp): nBytes: " << nBytes << endl; + if ( nBytes > data.size() ) + { + cout<<"TRACE(serialechotest.cpp): resizing to " << nBytes << endl; + data.resize( nBytes ); + } + +// cout<<"TRACE(serialechotest.cpp): data.size(): " << data.size() << endl; +// cout<<"TRACE(serialechotest.cpp): &(data[0]): " << (int)(&(data[0])) << endl; +// cout<<"TRACE(serialechotest.cpp): data[0]: " << (int)(data[0]) << endl; + + serial.read( &(data[0]), nBytes ); + + cout << "got data: " << toHexString( data ) << endl; + } + } + + return 0; +} Added: gearbox/trunk/submitted/gbxserialacfr/test/serialloopbacktest =================================================================== (Binary files differ) Property changes on: gearbox/trunk/submitted/gbxserialacfr/test/serialloopbacktest ___________________________________________________________________ Name: svn:executable + * Name: svn:mime-type + application/octet-stream Added: gearbox/trunk/submitted/gbxserialacfr/test/serialloopbacktest.cpp =================================================================== --- gearbox/trunk/submitted/gbxserialacfr/test/serialloopbacktest.cpp (rev 0) +++ gearbox/trunk/submitted/gbxserialacfr/test/serialloopbacktest.cpp 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,87 @@ +#include <iostream> +#include <gbxserialacfr/serial.h> +#include <cstdlib> +#include <cstring> +#include <string> +#include <sstream> +#include <vector> +#include <iomanip> + +using namespace std; + +// +// A simple utility program for testing serial ports in loopback. +// + +std::vector<std::string> +getStringList() +{ + std::vector<std::string> stringList; + + stringList.push_back( "string_one" ); + stringList.push_back( "string_two" ); + stringList.push_back( "string_three" ); + stringList.push_back( "string_four" ); + stringList.push_back( "" ); + stringList.push_back( "testing" ); + stringList.push_back( "asdfasdfasdfasdf" ); + + return stringList; +} + +int main( int argc, char **argv ) +{ + if ( argc < 3 ) + { + cout << "USAGE: " << argv[0] << " <dev> <baudrate>" << endl; + exit(1); + } + + const bool enableTimeouts = true; + gbxserialacfr::Serial serial( argv[1], atoi(argv[2]), enableTimeouts ); + serial.setTimeout( 1, 0 ); + + const uint NUM_CHARS = 1000; + std::vector<std::string> stringList = getStringList(); + for ( uint i=0; i < NUM_CHARS; i++ ) + { + int stringI = i % (stringList.size()); + std::string theString = stringList[stringI]+"\n"; + + serial.writeString( theString ); + + char buf[ theString.size()+1 ]; + int ret = serial.readLine( buf, theString.size()+1 ); + if ( ret < 0 ) + { + cout << "ERROR(serialloopbacktest.cpp): Read timed out!" << endl; + int nBytes = serial.bytesAvailable(); + cout<<"TRACE(serialloopbacktest.cpp): bytesAvailable: " << nBytes << endl; + + if ( nBytes == 0 ) + { + cout<<"TRACE(serialloopbacktest.cpp): Ensure that the serial port is looped-back" << endl; + cout<<"TRACE(serialloopbacktest.cpp): (ie pins 2 & 3 connected)" << endl; + } + exit(1); + } + + if ( !strcmp( buf, theString.c_str() ) ) + { + cout<<"Wrote and read: " << theString << endl; + } + else + { + cout << "ERROR(serialloopbacktest.cpp): Strings didn't match!!" << endl; + cout << "ERROR(serialloopbacktest.cpp): Wrote: '" << theString <<"'"<< endl; + cout << "ERROR(serialloopbacktest.cpp): Read: '" << buf <<"'"<< endl; + + cout<<"TRACE(serialloopbacktest.cpp): test FAILED" << endl; + exit(1); + } + } + + cout<<"TRACE(serialloopbacktest.cpp): test PASSED" << endl; + + return 0; +} Added: gearbox/trunk/submitted/gbxserialacfr/uncopyable.h =================================================================== --- gearbox/trunk/submitted/gbxserialacfr/uncopyable.h (rev 0) +++ gearbox/trunk/submitted/gbxserialacfr/uncopyable.h 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,34 @@ +/* + * Orca-Robotics Project: Components for robotics + * http://orca-robotics.sf.net/ + * Copyright (c) 2004-2008 Alex Brooks, Alexei Makarenko, Tobias Kaupp + * + * This distribution is licensed to you under the terms described in + * the LICENSE file included in this distribution. + * + */ + +#ifndef GBXSERIALACFR_UNCOPYABLE_H +#define GBXSERIALACFR_UNCOPYABLE_H + +namespace gbxserialacfr { + +// +// @brief Handy way to avoid unintended copies. + +// Inherit from this and the compiler will barf if you try to copy the derived class. +// +// @author Alex Brooks +// +class Uncopyable +{ +public: + Uncopyable() {} +private: + Uncopyable(const Uncopyable&); + void operator=(const Uncopyable&); +}; + +} + +#endif Added: gearbox/trunk/submitted/gbxsickacfr/CMakeLists.txt =================================================================== --- gearbox/trunk/submitted/gbxsickacfr/CMakeLists.txt (rev 0) +++ gearbox/trunk/submitted/gbxsickacfr/CMakeLists.txt 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,41 @@ +SET ( lib_name GbxSickAcfr ) +GBX_ADD_LICENSE( LGPL ) + +SET ( build TRUE ) +GBX_REQUIRE_OPTION( build LIB ${lib_name} ON ) +GBX_REQUIRE_VAR( build LIB ${lib_name} GBX_OS_LINUX "only Linux OS is supported" ) + +INCLUDE( ${GBX_CMAKE_DIR}/FindIceUtil.cmake ) +GBX_REQUIRE_VAR( build LIB ${lib_name} ICEUTIL_FOUND "libIceUtil not found" ) + +SET( proj_libs GbxSerialAcfr ) +GBX_REQUIRE_TARGETS( build LIB ${lib_name} ${proj_libs} ) + +# these are built internally +SET( int_libs GbxUtilAcfr GbxIceUtilAcfr GbxSerialDeviceAcfr ) + +IF ( build ) + # allow subdir's to include themselves and each other +# INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR} ) + + ADD_SUBDIRECTORY( gbxutilacfr ) + ADD_SUBDIRECTORY( gbxiceutilacfr ) + ADD_SUBDIRECTORY( gbxserialdeviceacfr ) + + INCLUDE( ${GBX_CMAKE_DIR}/UseBasicRules.cmake ) +# INCLUDE( ${GBX_CMAKE_DIR}/UseIceUtil.cmake ) + + FILE( GLOB hdrs *.h ) + FILE( GLOB srcs *.cpp ) + SET( dep_libs ${proj_libs} ${int_libs} ) + + GBX_ADD_LIBRARY( ${lib_name} SHARED ${srcs} ) + TARGET_LINK_LIBRARIES( ${lib_name} ${dep_libs} ) + + GBX_ADD_HEADERS( gbxsickacfr ${hdrs} ) + + IF ( GBX_BUILD_TESTS ) + ADD_SUBDIRECTORY ( test ) + ENDIF ( GBX_BUILD_TESTS ) + +ENDIF ( build ) Added: gearbox/trunk/submitted/gbxsickacfr/doc.dox =================================================================== --- gearbox/trunk/submitted/gbxsickacfr/doc.dox (rev 0) +++ gearbox/trunk/submitted/gbxsickacfr/doc.dox 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,39 @@ +/* + * Orca-Robotics Project: Components for robotics + * http://orca-robotics.sf.net/ + * Copyright (c) 2004-2008 Alex Brooks, Alexei Makarenko, Tobias Kaupp + * + * This distribution is licensed to you under the terms described in + * the LICENSE file included in this distribution. + * + */ + +/*! +@ingroup hydro_drivers_laserscanner2d +@ingroup hydro_cpp +@ingroup hydro_linux +@defgroup hydro_driver_laserscanner2dsickacfr LaserScanner2dSickAcfr +@brief ACFR driver for SICK laser range-finder. + +Drives SICK hardware, directly connected to the computer. + +@par Dependencies + +- libIceUtil (for timing) +- libHydroSerial and libHydroSerialDevice + +@par Limitations + +- This is a Linux-only implementation. +- It has not been tested at 500k baud. + +@par Extra configuration + +- @c SickAcfr.Device (string) + - The unix device to use + - Default: "/dev/ttyS0" +- @c SickAcfr.Baudrate (int) [bps] + - Valid Values: { 9600, 19200, 38400, 500000 } + - Default: 38400 + +*/ Added: gearbox/trunk/submitted/gbxsickacfr/driver.cpp =================================================================== --- gearbox/trunk/submitted/gbxsickacfr/driver.cpp (rev 0) +++ gearbox/trunk/submitted/gbxsickacfr/driver.cpp 2008-02-16 06:25:16 UTC (rev 63) @@ -0,0 +1,583 @@ +/* + * Orca-Robotics Project: Components for robotics + * http://orca-robotics.sf.net/ + * Copyright (c) 2004-2008 Alex Brooks + * + * This distribution is licensed to you under the terms described in + * the LICENSE file included in this distribution. + * + */ + +#include <iostream> +#include <cstring> + +#include <gbxsickacfr/gbxutilacfr/gbxutilacfr.h> +#include <gbxsickacfr/gbxiceutilacfr/gbxiceutilacfr.h> +#include "driver.h" + +using namespace std; + +namespace gbxsickacfr { + +namespace { + + class NoResponseException : public std::exception + { + std::string message_; + public: + NoResponseException(const char *message) + : message_(message) {} + NoResponseException(const std::string &message) + : message_(message) ... [truncated message content] |