Commit 05fa4b8d authored by William Woodall's avatar William Woodall
Browse files

Removing serial listener, next make sure I can compile without boost completely.

parent f7cee5e1
find_path(serial_INCLUDE_DIRS serial.h serial_listener.h /usr/include/serial
find_path(serial_INCLUDE_DIRS serial.h /usr/include/serial
/usr/local/include/serial "$ENV{NAMER_ROOT}")
find_library(serial_LIBRARIES serial /usr/lib /usr/local/lib
......
# # ash_gti's dumb downed makefile so I can more easily test stuff
# CXX=clang++
# CXXFLAGS=-g -I./include -ferror-limit=5 -O3 -Wall -Weffc++ -pedantic -pedantic-errors -Wextra -Wall -Waggregate-return -Wcast-align -Wcast-qual -Wchar-subscripts -Wcomment -Wconversion -Wdisabled-optimization -Wfloat-equal -Wformat -Wformat=2 -Wformat-nonliteral -Wformat-security -Wformat-y2k -Wimplicit -Wimport -Winit-self -Winline -Winvalid-pch -Wlong-long -Wmissing-braces -Wmissing-field-initializers -Wmissing-format-attribute -Wmissing-include-dirs -Wmissing-noreturn -Wpacked -Wparentheses -Wpointer-arith -Wredundant-decls -Wreturn-type -Wsequence-point -Wshadow -Wsign-compare -Wstack-protector -Wstrict-aliasing -Wstrict-aliasing=2 -Wswitch -Wswitch-default -Wswitch-enum -Wtrigraphs -Wuninitialized -Wunknown-pragmas -Wunreachable-code -Wunused -Wunused-function -Wunused-label -Wunused-parameter -Wunused-value -Wunused-variable -Wvariadic-macros -Wvolatile-register-var -Wwrite-strings
#
# test: tests/serial_tests.o src/serial.o src/impl/unix.o
# $(CXX) -o test tests/serial_tests.o src/serial.o src/impl/unix.o
ifdef ROS_ROOT
include $(shell rospack find mk)/cmake.mk
else
......
#include <iostream>
#include <serial/serial.h>
#include <serial/serial_listener.h>
using namespace serial;
void default_handler(std::string token) {
std::cout << "default_handler got a: " << token << std::endl;
}
void callback(std::string token) {
std::cout << "callback got a: " << token << std::endl;
}
int run() {
// Assuming this device prints the string 'pre-substr-post\r' at 100Hz
Serial serial("/dev/tty.usbserial-A900cfJA", 115200);
SerialListener listener;
listener.startListening(serial);
// Set the tokenizer
// This is the same as the default delimeter, so an explicit call to
// setTokenizer is not necessary if your data is \r delimited.
// You can create your own Tokenizer as well.
listener.setTokenizer(SerialListener::delimeter_tokenizer("\r"));
// Method #1:
// comparator, callback - async
FilterPtr f1 =
listener.createFilter(SerialListener::startsWith("pre"), callback);
SerialListener::sleep(15); // Sleep 15ms, to let the data come in
listener.removeFilter(f1); // Not scoped, must be removed explicity
// Method #2:
// comparator - blocking
{
BlockingFilterPtr f2 =
listener.createBlockingFilter(SerialListener::endsWith("post"));
for (size_t i = 0; i < 3; i++) {
std::string token = f2->wait(100); // Wait for 100 ms or a matched token
if (token != "")
std::cout << "Found something ending with 'post'" << std::endl;
else
std::cout << "Did not find something ending with 'post'" << std::endl;
}
}
// BlockingFilter is scoped and will remove itself, so no removeFilter
// required, but a call like `listener.removeFilter(BlockingFilter) will
// remove it from the filter list so wait will always timeout.
// Method #3:
// comparator, token buffer size - blocking
{
// Give it a comparator, then a buffer size of 10
BufferedFilterPtr f3 =
listener.createBufferedFilter(SerialListener::contains("substr"), 10);
SerialListener::sleep(75); // Sleep 75ms, should have about 7
std::cout << "Caught " << f3->count();
std::cout << " tokens containing 'substr'" << std::endl;
for(size_t i = 0; i < 20; ++i) {
std::string token = f3->wait(5); // Pull message from the buffer
if (token == "") // If an empty string is returned, a timeout occured
break;
}
f3->clear(); // Empties the buffer
if (f3->wait(0) == "") // Non-blocking wait
std::cout << "We won the race condition!" << std::endl;
else
std::cout << "We lost the race condition..." << std::endl;
// The buffer is circular, so the oldest matches will be dropped first
}
// BufferedFilter is scoped and will remove itself just like BlockingFilter.
// Method #4:
// callback - async
// Gets called if a token doesn't match a filter
listener.setDefaultHandler(default_handler);
SerialListener::sleep(25); // Sleep 25 ms, so some default callbacks occur
return 0;
}
int main(void) {
try {
return run();
} catch (std::exception &e) {
std::cerr << e.what() << std::endl;
return 1;
}
}
This diff is collapsed.
macro(build_serial)
## Project Setup
cmake_minimum_required(VERSION 2.4.6)
if(COMMAND cmake_policy)
cmake_policy(SET CMP0003 NEW)
endif(COMMAND cmake_policy)
project(Serial)
## Configurations
# Use clang if available
IF(EXISTS /usr/bin/clang)
set(CMAKE_CXX_COMPILER /usr/bin/clang++)
set(CMAKE_OSX_DEPLOYMENT_TARGET "")
# set(CMAKE_CXX_FLAGS "-ferror-limit=5 -std=c++0x -stdlib=libc++")
set(CMAKE_CXX_FLAGS "-ferror-limit=5 -Wall -Weffc++ -pedantic -pedantic-errors -Wextra -Wall -Waggregate-return -Wcast-align -Wcast-qual -Wchar-subscripts -Wcomment -Wconversion -Wdisabled-optimization -Wfloat-equal -Wformat -Wformat=2 -Wformat-nonliteral -Wformat-security -Wformat-y2k -Wimplicit -Wimport -Winit-self -Winline -Winvalid-pch -Wlong-long -Wmissing-braces -Wmissing-field-initializers -Wmissing-format-attribute -Wmissing-include-dirs -Wmissing-noreturn -Wpacked -Wparentheses -Wpointer-arith -Wredundant-decls -Wreturn-type -Wsequence-point -Wshadow -Wsign-compare -Wstack-protector -Wstrict-aliasing -Wstrict-aliasing=2 -Wswitch -Wswitch-default -Wswitch-enum -Wtrigraphs -Wuninitialized -Wunknown-pragmas -Wunreachable-code -Wunused -Wunused-function -Wunused-label -Wunused-parameter -Wunused-value -Wunused-variable -Wvariadic-macros -Wvolatile-register-var -Wwrite-strings")
set(CMAKE_BUILD_TYPE Debug)
ENDIF(EXISTS /usr/bin/clang)
option(SERIAL_BUILD_TESTS "Build all of the Serial tests." OFF)
option(SERIAL_BUILD_EXAMPLES "Build all of the Serial examples." OFF)
# Allow for building shared libs override
IF(NOT BUILD_SHARED_LIBS)
set(BUILD_SHARED_LIBS OFF)
ENDIF(NOT BUILD_SHARED_LIBS)
# Set the default path for built executables to the "bin" directory
IF(NOT DEFINED(EXECUTABLE_OUTPUT_PATH))
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
ENDIF(NOT DEFINED(EXECUTABLE_OUTPUT_PATH))
# set the default path for built libraries to the "lib" directory
IF(NOT DEFINED(LIBRARY_OUTPUT_PATH))
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
ENDIF(NOT DEFINED(LIBRARY_OUTPUT_PATH))
## Configure the build system
# Add the include folder to the include path
include_directories(${PROJECT_SOURCE_DIR}/include)
# Add default source files
set(SERIAL_SRCS src/serial.cc src/impl/unix.cc src/serial_listener.cc)
# Add default header files
set(SERIAL_HEADERS include/serial/serial.h include/serial/serial_listener.h)
IF(UNIX)
list(APPEND SERIAL_SRCS src/impl/unix.cc)
list(APPEND SERIAL_HEADERS include/serial/impl/unix.h)
ELSE(UNIX)
ENDIF(UNIX)
# Find Boost, if it hasn't already been found
IF(NOT Boost_FOUND OR NOT Boost_SYSTEM_FOUND OR NOT Boost_FILESYSTEM_FOUND OR NOT Boost_THREAD_FOUND)
find_package(Boost COMPONENTS system filesystem thread REQUIRED)
ENDIF(NOT Boost_FOUND OR NOT Boost_SYSTEM_FOUND OR NOT Boost_FILESYSTEM_FOUND OR NOT Boost_THREAD_FOUND)
link_directories(${Boost_LIBRARY_DIRS})
include_directories(${Boost_INCLUDE_DIRS})
set(SERIAL_LINK_LIBS ${Boost_SYSTEM_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY})
## Build the Serial Library
# Compile the Library
add_library(serial ${SERIAL_SRCS} ${SERIAL_HEADERS})
target_link_libraries(serial ${SERIAL_LINK_LIBS})
IF( WIN32 )
target_link_libraries(serial wsock32)
ENDIF( )
## Build Examples
# If asked to
IF(SERIAL_BUILD_EXAMPLES)
# Compile the Serial Test program
add_executable(serial_example examples/serial_example.cc)
# Link the Test program to the Serial library
target_link_libraries(serial_example serial)
# Compile the Serial Listener Test program
add_executable(serial_listener_example
examples/serial_listener_example.cc)
# Link the Test program to the Serial library
target_link_libraries(serial_listener_example serial)
ENDIF(SERIAL_BUILD_EXAMPLES)
## Build tests
# If asked to
IF(SERIAL_BUILD_TESTS)
# Find Google Test
enable_testing()
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
# Compile the Serial Listener Test program
add_executable(serial_listener_tests tests/serial_listener_tests.cc)
add_executable(serial_tests tests/serial_tests.cc)
# Link the Test program to the serial library
target_link_libraries(serial_listener_tests ${GTEST_BOTH_LIBRARIES}
serial)
target_link_libraries(serial_tests ${GTEST_BOTH_LIBRARIES}
serial)
# # See: http://code.google.com/p/googlemock/issues/detail?id=146
# add_definitions(-DGTEST_USE_OWN_TR1_TUPLE=1)
add_test(AllTestsIntest_serial serial_listener_tests)
add_test(AllTestsIntest_serial serial_tests)
ENDIF(SERIAL_BUILD_TESTS)
## Setup install and uninstall
# Unless asked not to...
IF(NOT SERIAL_DONT_CONFIGURE_INSTALL)
# Configure make install
IF(NOT CMAKE_INSTALL_PREFIX)
SET(CMAKE_INSTALL_PREFIX /usr/local)
ENDIF(NOT CMAKE_INSTALL_PREFIX)
## Project Setup
cmake_minimum_required(VERSION 2.4.6)
if(COMMAND cmake_policy)
cmake_policy(SET CMP0003 NEW)
endif(COMMAND cmake_policy)
project(Serial)
## Configurations
# Use clang if available
IF(EXISTS /usr/bin/clang)
set(CMAKE_CXX_COMPILER /usr/bin/clang++)
set(CMAKE_OSX_DEPLOYMENT_TARGET "")
set(SERIAL_BUILD_WARNINGS TRUE)
IF(SERIAL_BUILD_WARNINGS)
set(CMAKE_CXX_FLAGS "-ferror-limit=5 -Wall -Weffc++ -pedantic -pedantic-errors -Wextra -Wall -Waggregate-return -Wcast-align -Wcast-qual -Wchar-subscripts -Wcomment -Wconversion -Wdisabled-optimization -Wfloat-equal -Wformat -Wformat=2 -Wformat-nonliteral -Wformat-security -Wformat-y2k -Wimplicit -Wimport -Winit-self -Winline -Winvalid-pch -Wlong-long -Wmissing-braces -Wmissing-field-initializers -Wmissing-format-attribute -Wmissing-include-dirs -Wmissing-noreturn -Wpacked -Wparentheses -Wpointer-arith -Wredundant-decls -Wreturn-type -Wsequence-point -Wshadow -Wsign-compare -Wstack-protector -Wstrict-aliasing -Wstrict-aliasing=2 -Wswitch -Wswitch-default -Wswitch-enum -Wtrigraphs -Wuninitialized -Wunknown-pragmas -Wunreachable-code -Wunused -Wunused-function -Wunused-label -Wunused-parameter -Wunused-value -Wunused-variable -Wvariadic-macros -Wvolatile-register-var -Wwrite-strings")
ELSE(SERIAL_BUILD_WARNINGS)
set(CMAKE_CXX_FLAGS "-ferror-limit=5")
ENDIF(SERIAL_BUILD_WARNINGS)
set(CMAKE_BUILD_TYPE Debug)
ENDIF(EXISTS /usr/bin/clang)
option(SERIAL_BUILD_TESTS "Build all of the Serial tests." OFF)
option(SERIAL_BUILD_EXAMPLES "Build all of the Serial examples." OFF)
# Allow for building shared libs override
IF(NOT BUILD_SHARED_LIBS)
set(BUILD_SHARED_LIBS OFF)
ENDIF(NOT BUILD_SHARED_LIBS)
# Set the default path for built executables to the "bin" directory
IF(NOT DEFINED(EXECUTABLE_OUTPUT_PATH))
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
ENDIF(NOT DEFINED(EXECUTABLE_OUTPUT_PATH))
# set the default path for built libraries to the "lib" directory
IF(NOT DEFINED(LIBRARY_OUTPUT_PATH))
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
ENDIF(NOT DEFINED(LIBRARY_OUTPUT_PATH))
## Configure the build system
# Add the include folder to the include path
include_directories(${PROJECT_SOURCE_DIR}/include)
# Add default source files
set(SERIAL_SRCS src/serial.cc)
IF(WIN32)
list(APPEND SERIAL_SRCS src/impl/windows.cc)
ELSE(WIN32)
list(APPEND SERIAL_SRCS src/impl/unix.cc)
ENDIF(WIN32)
# Add default header files
set(SERIAL_HEADERS include/serial/serial.h)
## Build the Serial Library
# Compile the Library
add_library(serial ${SERIAL_SRCS})
## Build Examples
# If asked to
IF(SERIAL_BUILD_EXAMPLES)
# Compile the Serial Test program
add_executable(serial_example examples/serial_example.cc)
# Link the Test program to the Serial library
target_link_libraries(serial_example serial)
ENDIF(SERIAL_BUILD_EXAMPLES)
## Build tests
# If asked to
IF(SERIAL_BUILD_TESTS)
# Find Google Test
enable_testing()
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
# Compile the Serial Test program
add_executable(serial_tests tests/serial_tests.cc)
# Link the Test program to the serial library
target_link_libraries(serial_tests ${GTEST_BOTH_LIBRARIES}
serial)
add_test(AllTestsIntest_serial serial_tests)
ENDIF(SERIAL_BUILD_TESTS)
## Setup install and uninstall
# Unless asked not to...
IF(NOT SERIAL_DONT_CONFIGURE_INSTALL)
# Configure make install
IF(NOT CMAKE_INSTALL_PREFIX)
SET(CMAKE_INSTALL_PREFIX /usr/local)
ENDIF(NOT CMAKE_INSTALL_PREFIX)
INSTALL(TARGETS serial
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
INSTALL(TARGETS serial
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
INSTALL(FILES include/serial/serial.h
include/serial/serial_listener.h
DESTINATION include/serial)
INSTALL(FILES include/serial/serial.h
DESTINATION include/serial)
IF(NOT CMAKE_FIND_INSTALL_PATH)
set(CMAKE_FIND_INSTALL_PATH ${CMAKE_ROOT})
ENDIF(NOT CMAKE_FIND_INSTALL_PATH)
IF(NOT CMAKE_FIND_INSTALL_PATH)
set(CMAKE_FIND_INSTALL_PATH ${CMAKE_ROOT})
ENDIF(NOT CMAKE_FIND_INSTALL_PATH)
INSTALL(FILES Findserial.cmake DESTINATION ${CMAKE_FIND_INSTALL_PATH}/Modules/)
INSTALL(FILES Findserial.cmake
DESTINATION ${CMAKE_FIND_INSTALL_PATH}/Modules/)
ADD_CUSTOM_TARGET(uninstall @echo uninstall package)
ADD_CUSTOM_TARGET(uninstall @echo uninstall package)
IF (UNIX)
ADD_CUSTOM_COMMAND(
COMMENT "uninstall package"
COMMAND xargs ARGS rm < install_manifest.txt
IF (UNIX)
ADD_CUSTOM_COMMAND(
COMMENT "uninstall package"
COMMAND xargs ARGS rm < install_manifest.txt
TARGET uninstall
)
ELSE(UNIX)
ADD_CUSTOM_COMMAND(
COMMENT "uninstall only implemented in unix"
TARGET uninstall
)
ENDIF(UNIX)
ENDIF(NOT SERIAL_DONT_CONFIGURE_INSTALL)
TARGET uninstall
)
ELSE(UNIX)
ADD_CUSTOM_COMMAND(
COMMENT "uninstall only implemented in unix"
TARGET uninstall
)
ENDIF(UNIX)
ENDIF(NOT SERIAL_DONT_CONFIGURE_INSTALL)
endmacro(build_serial)
......@@ -37,4 +37,4 @@ ifneq ($(MAKE),)
else
cd build && make
endif
cd bin && ./serial_listener_tests
\ No newline at end of file
cd bin && ./serial_tests
\ No newline at end of file
macro(build_serial)
cmake_minimum_required(VERSION 2.4.6)
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
# Set the build type. Options are:
# Coverage : w/ debug symbols, w/o optimization, w/ code-coverage
# Debug : w/ debug symbols, w/o optimization
# Release : w/o debug symbols, w/ optimization
# RelWithDebInfo : w/ debug symbols, w/ optimization
# MinSizeRel : w/o debug symbols, w/ optimization, stripped binaries
set(ROS_BUILD_TYPE RelWithDebInfo)
rosbuild_init()
#set the default path for built executables to the "bin" directory
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#set the default path for built libraries to the "lib" directory
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
include_directories(include)
set(SERIAL_SRCS src/serial.cc)
if(UNIX)
list(APPEND SERIAL_SRCS src/impl/unix.cc)
else(UNIX)
list(APPEND SERIAL_SRCS src/impl/windows.cc)
endif(UNIX)
list(APPEND SERIAL_SRCS src/serial_listener.cc)
# Build the serial library
rosbuild_add_library(${PROJECT_NAME} ${SERIAL_SRCS})
# Add boost dependencies
rosbuild_add_boost_directories()
rosbuild_link_boost(${PROJECT_NAME} system filesystem thread)
# Build example
rosbuild_add_executable(serial_example examples/serial_example.cc)
target_link_libraries(serial_example ${PROJECT_NAME})
rosbuild_add_executable(serial_listener_example
examples/serial_listener_example.cc)
target_link_libraries(serial_listener_example ${PROJECT_NAME})
# Create unit tests
rosbuild_add_gtest(serial_tests tests/serial_tests.cc)
target_link_libraries(serial_tests ${PROJECT_NAME})
rosbuild_add_gtest(serial_listener_tests tests/serial_listener_tests.cc)
target_link_libraries(serial_listener_tests ${PROJECT_NAME})
cmake_minimum_required(VERSION 2.4.6)
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
# Set the build type. Options are:
# Coverage : w/ debug symbols, w/o optimization, w/ code-coverage
# Debug : w/ debug symbols, w/o optimization
# Release : w/o debug symbols, w/ optimization
# RelWithDebInfo : w/ debug symbols, w/ optimization
# MinSizeRel : w/o debug symbols, w/ optimization, stripped binaries
set(ROS_BUILD_TYPE RelWithDebInfo)
rosbuild_init()
#set the default path for built executables to the "bin" directory
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#set the default path for built libraries to the "lib" directory
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
include_directories(include)
set(SERIAL_SRCS src/serial.cc)
if(UNIX)
list(APPEND SERIAL_SRCS src/impl/unix.cc)
else(UNIX)
list(APPEND SERIAL_SRCS src/impl/winows.cc)
endif(UNIX)
# Build the serial library
rosbuild_add_library(${PROJECT_NAME} ${SERIAL_SRCS})
# Build example
rosbuild_add_executable(serial_example examples/serial_example.cc)
target_link_libraries(serial_example ${PROJECT_NAME})
# Create unit tests
rosbuild_add_gtest(serial_tests tests/serial_tests.cc)
target_link_libraries(serial_tests ${PROJECT_NAME})
endmacro(build_serial)
/* Copyright 2012 William Woodall and John Harrison */
#include "serial/serial_listener.h"
/***** Inline Functions *****/
inline void defaultExceptionCallback(const std::exception &error) {
std::cerr << "SerialListener Unhandled Exception: " << error.what();
std::cerr << std::endl;
}
inline bool defaultComparator(const std::string &token) {
return token == token;
}
using namespace serial;
/***** Listener Class Functions *****/
void
SerialListener::default_handler(const std::string &token) {
if (this->_default_handler)
this->_default_handler(token);
}
SerialListener::SerialListener() : listening(false), chunk_size_(5) {
// Set default callbacks
this->handle_exc = defaultExceptionCallback;
// Default handler stuff
this->_default_handler = NULL;
this->default_comparator = defaultComparator;
DataCallback tmp = boost::bind(&SerialListener::default_handler, this, _1);
this->default_filter = FilterPtr(new Filter(default_comparator, tmp));
// Set default tokenizer
this->setTokenizer(delimeter_tokenizer("\r"));
}
SerialListener::~SerialListener() {
if (this->listening) {
this->stopListening();
}
}
void
SerialListener::callback() {
try {
// <filter id, token>
std::pair<FilterPtr,TokenPtr> pair;
while (this->listening) {
if (this->callback_queue.timed_wait_and_pop(pair, 10)) {
if (this->listening) {
try {
if (pair.first != NULL && pair.second != NULL) {
pair.first->callback_((*pair.second));
}
} catch (std::exception &e) {
this->handle_exc(e);
}// try callback
} // if listening
} // if popped
} // while (this->listening)
} catch (std::exception &e) {
this->handle_exc(SerialListenerException(e.what()));
}
}
void
SerialListener::startListening(Serial &serial_port) {
if (this->listening) {
throw(SerialListenerException("Already listening."));
return;
}
this->listening = true;
this->serial_port_ = &serial_port;
if (!this->serial_port_->isOpen()) {
throw(SerialListenerException("Serial port not open."));
return;
}
listen_thread = boost::thread(boost::bind(&SerialListener::listen, this));
// Start the callback thread
callback_thread =
boost::thread(boost::bind(&SerialListener::callback, this));
}
void
SerialListener::stopListening() {
// Stop listening and clear buffers
listening = false;
listen_thread.join();
callback_thread.join();
this->data_buffer = "";
this->serial_port_ = NULL;
}
size_t
SerialListener::determineAmountToRead() {
// TODO: Make a more intelligent method based on the length of the things
// filters are looking for. e.g.: if the filter is looking for 'V=XX\r'
// make the read amount at least 5.
return this->chunk_size_;
}
void
SerialListener::filter(std::vector<TokenPtr> &tokens) {
// Lock the filters while filtering
boost::mutex::scoped_lock lock(filter_mux);
// Iterate through each new token and filter them
std::vector<TokenPtr>::iterator it;
for (it=tokens.begin(); it!=tokens.end(); it++) {
TokenPtr token = (*it);
// If it is empty then pass it
if (token->empty()) {
continue;
}
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
SerialListener::listen() {
try {
while (this->listening) {
// Read some data
std::string temp;
this->readSomeData(temp, determineAmountToRead());
// If nothing was read then we
// don't need to iterate through the filters
if (temp.length() != 0) {
// Add the new data to the buffer
this->data_buffer += temp;
// 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->filter(new_tokens);
}
// Done parsing lines and buffer should now be set to the left overs
} // while (this->listening)
} catch (std::exception &e) {
this->handle_exc(SerialListenerException(e.what()));
}
}
/***** Filter Functions *****/
FilterPtr
SerialListener::createFilter(ComparatorType comparator, DataCallback callback)
{
FilterPtr filter_ptr(new Filter(comparator, callback));
boost::mutex::scoped_lock l(filter_mux);
this->filters.push_back(filter_ptr);
return filter_ptr;
}
BlockingFilterPtr
SerialListener::createBlockingFilter(ComparatorType comparator) {
return BlockingFilterPtr(
new BlockingFilter(comparator, (*this)));
}
BufferedFilterPtr
SerialListener::createBufferedFilter(ComparatorType comparator,
size_t buffer_size)
{
return BufferedFilterPtr(
new BufferedFilter(comparator, buffer_size, (*this)));
}
void
SerialListener::removeFilter(FilterPtr filter_ptr) {
boost::mutex::scoped_lock l(filter_mux);
filters.erase(std::find(filters.begin(),filters.end(),filter_ptr));
}
void
SerialListener::removeFilter(BlockingFilterPtr blocking_filter) {
this->removeFilter(blocking_filter->filter_ptr);
}
void
SerialListener::removeFilter(BufferedFilterPtr buffered_filter) {
this->removeFilter(buffered_filter->filter_ptr);
}
void
SerialListener::removeAllFilters() {
boost::mutex::scoped_lock l(filter_mux);
filters.clear();
callback_queue.clear();
}
/* To run these tests you need to change the define below to the serial port
* with a loop back device attached.
*
* Alternatively you could use an Arduino:
*
* void setup()
* {
* Serial.begin(115200);
* }
*
* void loop()
* {
* while (Serial.available() > 0) {
* Serial.write(Serial.read());
* }
* }
*
*/
// #define SERIAL_PORT_NAME "/dev/tty.usbserial-A900cfJA"
#define SERIAL_PORT_NAME "p0"
#include "gtest/gtest.h"
#include <boost/bind.hpp>
// OMG this is so nasty...
#define private public
#define protected public
#include "serial/serial_listener.h"
using namespace serial;
static size_t global_count, global_listen_count;
static bool matched;
void filter_handler(std::string token) {
global_listen_count++;
std::cout << "filter_handler got: " << token << std::endl;
}
void default_handler(std::string line) {
global_count++;
std::cout << "default_handler got: " << line << std::endl;
}
namespace {
void my_sleep(long milliseconds) {
boost::this_thread::sleep(boost::posix_time::milliseconds(milliseconds));
}
class SerialListenerTests : public ::testing::Test {
protected:
virtual void SetUp() {
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));
}
virtual void TearDown() {
listener.stopListening();
delete port1;
delete port2;
}
SerialListener listener;
Serial * port1;
Serial * port2;
};
TEST_F(SerialListenerTests, handlesPartialMessage) {
global_count = 0;
std::string input_str = "?$1E\r$1E=Robo";
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, normalFilterWorks) {
global_count = 0;
global_listen_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);
}
void run_blocking_filter(BlockingFilterPtr filt_1) {
// Wait 100 ms for a match
std::string temp = filt_1->wait(100);
if (temp.empty()) {
return;
}
std::cout << "blocking filter matched: " << temp << std::endl;
global_listen_count++;
matched = true;
}
TEST_F(SerialListenerTests, blockingFilterWorks) {
global_count = 0;
global_listen_count = 0;
matched = false;
std::string input_str = "?$1E\r$1E=Robo\rV=1334:1337\rT=123";
// Setup blocking filter
BlockingFilterPtr filt_1 =
listener.createBlockingFilter(SerialListener::startsWith("$1E="));
boost::thread t(boost::bind(run_blocking_filter, filt_1));
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);
using boost::posix_time::milliseconds;
ASSERT_TRUE(t.timed_join(milliseconds(10)));
ASSERT_EQ(2, global_count);
ASSERT_EQ(1, global_listen_count);
ASSERT_TRUE(matched);
}
TEST_F(SerialListenerTests, blockingFilterTimesOut) {
global_count = 0;
global_listen_count = 0;
matched = false;
std::string input_str = "?$1E\r$1E=Robo\rV=1334:1337\rT=123";
// Setup blocking filter
BlockingFilterPtr filt_1 =
listener.createBlockingFilter(SerialListener::startsWith("T="));
boost::thread t(boost::bind(run_blocking_filter, filt_1));
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);
using boost::posix_time::milliseconds;
// First one should not be within timeout, should be false
ASSERT_FALSE(t.timed_join(milliseconds(10)));
// Second one should capture timeout and return true to join
ASSERT_TRUE(t.timed_join(milliseconds(60)));
ASSERT_EQ(3, global_count);
ASSERT_EQ(0, global_listen_count);
ASSERT_FALSE(matched);
}
void write_later(Serial *port, std::string input_str, long wait_for) {
my_sleep(wait_for);
port->write(input_str);
}
TEST_F(SerialListenerTests, bufferedFilterWorks) {
global_count = 0;
std::string input_str = "?$1E\r+\r$1E=Robo\rV=1334:1337\rT=123";
// Setup buffered filter, buffer size 3
BufferedFilterPtr filt_1 =
listener.createBufferedFilter(SerialListener::exactly("+"), 3);
// Write the string to the port 10 ms in the future
boost::thread t(boost::bind(write_later, port2, input_str, 10));
// This should be empty because of a timeout
ASSERT_TRUE(filt_1->wait(2).empty());
// Make sure wait works properly
ASSERT_EQ("+", filt_1->wait(20));
// This should be empty cause there was only one
ASSERT_TRUE(filt_1->wait(2).empty());
// The queue in the filter should be empty
ASSERT_EQ(0, filt_1->queue.size());
ASSERT_EQ(3, global_count);
t.join();
}
TEST_F(SerialListenerTests, bufferedFilterQueueWorks) {
global_count = 0;
std::string input_str = "?$1E$\r+\r$1E=Robo$\rV=1334:1337$\rT=123$\r";
// Setup buffered filter, buffer size 3
BufferedFilterPtr filt_1 =
listener.createBufferedFilter(SerialListener::endsWith("$"), 3);
// write the string
port2->write(input_str);
my_sleep(20); // Let things process
// There should have been four matches
// therefore the first one should the second match.
ASSERT_EQ("$1E=Robo$", filt_1->wait(1));
ASSERT_EQ("V=1334:1337$", filt_1->wait(1));
ASSERT_EQ("T=123$", filt_1->wait(1));
ASSERT_EQ(0, filt_1->queue.size());
ASSERT_EQ(1, global_count);
}
} // namespace
int main(int argc, char **argv) {
try {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
} catch (std::exception &e) {
std::cerr << "Unhandled Exception: " << e.what() << std::endl;
}
return 1;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment