From 2978386696bea6f5b33b39399417cb7d61650a57 Mon Sep 17 00:00:00 2001 From: William Woodall Date: Sat, 4 Feb 2012 21:14:22 -0600 Subject: [PATCH] Read/write seem to be working on linux, need to test on OS X. --- examples/serial_example.cc | 1 + include/serial/impl/unix.h | 4 +- include/serial/serial.h | 187 ++++++++++++++------------ src/impl/unix.cc | 262 ++++++++++++++++++++----------------- src/serial.cc | 168 +++++++++++++----------- 5 files changed, 341 insertions(+), 281 deletions(-) diff --git a/examples/serial_example.cc b/examples/serial_example.cc index 60be39a..3ba886b 100644 --- a/examples/serial_example.cc +++ b/examples/serial_example.cc @@ -1,5 +1,6 @@ #include #include +#include // OS Specific sleep #ifdef __WIN32__ diff --git a/include/serial/impl/unix.h b/include/serial/impl/unix.h index b0d630b..98f0fec 100644 --- a/include/serial/impl/unix.h +++ b/include/serial/impl/unix.h @@ -75,7 +75,7 @@ public: available (); size_t - read (char* buf, size_t size = 1); + read (unsigned char* buf, size_t size = 1); size_t write (const string &data); @@ -174,7 +174,7 @@ private: string port_; // Path to the file descriptor int fd_; // The current file descriptor - bool isOpen_; + bool is_open_; bool xonxoff_; bool rtscts_; diff --git a/include/serial/serial.h b/include/serial/serial.h index 0311577..ee3ef39 100644 --- a/include/serial/serial.h +++ b/include/serial/serial.h @@ -88,41 +88,38 @@ typedef enum { class Serial { public: /*! - * Constructor, creates a SerialPortBoost object and opens the port. - * - * \param port A std::string containing the address of the serial port, - * which would be something like 'COM1' on Windows and '/dev/ttyS0' - * on Linux. - * - * \param baudrate An integer that represents the buadrate - * - * \param timeout A long that represents the time (in milliseconds) until a - * timeout on reads occur. Setting this to zero (0) will cause reading to - * be non-blocking, i.e. the available data will be returned immediately, - * but it will not block to wait for more. Setting this to a number less - * than zero (-1) will result in infinite blocking behaviour, i.e. the - * serial port will block until either size bytes have been read or an - * exception has occured. - * - * \param bytesize Size of each byte in the serial transmission of data, - * default is EIGHTBITS, possible values are: FIVEBITS, SIXBITS, SEVENBITS, - * EIGHTBITS - * - * \param parity Method of parity, default is PARITY_NONE, possible values - * are: PARITY_NONE, PARITY_ODD, PARITY_EVEN - * - * \param stopbits Number of stop bits used, default is STOPBITS_ONE, - * possible values are: STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO - * - * \param flowcontrol Type of flowcontrol used, default is - * FLOWCONTROL_NONE, possible values are: FLOWCONTROL_NONE, - * FLOWCONTROL_SOFTWARE, FLOWCONTROL_HARDWARE - * - * \param buffer_size The maximum size of the internal buffer, defaults - * to 256 bytes (2^8). - * - * \throw PortNotOpenedException - */ + * Constructor, creates a SerialPortBoost object and opens the port. + * + * \param port A std::string containing the address of the serial port, + * which would be something like 'COM1' on Windows and '/dev/ttyS0' + * on Linux. + * + * \param baudrate An integer that represents the buadrate + * + * \param timeout A long that represents the time (in milliseconds) until a + * timeout on reads occur. Setting this to zero (0) will cause reading to + * be non-blocking, i.e. the available data will be returned immediately, + * but it will not block to wait for more. Setting this to a number less + * than zero (-1) will result in infinite blocking behaviour, i.e. the + * serial port will block until either size bytes have been read or an + * exception has occured. + * + * \param bytesize Size of each byte in the serial transmission of data, + * default is EIGHTBITS, possible values are: FIVEBITS, SIXBITS, SEVENBITS, + * EIGHTBITS + * + * \param parity Method of parity, default is PARITY_NONE, possible values + * are: PARITY_NONE, PARITY_ODD, PARITY_EVEN + * + * \param stopbits Number of stop bits used, default is STOPBITS_ONE, + * possible values are: STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO + * + * \param flowcontrol Type of flowcontrol used, default is + * FLOWCONTROL_NONE, possible values are: FLOWCONTROL_NONE, + * FLOWCONTROL_SOFTWARE, FLOWCONTROL_HARDWARE + * + * \throw PortNotOpenedException + */ Serial (const std::string &port = "", unsigned long baudrate = 9600, long timeout = 0, @@ -135,25 +132,25 @@ public: virtual ~Serial (); /*! - * Opens the serial port as long as the portname is set and the port isn't - * alreay open. - * - * If the port is provided to the constructor then an explicit call to open - * is not needed. - * - * \see Serial::Serial - * - * \throw std::invalid_argument - * \throw serial::SerialExecption - * \throw serial::IOException - */ + * Opens the serial port as long as the portname is set and the port isn't + * alreay open. + * + * If the port is provided to the constructor then an explicit call to open + * is not needed. + * + * \see Serial::Serial + * + * \throw std::invalid_argument + * \throw serial::SerialExecption + * \throw serial::IOException + */ void open (); /*! Gets the open status of the serial port. - * - * \return Returns true if the port is open, false otherwise. - */ + * + * \return Returns true if the port is open, false otherwise. + */ bool isOpen () const; @@ -166,51 +163,67 @@ public: available(); /*! Read a given amount of bytes from the serial port. - * - * If a timeout is set it may return less characters than requested. With - * no timeout it will block until the requested number of bytes have been - * read or until an exception occurs. - * - * \param size A size_t defining how many bytes to be read. - * - * \return A std::string containing the data read. - */ + * + * If a timeout is set it may return less characters than requested. With + * no timeout it will block until the requested number of bytes have been + * read or until an exception occurs. + * + * \param size A size_t defining how many bytes to be read. + * + * \return A std::string containing the data read. + */ + size_t + read (unsigned char *buffer, size_t size); + size_t + read (std::vector &buffer, size_t size = 1); + size_t + read (std::string &buffer, size_t size = 1); std::string read (size_t size = 1); /*! Reads in a line or until a given delimiter has been processed - * - * Reads from the serial port until a single line has been read. - * - * \param size A maximum length of a line defaults to size_t::max() - * \param eol A string to match against for the EOL. - * - * \return A std::string containing the line. - */ + * + * Reads from the serial port until a single line has been read. + * + * \param size A maximum length of a line, defaults to 65536 (2^16) + * \param eol A string to match against for the EOL. + * + * \return A std::string containing the line. + */ + size_t + readline (std::string &buffer, + size_t size = 65536, + std::string eol = "\n"); std::string - readline(size_t size = std::numeric_limits::max(), - std::string eol = "\n"); + readline (size_t size = 65536, + std::string eol = "\n"); /*! Reads in multiple lines until the serail port times out. - * - * This requires a timeout > 0 before it can be run. It will read until a - * timeout occurs and return a list of strings. - * - * \param eol A string to match against for the EOL. - * - * \return A vector containing the lines. - */ + * + * This requires a timeout > 0 before it can be run. It will read until a + * timeout occurs and return a list of strings. + * + * \param size A maximum length of combined lines, defaults to 65536 (2^16) + * + * \param eol A string to match against for the EOL. + * + * \return A vector containing the lines. + */ std::vector - readlines(std::string eol = "\n"); + readlines (size_t size = 65536, std::string eol = "\n"); /*! Write a string to the serial port. - * - * \param data A const std::string reference containg the data to be written - * to the serial port. - * - * \return A size_t representing the number of bytes actually written to - * the serial port. - */ + * + * \param data A const reference containg the data to be written + * to the serial port. + * + * \return A size_t representing the number of bytes actually written to + * the serial port. + */ + size_t + write (const unsigned char *data, size_t size); + size_t + write (const std::vector &data); size_t write (const std::string &data); @@ -410,6 +423,10 @@ private: class ScopedReadLock; class ScopedWriteLock; + // Read common function + size_t + read_ (unsigned char *buffer, size_t size); + }; class SerialExecption : public std::exception diff --git a/src/impl/unix.cc b/src/impl/unix.cc index 9a5c23b..4896d63 100644 --- a/src/impl/unix.cc +++ b/src/impl/unix.cc @@ -10,15 +10,20 @@ #include #include #include -#include -#include -#include #include #if defined(__linux__) #include #endif +#include +#include +#include +#ifdef __MACH__ +#include +#include +#endif + #include "serial/impl/unix.h" #ifndef TIOCINQ @@ -41,7 +46,7 @@ Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate, long timeout, bytesize_t bytesize, parity_t parity, stopbits_t stopbits, flowcontrol_t flowcontrol) -: port_ (port), fd_ (-1), isOpen_ (false), xonxoff_ (true), rtscts_ (false), +: port_ (port), fd_ (-1), is_open_ (false), xonxoff_ (true), rtscts_ (false), timeout_ (timeout), baudrate_ (baudrate), parity_ (parity), bytesize_ (bytesize), stopbits_ (stopbits), flowcontrol_ (flowcontrol) { @@ -61,13 +66,13 @@ Serial::SerialImpl::~SerialImpl () void Serial::SerialImpl::open () { - if (port_.empty()) + if (port_.empty ()) { - throw invalid_argument ("bad port specified"); + throw invalid_argument ("Empty port is invalid."); } - if (isOpen_ == true) + if (is_open_ == true) { - throw SerialExecption ("port already open"); + throw SerialExecption ("Serial port already open."); } fd_ = ::open (port_.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); @@ -82,7 +87,7 @@ Serial::SerialImpl::open () return; case ENFILE: case EMFILE: - throw IOException ("to many file handles open"); + throw IOException ("Too many file handles open."); break; default: throw IOException (errno); @@ -90,7 +95,7 @@ Serial::SerialImpl::open () } reconfigurePort(); - isOpen_ = true; + is_open_ = true; } void @@ -99,7 +104,7 @@ Serial::SerialImpl::reconfigurePort () if (fd_ == -1) { // Can only operate on a valid file descriptor - throw IOException ("invalid file descriptor"); + throw IOException ("Invalid file descriptor, is the serial port open?"); } struct termios options; // The options for the file descriptor @@ -266,7 +271,8 @@ Serial::SerialImpl::reconfigurePort () if (stopbits_ == STOPBITS_ONE) options.c_cflag &= (unsigned long) ~(CSTOPB); else if (stopbits_ == STOPBITS_ONE_POINT_FIVE) - options.c_cflag |= (CSTOPB); // XXX same as TWO.. there is no POSIX support for 1.5 + // ONE POINT FIVE same as TWO.. there is no POSIX support for 1.5 + options.c_cflag |= (CSTOPB); else if (stopbits_ == STOPBITS_TWO) options.c_cflag |= (CSTOPB); else @@ -318,8 +324,12 @@ Serial::SerialImpl::reconfigurePort () #error "OS Support seems wrong." #endif - options.c_cc[VMIN] = 1; // Minimum of 1 character in the buffer - options.c_cc[VTIME] = 0; // timeout on waiting for new data + // http://www.unixwiz.net/techtips/termios-vmin-vtime.html + // this basically sets the read call up to be a polling read, + // but we are using select to ensure there is data available + // to read before each call, so we should never needlessly poll + options.c_cc[VMIN] = 0; + options.c_cc[VTIME] = 0; // activate settings ::tcsetattr (fd_, TCSANOW, &options); @@ -328,27 +338,27 @@ Serial::SerialImpl::reconfigurePort () void Serial::SerialImpl::close () { - if (isOpen_ == true) + if (is_open_ == true) { if (fd_ != -1) { ::close (fd_); // Ignoring the outcome fd_ = -1; } - isOpen_ = false; + is_open_ = false; } } bool Serial::SerialImpl::isOpen () const { - return isOpen_; + return is_open_; } size_t Serial::SerialImpl::available () { - if (!isOpen_) + if (!is_open_) { return 0; } @@ -364,113 +374,131 @@ Serial::SerialImpl::available () } } +inline void get_time_now(struct timespec &time) { +# ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + time.tv_sec = mts.tv_sec; + time.tv_nsec = mts.tv_nsec; +# else + clock_gettime(CLOCK_REALTIME, &time); +# endif +} + size_t -Serial::SerialImpl::read (char* buf, size_t size) +Serial::SerialImpl::read (unsigned char* buf, size_t size) { - if (!isOpen_) + if (!is_open_) { throw PortNotOpenedException ("Serial::read"); } fd_set readfds; - ssize_t bytes_read = 0; - int count = 0; - while (true) + size_t bytes_read = 0; + struct timeval timeout; + timeout.tv_sec = timeout_ / 1000; + timeout.tv_usec = static_cast (timeout_ % 1000) * 1000; + while (bytes_read < size) { - count++; - // printf("Counting: %u\n", count); - if (timeout_ != -1) - { - FD_ZERO (&readfds); - FD_SET (fd_, &readfds); - struct timeval timeout; - timeout.tv_sec = timeout_ / 1000; - timeout.tv_usec = static_cast (timeout_ % 1000) * 1000; - int r = select (fd_ + 1, &readfds, NULL, NULL, &timeout); + FD_ZERO (&readfds); + FD_SET (fd_, &readfds); + // On Linux the timeout struct is updated by select to contain the time + // left on the timeout to make looping easier, but on other platforms this + // does not occur. +#if !defined(__linux__) + // Begin timing select + struct timespec start, end; + get_time_now(start); +#endif + // Do the select + int r = select (fd_ + 1, &readfds, NULL, NULL, &timeout); +#if !defined(__linux__) + // Calculate difference and update the structure + get_time_now(end); + // Calculate the time select took + struct timeval diff; + diff.tv_sec = end.tv_sec-start.tv_sec; + diff.tv_usec = (end.tv_nsec-start.tv_nsec)/1000; + // Update the timeout + if (timeout.tv_sec <= diff.tv_sec) { + timeout.tv_sec = 0; + } else { + timeout.tv_sec -= diff.tv_sec; + } + if (timeout.tv_usec <= diff.tv_usec) { + timeout.tv_usec = 0; + } else { + timeout.tv_usec -= diff.tv_usec; + } +#endif - if (r == -1 && errno == EINTR) + // Figure out what happened by looking at select's response 'r' +/** Error **/ + if (r < 0) { + // Select was interrupted, try again + if (errno == EINTR) { continue; - - if (r == -1) - { - throw IOException (errno); } + // Otherwise there was some error + throw IOException (errno); } - - if (timeout_ == -1 || FD_ISSET (fd_, &readfds)) - { - bytes_read = ::read (fd_, buf, size); - // read should always return some data as select reported it was - // ready to read when we get to this point. - if (bytes_read < 1) - { - // Disconnected devices, at least on Linux, show the - // behavior that they are always ready to read immediately - // but reading returns nothing. - throw SerialExecption ("device reports readiness to read but " - "returned no data (device disconnected?)"); - } +/** Timeout **/ + if (r == 0) { break; } - else - { - break; +/** Something ready to read **/ + if (r > 0) { + // Make sure our file descriptor is in the ready to read list + if (FD_ISSET (fd_, &readfds)) { + // This should be non-blocking returning only what is avaialble now + // Then returning so that select can block again. + ssize_t bytes_read_now = ::read (fd_, buf, size-bytes_read); + // read should always return some data as select reported it was + // ready to read when we get to this point. + if (bytes_read_now < 1) + { + // Disconnected devices, at least on Linux, show the + // behavior that they are always ready to read immediately + // but reading returns nothing. + throw SerialExecption ("device reports readiness to read but " + "returned no data (device disconnected?)"); + } + // Update bytes_read + bytes_read += static_cast (bytes_read_now); + // If bytes_read == size then we have read everything we need + if (bytes_read == size) { + break; + } + // If bytes_read < size then we have more to read + if (bytes_read < size) { + continue; + } + // If bytes_read > size then we have over read, which shouldn't happen + if (bytes_read > size) { + throw SerialExecption ("read over read, too many bytes where " + "read, this shouldn't happen, might be " + "a logical error!"); + } + } + // This shouldn't happen, if r > 0 our fd has to be in the list! + throw IOException ("select reports ready to read, but our fd isn't" + " in the list, this shouldn't happen!"); } } - return static_cast (bytes_read); + return bytes_read; } size_t Serial::SerialImpl::write (const string &data) { - if (isOpen_ == false) + if (is_open_ == false) { throw PortNotOpenedException ("Serial::write"); } - - fd_set writefds; - ssize_t bytes_written = 0; - while (true) - { - if (timeout_ != -1) - { - FD_ZERO (&writefds); - FD_SET (fd_, &writefds); - struct timeval timeout; - timeout.tv_sec = timeout_ / 1000; - timeout.tv_usec = static_cast (timeout_ % 1000) * 1000; - int r = select (fd_ + 1, NULL, &writefds, NULL, &timeout); - if (r == -1 && errno == EINTR) - continue; - - if (r == -1) - { - throw IOException (errno); - } - } - - if (timeout_ == -1 || FD_ISSET (fd_, &writefds)) - { - bytes_written = ::write (fd_, data.c_str (), data.length ()); - // read should always return some data as select reported it was - // ready to read when we get to this point. - if (bytes_written < 1) - { - // Disconnected devices, at least on Linux, show the - // behavior that they are always ready to read immediately - // but reading returns nothing. - throw SerialExecption ("device reports readiness to read but " - "returned no data (device disconnected?)"); - } - break; - } - else - { - break; - } - } - - return static_cast (bytes_written); + return static_cast (::write (fd_, data.c_str (), data.length ())); } void @@ -501,7 +529,7 @@ void Serial::SerialImpl::setBaudrate (unsigned long baudrate) { baudrate_ = baudrate; - if (isOpen_) + if (is_open_) reconfigurePort (); } @@ -515,7 +543,7 @@ void Serial::SerialImpl::setBytesize (serial::bytesize_t bytesize) { bytesize_ = bytesize; - if (isOpen_) + if (is_open_) reconfigurePort (); } @@ -529,7 +557,7 @@ void Serial::SerialImpl::setParity (serial::parity_t parity) { parity_ = parity; - if (isOpen_) + if (is_open_) reconfigurePort (); } @@ -543,7 +571,7 @@ void Serial::SerialImpl::setStopbits (serial::stopbits_t stopbits) { stopbits_ = stopbits; - if (isOpen_) + if (is_open_) reconfigurePort (); } @@ -557,7 +585,7 @@ void Serial::SerialImpl::setFlowcontrol (serial::flowcontrol_t flowcontrol) { flowcontrol_ = flowcontrol; - if (isOpen_) + if (is_open_) reconfigurePort (); } @@ -570,7 +598,7 @@ Serial::SerialImpl::getFlowcontrol () const void Serial::SerialImpl::flush () { - if (isOpen_ == false) + if (is_open_ == false) { throw PortNotOpenedException ("Serial::flush"); } @@ -580,7 +608,7 @@ Serial::SerialImpl::flush () void Serial::SerialImpl::flushInput () { - if (isOpen_ == false) + if (is_open_ == false) { throw PortNotOpenedException ("Serial::flushInput"); } @@ -590,7 +618,7 @@ Serial::SerialImpl::flushInput () void Serial::SerialImpl::flushOutput () { - if (isOpen_ == false) + if (is_open_ == false) { throw PortNotOpenedException ("Serial::flushOutput"); } @@ -600,7 +628,7 @@ Serial::SerialImpl::flushOutput () void Serial::SerialImpl::sendBreak (int duration) { - if (isOpen_ == false) + if (is_open_ == false) { throw PortNotOpenedException ("Serial::sendBreak"); } @@ -610,7 +638,7 @@ Serial::SerialImpl::sendBreak (int duration) void Serial::SerialImpl::setBreak (bool level) { - if (isOpen_ == false) + if (is_open_ == false) { throw PortNotOpenedException ("Serial::setBreak"); } @@ -626,7 +654,7 @@ Serial::SerialImpl::setBreak (bool level) void Serial::SerialImpl::setRTS (bool level) { - if (isOpen_ == false) + if (is_open_ == false) { throw PortNotOpenedException ("Serial::setRTS"); } @@ -642,7 +670,7 @@ Serial::SerialImpl::setRTS (bool level) void Serial::SerialImpl::setDTR (bool level) { - if (isOpen_ == false) + if (is_open_ == false) { throw PortNotOpenedException ("Serial::setDTR"); } @@ -659,7 +687,7 @@ Serial::SerialImpl::setDTR (bool level) bool Serial::SerialImpl::getCTS () { - if (isOpen_ == false) + if (is_open_ == false) { throw PortNotOpenedException ("Serial::getCTS"); } @@ -670,7 +698,7 @@ Serial::SerialImpl::getCTS () bool Serial::SerialImpl::getDSR() { - if (isOpen_ == false) + if (is_open_ == false) { throw PortNotOpenedException ("Serial::getDSR"); } @@ -681,7 +709,7 @@ Serial::SerialImpl::getDSR() bool Serial::SerialImpl::getRI() { - if (isOpen_ == false) + if (is_open_ == false) { throw PortNotOpenedException ("Serial::getRI"); } @@ -692,7 +720,7 @@ Serial::SerialImpl::getRI() bool Serial::SerialImpl::getCD() { - if (isOpen_ == false) + if (is_open_ == false) { throw PortNotOpenedException ("Serial::getCD"); } diff --git a/src/serial.cc b/src/serial.cc index b058675..462e000 100644 --- a/src/serial.cc +++ b/src/serial.cc @@ -9,7 +9,6 @@ #endif using std::invalid_argument; -using std::memset; using std::min; using std::numeric_limits; using std::vector; @@ -86,98 +85,113 @@ Serial::available () return pimpl_->available (); } +size_t +Serial::read_ (unsigned char *buffer, size_t size) +{ + return this->pimpl_->read (buffer, size); +} + +size_t +Serial::read (unsigned char *buffer, size_t size) +{ + ScopedReadLock (this->pimpl_); + return this->pimpl_->read (buffer, size); +} + +size_t +Serial::read (std::vector &buffer, size_t size) +{ + ScopedReadLock (this->pimpl_); + unsigned char *buffer_ = new unsigned char[size]; + size_t bytes_read = this->pimpl_->read (buffer_, size); + buffer.insert (buffer.end (), buffer_, buffer_+bytes_read); + delete[] buffer_; + return bytes_read; +} + +size_t +Serial::read (std::string &buffer, size_t size) +{ + ScopedReadLock (this->pimpl_); + unsigned char *buffer_ = new unsigned char[size]; + size_t bytes_read = this->pimpl_->read (buffer_, size); + buffer.append (reinterpret_cast(buffer_), bytes_read); + delete[] buffer_; + return bytes_read; +} + string Serial::read (size_t size) { - ScopedReadLock(this->pimpl_); - if (read_cache_.size() >= size) - { - // Don't need to do a new read. - string result = read_cache_.substr (0, size); - read_cache_ = read_cache_.substr (size, read_cache_.size ()); - return result; - } - else - { - // Needs to read, loop until we have read enough or timeout - string result (read_cache_.substr (0, size)); - read_cache_.clear (); + std::string buffer; + this->read (buffer, size); + return buffer; +} - while (true) - { - char buf[256]; - size_t chars_read = pimpl_->read (buf, 256); - if (chars_read > 0) - { - read_cache_.append(buf, chars_read); - } - else - break; // Timeout occured - - if (chars_read > size) - { - result.append (read_cache_.substr (0, size)); - read_cache_ = read_cache_.substr (size, read_cache_.size ()); - break; - } - else - { - result.append (read_cache_.substr (0, size)); - read_cache_.clear (); - size -= chars_read; - } +size_t +Serial::readline (string &buffer, size_t size, string eol) +{ + ScopedReadLock (this->pimpl_); + size_t eol_len = eol.length(); + unsigned char buffer_[size]; + size_t read_so_far = 0; + while (true) + { + size_t bytes_read = this->read_ (buffer_+read_so_far, 1); + read_so_far += bytes_read; + if (bytes_read == 0) { + break; // Timeout occured on reading 1 byte + } + if (string(buffer_[read_so_far-eol_len], eol_len) == eol) { + break; // EOL found + } + if (read_so_far == size) { + break; // Reached the maximum read length } - return result; } + return read_so_far; } string Serial::readline (size_t size, string eol) { - size_t leneol = eol.length (); - string line = ""; - while (true) - { - string c = read (1); - if (!c.empty ()) - { - line.append (c); - if (line.length () > leneol && - line.substr (line.length () - leneol, leneol) == eol) - break; - if (line.length () >= size) - { - break; - } - } - else - // Timeout - break; - } - return line; + std::string buffer; + this->readline (buffer, size, eol); + return buffer; } vector -Serial::readlines(string eol) +Serial::readlines (size_t size, string eol) { - if (pimpl_->getTimeout () < 0) - { - throw invalid_argument ("Error, must be set for readlines"); - } - size_t leneol = eol.length (); - vector lines; - while (true) - { - string line = readline (numeric_limits::max (), eol); - if (!line.empty ()) - { - lines.push_back (line); - if (line.substr (line.length () - leneol, leneol) == eol) - break; + ScopedReadLock (this->pimpl_); + std::vector lines; + size_t eol_len = eol.length(); + unsigned char buffer_[size]; + size_t read_so_far = 0; + size_t start_of_line = 0; + while (read_so_far < size) { + size_t bytes_read = this->read_ (buffer_+read_so_far, 1); + read_so_far += bytes_read; + if (bytes_read == 0) { + if (start_of_line != read_so_far) { + lines.push_back( + std::string(buffer_[start_of_line], read_so_far-start_of_line)); + } + break; // Timeout occured on reading 1 byte + } + if (string(buffer_[read_so_far-eol_len], eol_len) == eol) { + // EOL found + lines.push_back( + std::string(buffer_[start_of_line], read_so_far-start_of_line)); + start_of_line = read_so_far; + } + if (read_so_far == size) { + if (start_of_line != read_so_far) { + lines.push_back( + std::string(buffer_[start_of_line], read_so_far-start_of_line)); + } + break; // Reached the maximum read length } - else - // Timeout - break; } return lines; }