/** \file
 *
 *  Contains the UartStreamBase class implementation.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 *
 */

#include "UartStreamBase.h"

#include <cctype>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <csignal>
#include <cstdio>
#include <cstring>
#include <ctype.h>
#include <fcntl.h>
#include <sched.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <termios.h>
#include <unistd.h>
//#include <sys/ioctl.h>
//#include <linux/serial.h>

#define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
  __LINE__, __FILE__, errno, strerror(errno)); exit(1); } while(0)

/// A 10 millisecond sleep
const Timespan UartStreamBase::SLEEP_TIME( 0.010 );

// Constructor
UartStreamBase::UartStreamBase( const Str& filename, UartStreamBase::Baudrate baudrate, const double timeoutSec,
                                LoggerBase& logger, size_t bufferSize, bool blocking, bool debug )
    : serPortFd_( -1 ),
      filename_( filename ),
      uartId_( DeviceToUART( filename_ ) ),
      defaultBaudrate_( baudrate ),
      startTimeout_( timeoutSec ),
      defaultStartTimeout_( timeoutSec ),
      betweenTimeout_( 0.060 ),
      lastError_( PORT_NOT_OPEN ),
      logger_( logger ),
      pushedChar_( 0 ),
      bufferSize_( bufferSize ),
      bufferPos_( -1 ),
      blocking_( blocking ),
      debug_( debug )
{

    if( uartId_ == NOT_UART )
    {
        uartId_ = UART_OTHER;
    }

    if( bufferSize_ > 1 )
    {
        pushedChars_ = new char[bufferSize_ + 1];
    }
    else
    {
        bufferSize_ = 1;
        pushedChars_ = &pushedChar_;
    }
}


/// Destructor
UartStreamBase::~UartStreamBase()
{
    if( isReadable( ) )
    {
        close();
    }
    if( bufferSize_ > 1 )
    {
        delete[] pushedChars_;
    }
}

UartStreamBase& UartStreamBase::openAtBaudrate( Baudrate baud )
{
    initializePort( uartId_ );

    // Port already open
    if( serPortFd_ != -1 )
    {
        close();
    }
    lastError_ = UartStreamBase::OK;

    serPortFd_ = ::open( uartToDevice( uartId_ ), O_RDWR | O_NOCTTY | ( blocking_ ? 0 : O_NONBLOCK ) );
    if( serPortFd_ == -1 )
    {
        lastError_ = UartStreamBase::CANNOT_OPEN_SERIAL_PORT;
        return *this;
    }

    if( uartId_ != NOT_UART )
    {

        //struct serial_struct sset;
        termios term;

        if( cfsetispeed( &term, BaudToSpeed( baud ) )
                || cfsetospeed( &term, BaudToSpeed( baud ) ) )
        {
            lastError_ = UartStreamBase::BAD_BAUD_RATE_FOR_PORT;
            return *this;
        }


        // Set baud rate and other termios attributes
        if( tcgetattr( serPortFd_, &term ) )
        {
            lastError_ = UartStreamBase::CANNOT_GET_DEVICE_SETTINGS;
            return *this;
        }

        /* input mode flags */
        term.c_iflag = 0;

        /* output mode flags */
        term.c_oflag = 0;

        /* control mode flags */
        term.c_cflag = CS8 | CREAD | CLOCAL | BaudToSpeed( baud );

        /* local mode flags */
        term.c_lflag = 0;

        /* line discipline -- not available on some platforms! */
        //term.c_line = 0;

        // if no data is available read() will return with no characters read
        term.c_cc[VMIN] = 0;
        int vtime;
        if( !blocking_ )
        {
            // read() will always return immediately
            vtime = 0;
        }
        else
        {
            // read() will return when characters are read, or after vtime 10ths of seconds
            int vtime = ( int )( defaultStartTimeout_.asFloat() * 10.0 );
            if( vtime == 0 )
            {
                vtime = 1;
            }
            logger_.syslog( "Opening uart, block timeout 10ths=", vtime, Syslog::INFO );
        }
        term.c_cc[VTIME] = vtime;

        tcflush( serPortFd_, TCIFLUSH );
        if( tcsetattr( serPortFd_, TCSANOW, &term ) )
        {
            lastError_ = UartStreamBase::BAD_PARAMETERS_FOR_PORT;
        }
    }
    bufferPos_ = -1;
    return *this;
}


OutStream& UartStreamBase::write( const char* data, size_t length )
{
    if( length == 0xFFFFFFFF )
    {
        length = ( ssize_t ) strlen( data );
    }
    size_t nBytes;
    nBytes = ::write( serPortFd_, data, length );

    if( debug_ )
    {
        printf( "Wrote UART%d %zu bytes: \"", ( int )uartId_ + 1, nBytes );
        fflush( stdout );
        fwrite( data, length, 1, stdout );
        fflush( stdout );
        printf( "\"\n" );
        fflush( stdout );
    }
    if( nBytes != length )
    {
        lastError_ = UartStreamBase::WRITE_CHAR_LOST;
    }
    return *this;
}


UartStreamBase& UartStreamBase::close()
{
    if( serPortFd_ != -1 )
    {
        ::close( serPortFd_ );
    }
    serPortFd_ = -1;
    lastError_ = UartStreamBase::PORT_NOT_OPEN;
    return *this;
}

size_t UartStreamBase::dataAvailable()
{
    if( !isReadable() )
    {
        return 0;
    }
    fillInBuffer();
    return bufferPos_ + 1;
}

size_t UartStreamBase::dataBuffered()
{
    if( !isReadable() )
    {
        return 0;
    }
    return bufferPos_ + 1;
}

// Returns true if the buffer contains the indicated bytes
size_t UartStreamBase::canReadUntil( const unsigned char match )
{
    if( !isReadable() )
    {
        return 0;
    }

    fillInBuffer();
    for( int i = 0; i <= bufferPos_; ++i )
    {
        if( pushedChars_[i] == match )
        {
            return i + 1;
        }
    }
    return 0;
}

// Returns #bytes to read if the buffer contains the indicated bytes, 0 if not there
size_t UartStreamBase::canReadUntil( const char* match, int matchLen, int num )
{
    if( !isReadable() )
    {
        return 0;
    }

    fillInBuffer();
    matchLen = matchLen <= 0 ? strlen( match ) : matchLen;
    num = num <= 0 ? bufferSize_ : num;
    for( int i = 0; i <= bufferPos_ - matchLen && i < num; ++i )
    {
        if( pushedChars_[i] == *match )
        {
            int matchedLen = 1;
            for( int j = 1; j < matchLen; ++j )
            {
                if( pushedChars_[i + j] != match[j] )
                {
                    break;
                }
                ++matchedLen;
            }
            if( matchedLen == matchLen )
            {
                return i + matchLen;
            }
        }
    }
    return 0;
}

// Returns #bytes to read if the buffer contains the indicated bytes, 0 if not there
size_t UartStreamBase::canReadUntil( const unsigned char* match, int matchLen, int num )
{
    if( !isReadable() )
    {
        return 0;
    }

    fillInBuffer();
    num = num <= 0 ? bufferSize_ : num;
    for( int i = 0; i <= bufferPos_ - matchLen && i < num; ++i )
    {
        if( pushedChars_[i] == *match )
        {
            int matchedLen = 1;
            for( int j = 1; j < matchLen; ++j )
            {
                if( pushedChars_[i + j] != match[j] )
                {
                    break;
                }
                ++matchedLen;
            }
            if( matchedLen == matchLen )
            {
                return i + matchLen;
            }
        }
    }
    return 0;
}

// Fills buffer with given number of lines (CR/LF) until buffer size is exceeded or timeout has expired
// Passes initial timeout value to getChar for first character receipt but then uses the member serial timeout
// after that
UartStreamBase& UartStreamBase::readLines( char* buf, size_t bufsize, size_t lines )
{
    int lines_received = 0;
    int lines_requested = lines;
    bool atLineEnd = false;
    zeroBytesRead();
    buf[getBytesRead()] = 0;             // initialize empty string
    size_t bufposmax = bufsize - 1; // maximum subscript
    unsigned char c = 0;
    int success = 1;

    serGetChar( &c, &success, startTimeout_ );

    // Bail out if there aren't any chars
    if( success <= 0 )
    {
        if( debug_ ) printf( "timeout on 1st char\n" );
        lastError_ = TIMEOUT;
        return *this;
    }

    buf[getBytesRead()] = c;
    incBytesRead();
    buf[getBytesRead()] = 0; // guarantee validly terminated string

    // Read characters until any termination condition is met.
    while( lines_received < lines_requested && c != 3 )
    {
        serGetChar( &c, &success );
        // Check for timeout error
        if( success <= 0 )
        {
            //logger_->syslog( "Timed out waiting for serial input: " + ( Str )UARTToString( uartId_ ), SyslogSeverity::SYSLOG_ERROR );
            if( debug_ ) printf( "Timeout: line %d #%03zu\n", lines_received, getBytesRead() );
            lastError_ = TIMEOUT;       // timeout
            return *this;
        }
        //if( debug ) printf("line %d #%03d char %02X (%c)\n", lines_received, lastIntervalValue_, (int)c, c);
        buf[getBytesRead()] = c;
        incBytesRead();
        buf[getBytesRead()] = 0; // guarantee validly terminated string
        // Check for new line
        //if (( c == '\r' || c == '\n' ) && !atLineEnd )
        if( ( c == 0x0A || c == 0x0D ) && !atLineEnd )
        {
            lines_received++;
            atLineEnd = true;
        }
        else
        {
            atLineEnd = false;
        }
        // Check for buffer full
        if( getBytesRead() >= bufposmax )
        {
            //logger_->syslog( "Serial buffer full: " + ( Str )UARTToString( uartId_ ), SyslogSeverity::SYSLOG_ERROR );
            lastError_ = BUFFER_FULL;       // buffer full
            return *this;
        }
    }
    lastError_ = OK;
    return *this;
}

// Fills buffer with given number of bytes unless timeout is exceeded
// Buffer should be at least num+1 bytes.
InStream& UartStreamBase::read( char* buf, size_t num )
{
    zeroBytesRead();
    buf[getBytesRead()] = 0;             // initialize empty string
    bool startTimeout( false ), betweenTimeout( false );
    Timestamp step( Timestamp::Now() );
    while( !startTimeout && !betweenTimeout && bytesRead() < num )
    {
        fillInBuffer();
        int nChars( 0 );
        if( bufferPos_ >= 0 )
        {
            step = Timestamp::Now();
            nChars = num - bytesRead();
            if( nChars > bufferPos_ + 1 )
            {
                nChars = bufferPos_ + 1;
            }
            memcpy( buf + bytesRead(), pushedChars_, nChars );
            if( nChars < bufferPos_ + 1 )
            {
                memmove( pushedChars_, pushedChars_ + nChars, bufferPos_ - nChars + 1 );
            }
            bufferPos_ -= nChars;
            incBytesRead( nChars );
        }
        if( bytesRead() < num )
        {
            startTimeout = ( bytesRead() == 0 && step.elapsed() > startTimeout_ );
            betweenTimeout = ( bytesRead() > 0 && step.elapsed() > betweenTimeout_ );
            SLEEP_TIME.sleepFor();
        }
    }
    lastError_ = ( startTimeout || betweenTimeout ) ? TIMEOUT : OK;
    return *this;
}

// Fills buffer until character string is matched unless timeout is exceeded
// Do not read more than num bytes
UartStreamBase& UartStreamBase::readUntil( char* buf, size_t num, const char* match, unsigned int matchLen, bool flushUntil )
{
    Timestamp start( Timestamp::Now() );
    zeroBytesRead();
    size_t bufPos( 0 );
    buf[bufPos] = 0;             // initialize empty string
    unsigned char c = 0;
    int success = 0;

    if( num <= 0 )
    {
        lastError_ = OK;
        return *this;
    }

    // For the first character only, use startTimeout_, otherwise use interchar delay.
    serGetChar( &c, &success, startTimeout_ );
    // Bail out if there aren't any chars
    if( success <= 0 )
    {
        lastError_ = TIMEOUT;
        return *this;
    }

    buf[bufPos] = c;
    incBytesRead();
    ++bufPos;
    buf[bufPos] = 0; // guarantee validly terminated string

    //Check for a match
    if( matchLen == 1 && c == *( unsigned char * )match )
    {
        return *this;
    }

    // Read characters until any termination condition is met.
    while( ( !flushUntil && getBytesRead() < num )
            || ( flushUntil && start.elapsed() <= startTimeout_ ) )
    {
        serGetChar( &c, &success );

        // Check for timeout error
        if( success <= 0 )
        {
            lastError_ = TIMEOUT;       // timeout
            return *this;
        }

        buf[bufPos] = c;
        incBytesRead();
        ++bufPos;
        // guarantee validly terminated string or full buffer
        if( bufPos < num )
        {
            buf[bufPos] = 0;
        }

        //Flush
        if( flushUntil && bufPos > matchLen )
        {
            memmove( buf, buf + bufPos - matchLen, matchLen + 1 );
            bufPos = matchLen;
        }

        //Check for a match
        if( bufPos >= matchLen && 0 == memcmp( buf + bufPos - matchLen, match, matchLen ) )
        {
            break;
        }

        // Check for buffer full
        if( !flushUntil && getBytesRead() >= num )
        {
            lastError_ = BUFFER_FULL;       // buffer full
            return *this;
        }

        // Check for timeoout
        if( flushUntil && start.elapsed() > startTimeout_ )
        {
            lastError_ = TIMEOUT;
            return *this;
        }
    }
    lastError_ = OK;
    return *this;
}

// Fills buffer until character string is matched unless timeout is exceeded
// Do not read more than num bytes
UartStreamBase& UartStreamBase::readUntil( char* buf, size_t num, const unsigned char* match, unsigned int matchLen, bool flushUntil )
{
    Timestamp start( Timestamp::Now() );
    zeroBytesRead();
    size_t bufPos( 0 );
    buf[bufPos] = 0;             // initialize empty string
    unsigned char c = 0;
    int success = 0;

    if( num <= 0 )
    {
        lastError_ = OK;
        return *this;
    }

    // For the first character only, use startTimeout_, otherwise use interchar delay.
    serGetChar( &c, &success, startTimeout_ );
    // Bail out if there aren't any chars
    if( success <= 0 )
    {
        lastError_ = TIMEOUT;
        return *this;
    }

    buf[bufPos] = c;
    incBytesRead();
    ++bufPos;
    buf[bufPos] = 0; // guarantee validly terminated string

    //Check for a match
    if( matchLen == 1 && c == *( unsigned char * )match )
    {
        return *this;
    }

    // Read characters until any termination condition is met.
    while( ( !flushUntil && getBytesRead() < num )
            || ( flushUntil && start.elapsed() <= startTimeout_ ) )
    {
        serGetChar( &c, &success );

        // Check for timeout error
        if( success <= 0 )
        {
            lastError_ = TIMEOUT;       // timeout
            return *this;
        }

        buf[bufPos] = c;
        incBytesRead();
        ++bufPos;
        // guarantee validly terminated string or full buffer
        if( bufPos < num )
        {
            buf[bufPos] = 0;
        }

        //Flush
        if( flushUntil && bufPos > matchLen )
        {
            memmove( buf, buf + bufPos - matchLen, matchLen + 1 );
            bufPos = matchLen;
        }

        //Check for a match
        if( bufPos >= matchLen && 0 == memcmp( buf + bufPos - matchLen, match, matchLen ) )
        {
            break;
        }

        // Check for buffer full
        if( !flushUntil && getBytesRead() >= num )
        {
            lastError_ = BUFFER_FULL;       // buffer full
            return *this;
        }

        // Check for timeoout
        if( flushUntil && start.elapsed() > startTimeout_ )
        {
            lastError_ = TIMEOUT;
            return *this;
        }
    }
    lastError_ = OK;
    return *this;
}


UartStreamBase& UartStreamBase::flush( int num )
{
    unsigned char c = 0;
    int success = 0;
    int count = 0;
    zeroBytesRead();
    // Quick flush of large buffers
    if( bufferSize_ > 16 )
    {
        incBytesRead( bufferPos_ );
        bufferPos_ = -1;
        fillInBuffer();
        incBytesRead( bufferPos_ );
        bufferPos_ = -1;
        if( bufferSize_ > 4095 )
        {
            return *this;
        }
    }

    // Flush any characters
    if( ( num < 0 && uartId_ != NOT_UART ) || count < num ) do
        {
            serGetChar( &c, &success, Timespan::ZERO_TIMESPAN );
            if( success >= 1 )
            {
                incBytesRead();
            }
            else if( num < 0 || ++count < num )
            {
                SLEEP_TIME.sleepFor();
            }
        }
        while( success >= 1 && ( num < 0 || ++count < num ) );

    return *this;
}

UartStreamBase& UartStreamBase::flushCRLF()
{
    unsigned char c = 0;
    int success = 0;

    // Flush any CR or Line Feed characters left over from end of previous line
    do
    {
        serGetChar( &c, &success, Timespan::ZERO_TIMESPAN );
        if( ( success >= 1 ) && ( c <= 31 ) )
        {
            incBytesRead();
        }
    }
    while( ( success >= 1 ) && ( c <= 31 ) );

    if( success >= 1 && c > 31 )
    {
        for( int i = bufferPos_; i > 0; --i )
        {
            pushedChars_[i] = pushedChars_[i - 1];
        }
        ++bufferPos_;
        pushedChars_[0] = c;
    }
    return *this;
}

void UartStreamBase::serGetChar( unsigned char* c, int* success, const Timespan& timeout )
// Receives a character from the serial port or times out
{
    if( bufferPos_ <= ( ( int ) bufferSize_ >> 1 ) )
    {
        fillInBuffer();
    }

    if( bufferPos_ >= 0 )
    {
        *c = ( unsigned char )pushedChars_[0];
        for( int i = 0; i < bufferPos_; ++i )
        {
            pushedChars_[i] = pushedChars_[i + 1];
        }
        --bufferPos_;
        *success = 1;
        /*if( *success > 0 && uartId_ == UartStreamBase::UART2_TX1 )
        {
            printf("|%c%02hx\n", isprint(*c) ? *c:'_', *c);
        }*/
    }
    else
    {
        Timestamp start( Timestamp::Now() );
        Timespan myTimeout( timeout == Timespan::INVALID_TIMESPAN ? betweenTimeout_ : timeout );
        *success = 0;
        do
        {
            *success = ::read( serPortFd_, c, ( size_t )1 );
            //if( debug_ )
            //{
            //    printf("Read UART%d 0x%02hX='%c', success=%d\n", (int)uartId_+1, *c, *c, *success );
            //}
            /*if( *success > 0 && uartId_ == UartStreamBase::UART7_TX2 )
            {
                printf("/%c/", (char)*c);
            }*///if( uartId_ == UartStreamBase::UART_A4 )printf("%c", *success + 'a');
            if( *success <= 0 && start.elapsed() < myTimeout )
            {
                SLEEP_TIME.sleepFor();
            }
        }
        while( *success <= 0 && start.elapsed() < myTimeout );

    }
    /*if( *success <= 0 && uartId_ == UartStreamBase::UART7_TX2 )
    {
        printf("******No Char, ", (char)*c);
    }*/
}

void UartStreamBase::fillInBuffer()
{
    if( bufferSize_ - bufferPos_ > 1 )
    {
        int bytesRead = ::read( serPortFd_, pushedChars_ + bufferPos_ + 1,
                                ( size_t )( bufferSize_ - bufferPos_ - 1 ) );
        if( bytesRead > 0 )
        {
            bufferPos_ += bytesRead;
            /*if( uartId_ == UartStreamBase::UART7_TX2 )
            {
                pushedChars_[bufferPos_ + 1] = 0;
                printf( "<%s>", pushedChars_ );
            }*/
            /*if( uartId_ == UartStreamBase::UART2_TX1 )
            {
                printf("\ninbuf %d, pos=%d|", bytesRead, bufferPos_ );
                for(int i =0; i < bytesRead; ++i)
                {
                    printf("%02hx",pushedChars_[bufferPos_-bytesRead+i]);
                }
                printf("\n");
            }*/
        }
    }
}

const char* UartStreamBase::uartToDevice( UartId uart )
{
    switch( uart )
    {
    case UartStreamBase::UART1_TX0:
        return "/dev/ttyTX0";
    case UartStreamBase::UART2_TX1:
        return "/dev/ttyTX1";
    case UartStreamBase::UART3_S1:
        return "/dev/ttyS1";
    case UartStreamBase::UART4_S2:
        return "/dev/ttyS2";
    case UartStreamBase::UART5_S0: // Console Port
        return "/dev/ttyS0";
    case UartStreamBase::UART6_S3:
        return "/dev/ttyS3";
    case UartStreamBase::UART7_TX2:
        return "/dev/ttyTX2";
    case UartStreamBase::UART_A0:
        return "/dev/ttyA0";
    case UartStreamBase::UART_A1:
        return "/dev/ttyA1";
    case UartStreamBase::UART_A2:
        return "/dev/ttyA2";
    case UartStreamBase::UART_A3:
        return "/dev/ttyA3";
    case UartStreamBase::UART_A4:
        return "/dev/ttyA4";
    case UartStreamBase::UART_A5:
        return "/dev/ttyA5";
    case UartStreamBase::UART_A6:
        return "/dev/ttyA6";
    case UartStreamBase::UART_A7:
        return "/dev/ttyA7";
    case UartStreamBase::UART_B0:
        return "/dev/ttyB0";
    case UartStreamBase::UART_B1:
        return "/dev/ttyB1";
    case UartStreamBase::UART_B2:
        return "/dev/ttyB2";
    case UartStreamBase::UART_B3:
        return "/dev/ttyB3";
    case UartStreamBase::UART_B4:
        return "/dev/ttyB4";
    case UartStreamBase::UART_B5:
        return "/dev/ttyB5";
    case UartStreamBase::UART_B6:
        return "/dev/ttyB6";
    case UartStreamBase::UART_B7:
        return "/dev/ttyB7";
    case UartStreamBase::UART_C0:
        return "/dev/ttyC0";
    case UartStreamBase::UART_C1:
        return "/dev/ttyC1";
    case UartStreamBase::UART_C2:
        return "/dev/ttyC2";
    case UartStreamBase::UART_C3:
        return "/dev/ttyC3";
    case UartStreamBase::UART_C4:
        return "/dev/ttyC4";
    case UartStreamBase::UART_C5:
        return "/dev/ttyC5";
    case UartStreamBase::UART_C6:
        return "/dev/ttyC6";
    case UartStreamBase::UART_C7:
        return "/dev/ttyC7";
    case UartStreamBase::UART_OTHER:
    case UartStreamBase::NOT_UART:
        return filename_.cStr();
    }
    return "(undefined)";
};


UartStreamBase::UartId UartStreamBase::DeviceToUART( const Str& name )
{
    if( name.compare( "/dev/ttyTX0" ) == 0 )
    {
        return UartStreamBase::UART1_TX0;
    }
    else if( name.compare( "/dev/ttyTX1" ) == 0 )
    {
        return UartStreamBase::UART2_TX1;
    }
    else if( name.compare( "/dev/ttyS1" ) == 0 )
    {
        return UartStreamBase::UART3_S1;
    }
    else if( name.compare( "/dev/ttyS2" ) == 0 )
    {
        return UartStreamBase::UART4_S2;
    }
    else if( name.compare( "/dev/ttyS0" ) == 0 )
    {
        return UartStreamBase::UART5_S0;
    }
    else if( name.compare( "/dev/ttyS3" ) == 0 )
    {
        return UartStreamBase::UART6_S3;
    }
    else if( name.compare( "/dev/ttyTX2" ) == 0 )
    {
        return UartStreamBase::UART7_TX2;
    }
    else if( name.compare( "/dev/ttyA0" ) == 0 )
    {
        return UartStreamBase::UART_A0;
    }
    else if( name.compare( "/dev/ttyA1" ) == 0 )
    {
        return UartStreamBase::UART_A1;
    }
    else if( name.compare( "/dev/ttyA2" ) == 0 )
    {
        return UartStreamBase::UART_A2;
    }
    else if( name.compare( "/dev/ttyA3" ) == 0 )
    {
        return UartStreamBase::UART_A3;
    }
    else if( name.compare( "/dev/ttyA4" ) == 0 )
    {
        return UartStreamBase::UART_A4;
    }
    else if( name.compare( "/dev/ttyA5" ) == 0 )
    {
        return UartStreamBase::UART_A5;
    }
    else if( name.compare( "/dev/ttyA6" ) == 0 )
    {
        return UartStreamBase::UART_A6;
    }
    else if( name.compare( "/dev/ttyA7" ) == 0 )
    {
        return UartStreamBase::UART_A7;
    }
    else if( name.compare( "/dev/ttyB0" ) == 0 )
    {
        return UartStreamBase::UART_B0;
    }
    else if( name.compare( "/dev/ttyB1" ) == 0 )
    {
        return UartStreamBase::UART_B1;
    }
    else if( name.compare( "/dev/ttyB2" ) == 0 )
    {
        return UartStreamBase::UART_B2;
    }
    else if( name.compare( "/dev/ttyB3" ) == 0 )
    {
        return UartStreamBase::UART_B3;
    }
    else if( name.compare( "/dev/ttyB4" ) == 0 )
    {
        return UartStreamBase::UART_B4;
    }
    else if( name.compare( "/dev/ttyB5" ) == 0 )
    {
        return UartStreamBase::UART_B5;
    }
    else if( name.compare( "/dev/ttyB6" ) == 0 )
    {
        return UartStreamBase::UART_B6;
    }
    else if( name.compare( "/dev/ttyB7" ) == 0 )
    {
        return UartStreamBase::UART_B7;
    }
    else if( name.compare( "/dev/ttyC0" ) == 0 )
    {
        return UartStreamBase::UART_C0;
    }
    else if( name.compare( "/dev/ttyC1" ) == 0 )
    {
        return UartStreamBase::UART_C1;
    }
    else if( name.compare( "/dev/ttyC2" ) == 0 )
    {
        return UartStreamBase::UART_C2;
    }
    else if( name.compare( "/dev/ttyC3" ) == 0 )
    {
        return UartStreamBase::UART_C3;
    }
    else if( name.compare( "/dev/ttyC4" ) == 0 )
    {
        return UartStreamBase::UART_C4;
    }
    else if( name.compare( "/dev/ttyC5" ) == 0 )
    {
        return UartStreamBase::UART_C5;
    }
    else if( name.compare( "/dev/ttyC6" ) == 0 )
    {
        return UartStreamBase::UART_C6;
    }
    else if( name.compare( "/dev/ttyC7" ) == 0 )
    {
        return UartStreamBase::UART_C7;
    }
    else if( name.find( "/dev/tty" ) == 0 )
    {
        return UartStreamBase::UART_OTHER;
    }
    else
    {
        return UartStreamBase::NOT_UART;
    }
};



const char* UartStreamBase::uartIdToString( UartId uart )
{
    switch( uart )
    {
    case UartStreamBase::UART1_TX0:
        return "/dev/ttyTX0(UART1)";
    case UartStreamBase::UART2_TX1:
        return "/dev/ttyTX1(UART2)";
    case UartStreamBase::UART3_S1:
        return "/dev/ttyS1(UART3)";
    case UartStreamBase::UART4_S2:
        return "/dev/ttyS2(UART4)";
    case UartStreamBase::UART5_S0: // Console Port
        return "/dev/ttyS0(UART5)";
    case UartStreamBase::UART6_S3:
        return "/dev/ttyS3(UART6)";
    case UartStreamBase::UART7_TX2:
        return "/dev/ttyTX2(UART7)";
    case UartStreamBase::UART_A0:
        return "/dev/ttyA0";
    case UartStreamBase::UART_A1:
        return "/dev/ttyA1";
    case UartStreamBase::UART_A2:
        return "/dev/ttyA2";
    case UartStreamBase::UART_A3:
        return "/dev/ttyA3";
    case UartStreamBase::UART_A4:
        return "/dev/ttyA4";
    case UartStreamBase::UART_A5:
        return "/dev/ttyA5";
    case UartStreamBase::UART_A6:
        return "/dev/ttyA6";
    case UartStreamBase::UART_A7:
        return "/dev/ttyA7";
    case UartStreamBase::UART_B0:
        return "/dev/ttyB0";
    case UartStreamBase::UART_B1:
        return "/dev/ttyB1";
    case UartStreamBase::UART_B2:
        return "/dev/ttyB2";
    case UartStreamBase::UART_B3:
        return "/dev/ttyB3";
    case UartStreamBase::UART_B4:
        return "/dev/ttyB4";
    case UartStreamBase::UART_B5:
        return "/dev/ttyB5";
    case UartStreamBase::UART_B6:
        return "/dev/ttyB6";
    case UartStreamBase::UART_B7:
        return "/dev/ttyB7";
    case UartStreamBase::UART_C0:
        return "/dev/ttyC0";
    case UartStreamBase::UART_C1:
        return "/dev/ttyC1";
    case UartStreamBase::UART_C2:
        return "/dev/ttyC2";
    case UartStreamBase::UART_C3:
        return "/dev/ttyC3";
    case UartStreamBase::UART_C4:
        return "/dev/ttyC4";
    case UartStreamBase::UART_C5:
        return "/dev/ttyC5";
    case UartStreamBase::UART_C6:
        return "/dev/ttyC6";
    case UartStreamBase::UART_C7:
        return "/dev/ttyC7";
    case UartStreamBase::UART_OTHER:
    case UartStreamBase::NOT_UART:
        return filename_.cStr();
    }
    return "(undefined)";
};

const char* UartStreamBase::errorString()
{
    switch( lastError_ )
    {
    case UartStreamBase::OK:
        return "no error";
    case UartStreamBase::BUFFER_FULL:
        return "serial buffer full";
    case UartStreamBase::TIMEOUT:
        return "serial timeout";
    case UartStreamBase::CANNOT_OPEN_SERIAL_PORT:
        return "cannot open serial port";
    case UartStreamBase::CANNOT_GET_DEVICE_SETTINGS:
        return "cannot get device settings";
    case UartStreamBase::BAD_BAUD_RATE_FOR_PORT:
        return "bad baud rate for port";
    case UartStreamBase::BAD_PARAMETERS_FOR_PORT:
        return "bad parameters for port";
    case UartStreamBase::PORT_NOT_OPEN:
        return "port not yet opened";
    case UartStreamBase::WRITE_CHAR_LOST:
        return "character(s) dropped while writing";
    }
    return "(undefined)";
};

const speed_t UartStreamBase::BaudToSpeed( Baudrate baud )
{
    switch( baud )
    {
    case UartStreamBase::B_50:
        return B50;
    case UartStreamBase::B_75:
        return B75;
    case UartStreamBase::B_110:
        return B110;
    case UartStreamBase::B_134:
        return B134;
    case UartStreamBase::B_150:
        return B150;
    case UartStreamBase::B_200:
        return B200;
    case UartStreamBase::B_300:
        return B300;
    case UartStreamBase::B_600:
        return B600;
    case UartStreamBase::B_1200:
        return B1200;
    case UartStreamBase::B_1800:
        return B1800;
    case UartStreamBase::B_2400:
        return B2400;
    case UartStreamBase::B_4800:
        return B4800;
    case UartStreamBase::B_9600:
        return B9600;
    case UartStreamBase::B_19200:
        return B19200;
    case UartStreamBase::B_38400:
        return B38400;
    case UartStreamBase::B_57600:
        return B57600;
    case UartStreamBase::B_115200:
        return B115200;
    case UartStreamBase::B_230400:
        return B230400;
        /*case UartStreamBase::B_460800: -- speeds not available on some platforms
            return B460800;
        case UartStreamBase::B_921600:
            return B921600;*/
    }
    return B9600;
};
