From 20f552bc8049be46025c402d1d7f9fddda64d344 Mon Sep 17 00:00:00 2001 From: William Woodall Date: Tue, 24 Jan 2012 12:20:46 -0600 Subject: [PATCH] Got some serial listener tests working --- examples/serial_example.cc | 2 +- include/serial/serial.h | 8 +- include/serial/serial_listener.h | 6 +- src/impl/unix.cc | 179 +++---------------------------- src/serial_listener.cc | 50 ++++----- tests/serial_listener_tests.cc | 126 +++++++--------------- 6 files changed, 87 insertions(+), 284 deletions(-) diff --git a/examples/serial_example.cc b/examples/serial_example.cc index 27857ff..4499a61 100644 --- a/examples/serial_example.cc +++ b/examples/serial_example.cc @@ -18,7 +18,7 @@ int run(int argc, char **argv) sscanf(argv[2], "%lu", &baud); // port, baudrate, timeout in milliseconds - serial::Serial serial(port, baud, 250); + serial::Serial serial(port, baud, 1000); std::cout << "Is the serial port open?"; if(serial.isOpen()) diff --git a/include/serial/serial.h b/include/serial/serial.h index 3b4c20d..c05dad6 100644 --- a/include/serial/serial.h +++ b/include/serial/serial.h @@ -102,8 +102,10 @@ class IOException : public std::exception const char* e_what_; int errno_; public: - explicit IOException (int errnum) : e_what_ (strerror (errnum)), errno_(errnum) {} - explicit IOException (const char * description) : e_what_ (description), errno_(0) {} + explicit IOException (int errnum) + : e_what_ (strerror (errnum)), errno_(errnum) {} + explicit IOException (const char * description) + : e_what_ (description), errno_(0) {} int getErrorNumber () { return errno_; } @@ -113,7 +115,7 @@ public: if (errno_ == 0) ss << "IO Exception " << e_what_ << " failed."; else - ss << "IO Exception " << errno_ << ":" << e_what_ << " failed."; + ss << "IO Exception (" << errno_ << "): " << e_what_ << " failed."; return ss.str ().c_str (); } }; diff --git a/include/serial/serial_listener.h b/include/serial/serial_listener.h index da9213a..35c93fc 100644 --- a/include/serial/serial_listener.h +++ b/include/serial/serial_listener.h @@ -663,10 +663,8 @@ private: // Gets some data from the serial port void readSomeData (std::string&, size_t); - // Runs the new tokens through the filters - void filterNewTokens (std::vector new_tokens); - // Given a filter_id and a list of tokens, return list of matched tokens - void filter (FilterPtr filter, std::vector &tokens); + // Runs the new_tokens through all the filters + void filter (std::vector &tokens); // Function that loops while listening is true void listen (); // Target of callback thread diff --git a/src/impl/unix.cc b/src/impl/unix.cc index 394ad05..24ad257 100644 --- a/src/impl/unix.cc +++ b/src/impl/unix.cc @@ -31,7 +31,6 @@ using serial::SerialExecption; using serial::PortNotOpenedException; using serial::IOException; - Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate, long timeout, bytesize_t bytesize, parity_t parity, stopbits_t stopbits, @@ -115,131 +114,8 @@ Serial::SerialImpl::reconfigurePort () #endif // setup baud rate - bool custom_baud = false; - speed_t baud; - switch (baudrate_) - { -#ifdef B0 - case 0: baud = B0; break; -#endif -#ifdef B50 - case 50: baud = B50; break; -#endif -#ifdef B75 - case 75: baud = B75; break; -#endif -#ifdef B110 - case 110: baud = B110; break; -#endif -#ifdef B134 - case 134: baud = B134; break; -#endif -#ifdef B150 - case 150: baud = B150; break; -#endif -#ifdef B200 - case 200: baud = B200; break; -#endif -#ifdef B300 - case 300: baud = B300; break; -#endif -#ifdef B600 - case 600: baud = B600; break; -#endif -#ifdef B1200 - case 1200: baud = B1200; break; -#endif -#ifdef B1800 - case 1800: baud = B1800; break; -#endif -#ifdef B2400 - case 2400: baud = B2400; break; -#endif -#ifdef B4800 - case 4800: baud = B4800; break; -#endif -#ifdef B7200 - case 7200: baud = B7200; break; -#endif -#ifdef B9600 - case 9600: baud = B9600; break; -#endif -#ifdef B14400 - case 14400: baud = B14400; break; -#endif -#ifdef B19200 - case 19200: baud = B19200; break; -#endif -#ifdef B28800 - case 28800: baud = B28800; break; -#endif -#ifdef B57600 - case 57600: baud = B57600; break; -#endif -#ifdef B76800 - case 76800: baud = B76800; break; -#endif -#ifdef B38400 - case 38400: baud = B38400; break; -#endif -#ifdef B115200 - case 115200: baud = B115200; break; -#endif -#ifdef B128000 - case 128000: baud = B128000; break; -#endif -#ifdef B153600 - case 153600: baud = B153600; break; -#endif -#ifdef B230400 - case 230400: baud = B230400; break; -#endif -#ifdef B256000 - case 256000: baud = B256000; break; -#endif -#ifdef B460800 - case 460800: baud = B460800; break; -#endif -#ifdef B921600 - case 921600: baud = B921600; break; -#endif - default: - custom_baud = true; -// Mac OS X 10.x Support -#if defined(__APPLE__) && defined(__MACH__) -#define IOSSIOSPEED _IOW('T', 2, speed_t) - int new_baud = static_cast (baudrate_); - if (ioctl (fd_, IOSSIOSPEED, &new_baud, 1) < 0) - { - throw IOException (errno); - } -// Linux Support -#elif defined(__linux__) - struct serial_struct ser; - ioctl(fd_, TIOCGSERIAL, &ser); - // set custom divisor - ser.custom_divisor = ser.baud_base / baudrate; - // update flags - ser.flags &= ~ASYNC_SPD_MASK; - ser.flags |= ASYNC_SPD_CUST; - - if (ioctl(fd_, TIOCSSERIAL, buf) < 0) - { - throw IOException (errno); - } -#else - throw invalid_argument ("OS does not currently support custom bauds"); -#endif - } - if (custom_baud == false) - { -#ifdef _BSD_SOURCE - ::cfsetspeed(&options, baud); -#else - ::cfsetispeed(&options, baud); - ::cfsetospeed(&options, baud); -#endif - } + // TODO(ash_git): validate baud rate + cfsetspeed(&options, baudrate_); // setup char len options.c_cflag &= (unsigned long) ~CSIZE; @@ -389,6 +265,7 @@ Serial::SerialImpl::read (char* buf, size_t size) 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. + // printf("bytes_read: %lu\n", bytes_read); if (bytes_read < 1) { // Disconnected devices, at least on Linux, show the @@ -414,51 +291,25 @@ Serial::SerialImpl::write (const string &data) { throw PortNotOpenedException ("Serial::write"); } - - fd_set writefds; - ssize_t bytes_written = 0; - while (true) + + ssize_t n = ::write (fd_, data.c_str (), data.length ()); + + if (n != static_cast (data.length ())) { - if (timeout_ != -1) + throw IOException ("Write did not complete"); + } + else if (n == -1) + { + if (errno == EINTR) { - 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; + return write (data); } else { - break; + throw IOException (errno); } } - - return static_cast (bytes_written); + return static_cast (n); } void diff --git a/src/serial_listener.cc b/src/serial_listener.cc index 0f2073c..fc6c298 100644 --- a/src/serial_listener.cc +++ b/src/serial_listener.cc @@ -50,8 +50,6 @@ SerialListener::callback() { std::pair pair; while (this->listening) { if (this->callback_queue.timed_wait_and_pop(pair, 10)) { - std::cerr << "Got something off the callback queue: "; - std::cerr << (*pair.second) << std::endl; if (this->listening) { try { pair.first->callback_((*pair.second)); @@ -118,37 +116,32 @@ SerialListener::readSomeData(std::string &temp, size_t this_many) { this->handle_exc(SerialListenerException("Serial port not open.")); } temp = this->serial_port_->read(this_many); - // if (temp.length() > 0) { - std::cerr << "SerialListener read (" << temp.length() << "): "; - std::cerr << temp << std::endl; - // } } void -SerialListener::filterNewTokens (std::vector new_tokens) { - // Iterate through the filters, checking each against new tokens +SerialListener::filter(std::vector &tokens) { + // Lock the filters while filtering boost::mutex::scoped_lock lock(filter_mux); - std::vector::iterator it; - for (it=filters.begin(); it!=filters.end(); it++) { - this->filter((*it), new_tokens); - } // for (it=filters.begin(); it!=filters.end(); it++) - // Put the last token back in the data buffer - this->data_buffer = (*new_tokens.back()); -} - -void -SerialListener::filter (FilterPtr filter, std::vector &tokens) -{ - // Iterate through the token uuids and run each against the filter + // Iterate through each new token and filter them std::vector::iterator it; for (it=tokens.begin(); it!=tokens.end(); it++) { - // The last element goes back into the data_buffer, don't filter it - if (it == tokens.end()-1) - continue; TokenPtr token = (*it); - if (filter->comparator_((*token))) - callback_queue.push(std::make_pair(filter,token)); - } + bool matched = false; + // Iterate through each filter + std::vector::iterator itt; + for (itt=filters.begin(); itt!=filters.end(); itt++) { + FilterPtr filter = (*itt); + if (filter->comparator_((*token))) { + callback_queue.push(std::make_pair(filter,token)); + matched = true; + break; + } + } // for (itt=filters.begin(); itt!=filters.end(); itt++) + // If matched is false then send it to the default handler + if (!matched) { + callback_queue.push(std::make_pair(default_filter,token)); + } + } // for (it=tokens.begin(); it!=tokens.end(); it++) } void @@ -166,8 +159,11 @@ SerialListener::listen() { // Call the tokenizer on the updated buffer std::vector new_tokens; this->tokenize(this->data_buffer, new_tokens); + // Put the last token back in the data buffer + this->data_buffer = (*new_tokens.back()); + new_tokens.pop_back(); // Run the new tokens through existing filters - this->filterNewTokens(new_tokens); + this->filter(new_tokens); } // Done parsing lines and buffer should now be set to the left overs } // while (this->listening) diff --git a/tests/serial_listener_tests.cc b/tests/serial_listener_tests.cc index e6197ed..8581dc4 100644 --- a/tests/serial_listener_tests.cc +++ b/tests/serial_listener_tests.cc @@ -17,7 +17,8 @@ * */ -#define SERIAL_PORT_NAME "/dev/tty.usbserial" +// #define SERIAL_PORT_NAME "/dev/tty.usbserial-A900cfJA" +#define SERIAL_PORT_NAME "p0" #include "gtest/gtest.h" @@ -32,6 +33,12 @@ using namespace serial; static size_t global_count, global_listen_count; +void filter_handler(std::string token) { + global_listen_count++; + std::cout << "filter_handler got: " << token << std::endl; + return true; +} + void default_handler(std::string line) { global_count++; std::cout << "default_handler got: " << line << std::endl; @@ -39,7 +46,6 @@ void default_handler(std::string line) { namespace { - void my_sleep(long milliseconds) { boost::this_thread::sleep(boost::posix_time::milliseconds(milliseconds)); } @@ -47,10 +53,8 @@ void my_sleep(long milliseconds) { class SerialListenerTests : public ::testing::Test { protected: virtual void SetUp() { - port1 = new Serial(SERIAL_PORT_NAME, 115200, 250); - - // Need to wait a bit for the Arduino to come up - // my_sleep(1000); + port1 = new Serial("/dev/pty"SERIAL_PORT_NAME, 115200, 10); + port2 = new Serial("/dev/tty"SERIAL_PORT_NAME, 115200, 250); listener.setDefaultHandler(default_handler); listener.startListening((*port1)); @@ -58,12 +62,13 @@ protected: virtual void TearDown() { listener.stopListening(); - port1->close(); delete port1; + delete port2; } SerialListener listener; Serial * port1; + Serial * port2; }; @@ -71,88 +76,39 @@ TEST_F(SerialListenerTests, handlesPartialMessage) { global_count = 0; std::string input_str = "?$1E\r$1E=Robo"; - ASSERT_EQ(input_str.length(), port1->write(input_str)); - - // give some time for the callback thread to finish - my_sleep(2000); + std::cout << "writing: ?$1E$1E=Robo" << std::endl; + port2->write(input_str); + // Allow time for processing + my_sleep(50); ASSERT_EQ(1, global_count); + + input_str = "?$1E\r$1E=Roboteq\r"; + std::cout << "writing: ?$1E$1E=Roboteq" << std::endl; + port2->write(input_str); + // Allow time for processing + my_sleep(50); + + ASSERT_EQ(3, global_count); } -// TEST_F(SerialListenerTests, listenForOnceWorks) { -// global_count = 0; -// -// boost::thread t( -// boost::bind(&SerialListenerTests::execute_listenForStringOnce, this)); -// -// boost::this_thread::sleep(boost::posix_time::milliseconds(5)); -// -// simulate_loop("\r+\r?$1E\r$1E=Robo"); -// -// ASSERT_TRUE(t.timed_join(boost::posix_time::milliseconds(60))); -// -// // Make sure the filters are getting deleted -// ASSERT_EQ(listener.filters.size(), 0); -// -// // give some time for the callback thread to finish -// stopCallbackThread(); -// -// ASSERT_EQ(global_count, 1); -// } -// -// // lookForOnce should not find it, but timeout after 1000ms, so it should -// // still join. -// TEST_F(SerialListenerTests, listenForOnceTimesout) { -// global_count = 0; -// -// boost::thread t( -// boost::bind(&SerialListenerTests::execute_listenForStringOnce, this)); -// -// boost::this_thread::sleep(boost::posix_time::milliseconds(55)); -// -// simulate_loop("\r+\r?$1ENOTRIGHT\r$1E=Robo"); -// -// ASSERT_TRUE(t.timed_join(boost::posix_time::milliseconds(60))); -// -// // give some time for the callback thread to finish -// stopCallbackThread(); -// -// ASSERT_EQ(global_count, 2); -// } -// -// bool listenForComparator(std::string line) { -// // std::cout << "In listenForComparator(" << line << ")" << std::endl; -// if (line.substr(0,2) == "V=") { -// return true; -// } -// return false; -// } -// -// void listenForCallback(std::string line) { -// // std::cout << "In listenForCallback(" << line << ")" << std::endl; -// global_listen_count++; -// } -// -// TEST_F(SerialListenerTests, listenForWorks) { -// global_count = 0; -// global_listen_count = 0; -// -// FilterPtr filt_uuid = -// listener.listenFor(listenForComparator, listenForCallback); -// -// simulate_loop("\r+\rV=05:06\r?$1E\rV=06:05\r$1E=Robo"); -// -// // give some time for the callback thread to finish -// stopCallbackThread(); -// -// ASSERT_EQ(global_count, 2); -// ASSERT_EQ(global_listen_count, 2); -// -// listener.stopListeningFor(filt_uuid); -// -// ASSERT_EQ(listener.filters.size(), 0); -// -// } +TEST_F(SerialListenerTests, normalFilterWorks) { + global_count = 0; + std::string input_str = "?$1E\r$1E=Robo\rV=1334:1337\rT=123"; + + // Setup filter + FilterPtr filt_1 = + listener.createFilter(SerialListener::startsWith("V="), filter_handler); + + std::cout << "writing: ?$1E$1E=RoboV=1334:1337T=123"; + std::cout << std::endl; + port2->write(input_str); + // Allow time for processing + my_sleep(50); + + ASSERT_EQ(2, global_count); + ASSERT_EQ(1, global_listen_count); +} } // namespace