1
0
mirror of https://github.com/wjwwood/serial.git synced 2026-01-22 19:54:57 +08:00

Got some serial listener tests working

This commit is contained in:
William Woodall 2012-01-24 12:20:46 -06:00
parent 6983d1145f
commit 20f552bc80
6 changed files with 87 additions and 284 deletions

View File

@ -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())

View File

@ -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 ();
}
};

View File

@ -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<TokenPtr> new_tokens);
// Given a filter_id and a list of tokens, return list of matched tokens
void filter (FilterPtr filter, std::vector<TokenPtr> &tokens);
// Runs the new_tokens through all the filters
void filter (std::vector<TokenPtr> &tokens);
// Function that loops while listening is true
void listen ();
// Target of callback thread

View File

@ -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<int> (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<ssize_t> (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<int> (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<size_t> (bytes_written);
return static_cast<size_t> (n);
}
void

View File

@ -50,8 +50,6 @@ SerialListener::callback() {
std::pair<FilterPtr,TokenPtr> 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<TokenPtr> new_tokens) {
// Iterate through the filters, checking each against new tokens
SerialListener::filter(std::vector<TokenPtr> &tokens) {
// Lock the filters while filtering
boost::mutex::scoped_lock lock(filter_mux);
std::vector<FilterPtr>::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<TokenPtr> &tokens)
{
// Iterate through the token uuids and run each against the filter
// Iterate through each new token and filter them
std::vector<TokenPtr>::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<FilterPtr>::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<TokenPtr> 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)

View File

@ -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<cr>$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<cr>$1E=Roboteq<cr>" << 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<cr>$1E=Robo<cr>V=1334:1337<cr>T=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