mirror of
https://github.com/wjwwood/serial.git
synced 2026-01-22 11:44:53 +08:00
Removing serial listener, next make sure I can compile without boost completely.
This commit is contained in:
parent
f7cee5e175
commit
05fa4b8d77
@ -1,4 +1,4 @@
|
||||
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
|
||||
|
||||
6
Makefile
6
Makefile
@ -1,9 +1,3 @@
|
||||
# # 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
|
||||
|
||||
@ -1,93 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
@ -1,931 +0,0 @@
|
||||
/*!
|
||||
* \file serial/serial_listener.h
|
||||
* \author William Woodall <wjwwood@gmail.com>
|
||||
* \version 0.1
|
||||
*
|
||||
* \section LICENSE
|
||||
*
|
||||
* The BSD License
|
||||
*
|
||||
* Copyright (c) 2011 William Woodall
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* \section DESCRIPTION
|
||||
*
|
||||
* This provides a class that allows for asynchronous serial port reading.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SERIAL_LISTENER_H
|
||||
#define SERIAL_LISTENER_H
|
||||
|
||||
#ifndef SERIAL_LISTENER_DEBUG
|
||||
#define SERIAL_LISTENER_DEBUG 0
|
||||
#endif
|
||||
|
||||
// STL
|
||||
#include <queue>
|
||||
#include <stdint.h>
|
||||
#include <iostream>
|
||||
|
||||
// Serial
|
||||
#include <serial/serial.h>
|
||||
|
||||
// Boost
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#if SERIAL_LISTENER_DEBUG
|
||||
# warning SerialListener in debug mode
|
||||
#endif
|
||||
|
||||
namespace serial {
|
||||
|
||||
/*!
|
||||
* This is an alias to boost::shared_ptr<const std::string> used for tokens.
|
||||
*
|
||||
* This is the type used internally and is the type returned in a vector by
|
||||
* the tokenizer. The shared_ptr allows for the token to be stored and kept
|
||||
* around long enough to be used by the comparators and callbacks, but no
|
||||
* longer. This internal storage is passed as a const std::string reference
|
||||
* to callbacks, like the DataCallback function type, to prevent implicit
|
||||
* copying.
|
||||
*
|
||||
* \see serial::TokenizerType, serial::SerialListener::setTokenizer
|
||||
*/
|
||||
typedef boost::shared_ptr<const std::string> TokenPtr;
|
||||
|
||||
/*!
|
||||
* This is a general function type that is used as the callback prototype
|
||||
* for asynchronous functions like the default handler callback and the
|
||||
* listenFor callbacks.
|
||||
*
|
||||
* The function takes a std::string reference and returns nothing, it is
|
||||
* simply passing the resulting line detected by the comparator to the user's
|
||||
* callback for processing.
|
||||
*
|
||||
* \see SerialListener::listenFor, SerialListener::setDefaultHandler
|
||||
*/
|
||||
typedef boost::function<void(const std::string&)> DataCallback;
|
||||
|
||||
/*!
|
||||
* This is a general function type that is used as the comparator callback
|
||||
* prototpe for the listenFor* type functions.
|
||||
*
|
||||
* The function takes a std::string reference and returns true if the string
|
||||
* matches what the comparator is looking for and false if it does not, unless
|
||||
* otherwise specified.
|
||||
*
|
||||
* \see SerialListener::listenFor, SerialListener::listenForOnce
|
||||
*/
|
||||
typedef boost::function<bool(const std::string&)> ComparatorType;
|
||||
|
||||
/*!
|
||||
* This function type describes the prototype for the tokenizer callback.
|
||||
*
|
||||
* The function should take a std::string reference and tokenize it into a
|
||||
* several TokenPtr's and store them in the given std::vector<TokenPtr>
|
||||
* reference. There are some default ones or the user can create their own.
|
||||
*
|
||||
* The last element in the std::vector of TokenPtr's should always be
|
||||
* either an empty string ("") or the last partial message. The last element
|
||||
* in the std::vector will be put back into the data buffer so that if it is
|
||||
* incomplete it can be completed when more data is read.
|
||||
*
|
||||
* Example: A delimeter tokenizer with a delimeter of "\r". The result would
|
||||
* be: "msg1\rmsg2\r" -> ["msg1", "msg2", ""] for two complete messages, or:
|
||||
* "msg1\rpartial_msg2" -> ["msg1","partial_msg2"] for one complete message
|
||||
* and one partial message.
|
||||
*
|
||||
* \see SerialListener::setTokenizer, serial::delimeter_tokenizer,
|
||||
* serial::TokenPtr
|
||||
*/
|
||||
typedef boost::function<void(const std::string&, std::vector<TokenPtr>&)>
|
||||
TokenizerType;
|
||||
|
||||
/*!
|
||||
* This function type describes the prototype for the exception callback.
|
||||
*
|
||||
* The function takes a std::exception reference and returns nothing. It is
|
||||
* called from the library when an exception occurs in a library thread.
|
||||
* This exposes these exceptions to the user so they can to error handling.
|
||||
*
|
||||
* \see SerialListener::setExceptionHandler
|
||||
*/
|
||||
typedef boost::function<void(const std::exception&)> ExceptionCallback;
|
||||
|
||||
/*!
|
||||
* Represents a filter which new data is passed through.
|
||||
*
|
||||
* The filter consists of a comparator and a callback. The comparator takes a
|
||||
* token and returns true if it matches, false if it doesn't. If a match
|
||||
* occurs the serial listener will dispatch a call of the callback with the
|
||||
* matched data in a another thread. The comparator should be as short as
|
||||
* possible, but the callback can be longer since it is executed in a thread
|
||||
* or thread pool.
|
||||
*
|
||||
* \param comparator A ComparatorType that matches incoming data, returns true
|
||||
* for a match, false othewise.
|
||||
*
|
||||
* \param callback A DataCallback that gets called when a match occurs.
|
||||
*
|
||||
* \see serial::ComparatorType, serial::DataCallback, serial::FilterPtr
|
||||
*/
|
||||
class Filter
|
||||
{
|
||||
public:
|
||||
Filter (ComparatorType comparator, DataCallback callback)
|
||||
: comparator_(comparator), callback_(callback) {}
|
||||
virtual ~Filter () {}
|
||||
|
||||
ComparatorType comparator_;
|
||||
DataCallback callback_;
|
||||
|
||||
private:
|
||||
// Disable copy constructors
|
||||
Filter(const Filter&);
|
||||
void operator=(const Filter&);
|
||||
const Filter& operator=(Filter);
|
||||
};
|
||||
|
||||
/*!
|
||||
* This is an alias to boost::shared_ptr<Filter> used for tokens.
|
||||
*
|
||||
* This is used internally and is returned from SerialListener::listenFor like
|
||||
* functions so that users can later remove those filters by passing the
|
||||
* FilterPtr.
|
||||
*
|
||||
* \see serial::Filter, serial::SerialListener::listenFor,
|
||||
* serial::SerialListener::listenForOnce
|
||||
*/
|
||||
typedef boost::shared_ptr<Filter> FilterPtr;
|
||||
|
||||
class BlockingFilter; // Forward declaration
|
||||
|
||||
/*!
|
||||
* Shared Pointer of BlockingFilter, returned by
|
||||
* SerialListener::createBlockingFilter.
|
||||
*
|
||||
* \see serial::BlockingFilter, SerialListener::createBlockingFilter
|
||||
*/
|
||||
typedef boost::shared_ptr<BlockingFilter> BlockingFilterPtr;
|
||||
|
||||
class BufferedFilter; // Forward declaration
|
||||
|
||||
/*!
|
||||
* Shared Pointer of BufferedFilter, returned by
|
||||
* SerialListener::createBufferedFilter.
|
||||
*
|
||||
* \see serial::BufferedFilter, SerialListener::createBufferedFilter
|
||||
*/
|
||||
typedef boost::shared_ptr<BufferedFilter> BufferedFilterPtr;
|
||||
|
||||
/*!
|
||||
* This is a general exception generated by the SerialListener class.
|
||||
*
|
||||
* Check the SerialListenerException::what function for the cause.
|
||||
*
|
||||
* \param e_what is a std::string that describes the cause of the error.
|
||||
*/
|
||||
class SerialListenerException : public std::exception {
|
||||
const std::string e_what_;
|
||||
public:
|
||||
SerialListenerException(const std::string e_what) : e_what_(e_what) {}
|
||||
~SerialListenerException() throw() {}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
std::stringstream ss;
|
||||
ss << "SerialListenerException: " << this->e_what_;
|
||||
return ss.str().c_str();
|
||||
}
|
||||
};
|
||||
|
||||
// Based on: http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html
|
||||
template<typename Data>
|
||||
class ConcurrentQueue
|
||||
{
|
||||
private:
|
||||
std::queue<Data> the_queue;
|
||||
mutable boost::mutex the_mutex;
|
||||
boost::condition_variable the_condition_variable;
|
||||
public:
|
||||
void push(Data const& data) {
|
||||
boost::mutex::scoped_lock lock(the_mutex);
|
||||
the_queue.push(data);
|
||||
lock.unlock();
|
||||
the_condition_variable.notify_one();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
boost::mutex::scoped_lock lock(the_mutex);
|
||||
return the_queue.empty();
|
||||
}
|
||||
|
||||
bool try_pop(Data& popped_value) {
|
||||
boost::mutex::scoped_lock lock(the_mutex);
|
||||
if(the_queue.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
popped_value=the_queue.front();
|
||||
the_queue.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool timed_wait_and_pop(Data& popped_value, long timeout) {
|
||||
using namespace boost::posix_time;
|
||||
bool result;
|
||||
boost::mutex::scoped_lock lock(the_mutex);
|
||||
result = !the_queue.empty();
|
||||
if (!result) {
|
||||
result = the_condition_variable.timed_wait(lock, milliseconds(timeout));
|
||||
}
|
||||
|
||||
if (result) {
|
||||
popped_value=the_queue.front();
|
||||
the_queue.pop();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void wait_and_pop(Data& popped_value) {
|
||||
boost::mutex::scoped_lock lock(the_mutex);
|
||||
while(the_queue.empty()) {
|
||||
the_condition_variable.wait(lock);
|
||||
}
|
||||
|
||||
popped_value=the_queue.front();
|
||||
the_queue.pop();
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return the_queue.size();
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
the_condition_variable.notify_one();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
boost::mutex::scoped_lock lock(the_mutex);
|
||||
while (!the_queue.empty()) {
|
||||
the_queue.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Listens to a serial port, facilitates asynchronous reading
|
||||
*/
|
||||
class SerialListener
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Creates a new Serial Listener.
|
||||
*/
|
||||
SerialListener ();
|
||||
|
||||
/*!
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~SerialListener ();
|
||||
|
||||
/***** Configurations ******/
|
||||
|
||||
/*!
|
||||
* Sets the tokenizer to be used when tokenizing the data into tokens.
|
||||
*
|
||||
* This function is given a std::string of data and is responsible for
|
||||
* tokenizing that data into a std::vector<TokenPtr> of data tokens.
|
||||
* The default tokenizer splits the data by the ascii return carriage.
|
||||
* The user can create their own tokenizer or use one of the default ones.
|
||||
*
|
||||
* \param tokenizer Function for tokenizing the incoming data.
|
||||
*
|
||||
* \see serial::TokenizerType, serial::delimeter_tokenizer
|
||||
*/
|
||||
void
|
||||
setTokenizer (TokenizerType tokenizer) {
|
||||
this->tokenize = tokenizer;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Sets the number of bytes to be read at a time by the listener.
|
||||
*
|
||||
* \param chunk_size Number of bytes to be read at a time.
|
||||
*/
|
||||
void
|
||||
setChunkSize (size_t chunk_size) {
|
||||
this->chunk_size_ = chunk_size;
|
||||
}
|
||||
|
||||
/***** Start and Stop Listening ******/
|
||||
|
||||
/*!
|
||||
* Starts a thread to listen for messages and process them through filters.
|
||||
*
|
||||
* \param serial_port Pointer to a serial::Serial object that is used to
|
||||
* retrieve new data.
|
||||
*/
|
||||
void
|
||||
startListening (serial::Serial &serial_port);
|
||||
|
||||
/*!
|
||||
* Stops the listening thread and blocks until it completely stops.
|
||||
*
|
||||
* This function also clears all of the active filters from listenFor and
|
||||
* similar functions.
|
||||
*/
|
||||
void
|
||||
stopListening ();
|
||||
|
||||
/***** Filter Functions ******/
|
||||
|
||||
/*!
|
||||
* Creates a filter that calls a callback when the comparator returns true.
|
||||
*
|
||||
* The user provides a comparator and a callback, and every time a line is
|
||||
* received the comparator is called and the comparator has to evaluate the
|
||||
* line and return true if it matches and false if it doesn't. If it does
|
||||
* match, the callback is called with the resulting line.
|
||||
*
|
||||
* \param comparator This is a comparator for detecting if a line matches.
|
||||
* The comparartor receives a std::string reference and must return a true
|
||||
* if it matches and false if it doesn't.
|
||||
*
|
||||
* \param callback This is the handler for when a match occurs. It is given
|
||||
* a std::string reference of the line that matched your comparator.
|
||||
*
|
||||
* \return boost::shared_ptr<Filter> so you can remove it later.
|
||||
*
|
||||
* \see SerialListener::removeFilter
|
||||
*/
|
||||
FilterPtr
|
||||
createFilter (ComparatorType comparator, DataCallback callback);
|
||||
|
||||
/*!
|
||||
* Creates a BlockingFilter which blocks until the comparator returns true.
|
||||
*
|
||||
* The user provides a comparator, and every time a line is
|
||||
* received the comparator is called and the comparator has to evaluate the
|
||||
* line and return true if it matches and false if it doesn't. If it does
|
||||
* match, any threads that have called BlockingFilter::wait will be
|
||||
* notified. The BlockingFilter will remove itself when its destructor is
|
||||
* called, i.e. when it leaves the scope, so in those cases an explicit call
|
||||
* to SerialListener::removeFilter is not needed.
|
||||
*
|
||||
* \param comparator This is a comparator for detecting if a line matches.
|
||||
* The comparartor receives a std::string reference and must return a true
|
||||
* if it matches and false if it doesn't.
|
||||
*
|
||||
* \return BlockingFilterPtr So you can call BlockingFilter::wait on it.
|
||||
*
|
||||
* \see SerialListener::removeFilter, serial::BlockingFilter,
|
||||
* serial::BlockingFilterPtr
|
||||
*/
|
||||
BlockingFilterPtr
|
||||
createBlockingFilter (ComparatorType comparator);
|
||||
|
||||
/*!
|
||||
* Creates a BlockingFilter blocks until the comparator returns true.
|
||||
*
|
||||
* The user provides a comparator, and every time a line is
|
||||
* received the comparator is called and the comparator has to evaluate the
|
||||
* line and return true if it matches and false if it doesn't. If it does
|
||||
* match, any threads that have called BlockingFilter::wait will be
|
||||
* notified. The BlockingFilter will remove itself when its destructor is
|
||||
* called, i.e. when it leaves the scope, so in those cases an explicit call
|
||||
* to SerialListener::removeFilter is not needed.
|
||||
*
|
||||
* \param comparator This is a comparator for detecting if a line matches.
|
||||
* The comparartor receives a std::string reference and must return a true
|
||||
* if it matches and false if it doesn't.
|
||||
*
|
||||
* \param buffer_size This is the number of tokens to be buffered by the
|
||||
* BufferedFilter, defaults to 1024.
|
||||
*
|
||||
* \return BlockingFilter So you can call BlockingFilter::wait on it.
|
||||
*
|
||||
* \see SerialListener::removeFilter, serial::BufferedFilter,
|
||||
* serial::BufferedFilterPtr
|
||||
*/
|
||||
BufferedFilterPtr
|
||||
createBufferedFilter (ComparatorType comparator, size_t buffer_size = 1024);
|
||||
|
||||
/*!
|
||||
* Removes a filter by a given FilterPtr.
|
||||
*
|
||||
* \param filter_ptr A shared pointer to the filter to be removed.
|
||||
*
|
||||
* \see SerialListener::createFilter
|
||||
*/
|
||||
void
|
||||
removeFilter (FilterPtr filter_ptr);
|
||||
|
||||
/*!
|
||||
* Removes a BlockingFilter.
|
||||
*
|
||||
* The BlockingFilter will remove itself if the destructor is called.
|
||||
*
|
||||
* \param blocking_filter A BlockingFilter to be removed.
|
||||
*
|
||||
* \see SerialListener::createBlockingFilter
|
||||
*/
|
||||
void
|
||||
removeFilter (BlockingFilterPtr blocking_filter);
|
||||
|
||||
/*!
|
||||
* Removes a BufferedFilter.
|
||||
*
|
||||
* The BufferedFilter will remove itself if the destructor is called.
|
||||
*
|
||||
* \param buffered_filter A BufferedFilter to be removed.
|
||||
*
|
||||
* \see SerialListener::createBufferedFilter
|
||||
*/
|
||||
void
|
||||
removeFilter (BufferedFilterPtr buffered_filter);
|
||||
|
||||
/*!
|
||||
* Removes all filters.
|
||||
*/
|
||||
void
|
||||
removeAllFilters ();
|
||||
|
||||
/***** Hooks and Handlers ******/
|
||||
|
||||
/*!
|
||||
* Sets the handler to be called when a lines is not caught by a filter.
|
||||
*
|
||||
* This allows you to set a catch all function that will get called
|
||||
* everytime a line is not matched by a filter and the ttl expires.
|
||||
*
|
||||
* Setting the callbacks works just like SerialListener::setInfoHandler.
|
||||
*
|
||||
* \param default_handler A function pointer to the callback to handle
|
||||
* unmatched and expired messages.
|
||||
*
|
||||
* \see serial::DataCallback, SerialListener::setInfoHandler
|
||||
*/
|
||||
void setDefaultHandler(DataCallback default_handler) {
|
||||
this->_default_handler = default_handler;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Sets the function to be called when an exception occurs internally.
|
||||
*
|
||||
* This allows you to hook into the exceptions that occur in threads inside
|
||||
* the serial listener library.
|
||||
*
|
||||
* \param exception_handler A function pointer to the callback to handle new
|
||||
* interal exceptions.
|
||||
*
|
||||
* \see serial::ExceptionCallback
|
||||
*/
|
||||
void
|
||||
setExceptionHandler (ExceptionCallback exception_handler) {
|
||||
this->handle_exc = exception_handler;
|
||||
}
|
||||
|
||||
/***** Static Functions ******/
|
||||
|
||||
/*!
|
||||
* Sleeps for a given number of milliseconds.
|
||||
*
|
||||
* \param milliseconds number of milliseconds to sleep.
|
||||
*/
|
||||
static void
|
||||
sleep (long milliseconds) {
|
||||
boost::int64_t ms(milliseconds);
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(ms));
|
||||
}
|
||||
|
||||
/*!
|
||||
* This returns a tokenizer that splits on a given delimeter.
|
||||
*
|
||||
* The delimeter is passed into the function and a TokenizerType is returned
|
||||
* that can be passed to SerialListener::setTokenizer.
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
* my_listener.setTokenizer(SerialListener::delimeter_tokenizer("\r"));
|
||||
* <\pre>
|
||||
*
|
||||
* \param delimeter A std::string that is used as a delimeter when
|
||||
* tokenizing data.
|
||||
*
|
||||
* \return TokenizerType A tokenizer function type that can be passed to
|
||||
* SerialListener::setTokenizer.
|
||||
*
|
||||
* \see SerialListener::setTokenizer, serial::TokenizerType
|
||||
*/
|
||||
static TokenizerType
|
||||
delimeter_tokenizer (std::string delimeter) {
|
||||
return boost::bind(&SerialListener::_delimeter_tokenizer,
|
||||
_1, _2, delimeter);
|
||||
}
|
||||
|
||||
/*!
|
||||
* This returns a comparator that matches only the exact string given.
|
||||
*
|
||||
* This can be used with listenFor or listenForOnce:
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
* my_listener.listenFor(SerialListener::exactly("my_string"),
|
||||
* my_callback);
|
||||
* <\pre>
|
||||
*
|
||||
* \param exact_str A std::string that is used as the exact string to match
|
||||
* when comparing tokens for matching.
|
||||
*
|
||||
* \return ComparatorType A comparator function type that can be passed to
|
||||
* SerialListener::listenFor or SerialListener::listenForOnce.
|
||||
*
|
||||
* \see SerialListener::listenFor, SerialListener::listenForOnce,
|
||||
* serial::ComparatorType
|
||||
*/
|
||||
static ComparatorType
|
||||
exactly (std::string exact_str) {
|
||||
return boost::bind(&SerialListener::_exactly, _1, exact_str);
|
||||
}
|
||||
|
||||
/*!
|
||||
* This returns a comparator that looks for a given prefix.
|
||||
*
|
||||
* This can be used with listenFor or listenForOnce:
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
* my_listener.listenFor(SerialListener::startsWith("V="), my_callback);
|
||||
* <\pre>
|
||||
*
|
||||
* \param prefix A std::string that is used as the prefix string to match
|
||||
* when comparing tokens for matching.
|
||||
*
|
||||
* \return ComparatorType A comparator function type that can be passed to
|
||||
* SerialListener::listenFor or SerialListener::listenForOnce.
|
||||
*
|
||||
* \see SerialListener::listenFor, SerialListener::listenForOnce,
|
||||
* serial::ComparatorType
|
||||
*/
|
||||
static ComparatorType
|
||||
startsWith (std::string prefix) {
|
||||
return boost::bind(&SerialListener::_startsWith, _1, prefix);
|
||||
}
|
||||
|
||||
/*!
|
||||
* This returns a comparator that looks for a given postfix.
|
||||
*
|
||||
* This can be used with listenFor or listenForOnce:
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
* my_listener.listenFor(SerialListener::endsWith(";"), my_callback);
|
||||
* <\pre>
|
||||
*
|
||||
* \param postfix A std::string that is used as the postfix string to match
|
||||
* when comparing tokens for matching.
|
||||
*
|
||||
* \return ComparatorType A comparator function type that can be passed to
|
||||
* SerialListener::listenFor or SerialListener::listenForOnce.
|
||||
*
|
||||
* \see SerialListener::listenFor, SerialListener::listenForOnce,
|
||||
* serial::ComparatorType
|
||||
*/
|
||||
static ComparatorType
|
||||
endsWith (std::string postfix) {
|
||||
return boost::bind(&SerialListener::_endsWith, _1, postfix);
|
||||
}
|
||||
|
||||
/*!
|
||||
* This returns a comparator that looks for a given substring in the token.
|
||||
*
|
||||
* This can be used with listenFor or listenForOnce:
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
* my_listener.listenFor(SerialListener::contains("some string"),
|
||||
* my_callback);
|
||||
* <\pre>
|
||||
*
|
||||
* \param substr A std::string that is used as the search substring to match
|
||||
* when comparing tokens for matching.
|
||||
*
|
||||
* \return ComparatorType A comparator function type that can be passed to
|
||||
* SerialListener::listenFor or SerialListener::listenForOnce.
|
||||
*
|
||||
* \see SerialListener::listenFor, SerialListener::listenForOnce,
|
||||
* serial::ComparatorType
|
||||
*/
|
||||
static ComparatorType
|
||||
contains (std::string substr) {
|
||||
return boost::bind(_contains, _1, substr);
|
||||
}
|
||||
|
||||
private:
|
||||
// Disable copy constructors
|
||||
SerialListener(const SerialListener&);
|
||||
void operator=(const SerialListener&);
|
||||
const SerialListener& operator=(SerialListener);
|
||||
// delimeter tokenizer function
|
||||
static void
|
||||
_delimeter_tokenizer (const std::string &data,
|
||||
std::vector<TokenPtr> &tokens,
|
||||
std::string delimeter)
|
||||
{
|
||||
typedef std::vector<std::string> find_vector_type;
|
||||
find_vector_type t;
|
||||
boost::split(t, data, boost::is_any_of(delimeter));
|
||||
for (find_vector_type::iterator it = t.begin(); it != t.end(); it++)
|
||||
tokens.push_back(TokenPtr( new std::string(*it) ));
|
||||
}
|
||||
// exact comparator function
|
||||
static bool
|
||||
_exactly (const std::string& token, std::string exact_str) {
|
||||
#if SERIAL_LISTENER_DEBUG
|
||||
std::cerr << "In exactly callback(" << token.length() << "): ";
|
||||
std::cerr << token << " == " << exact_str << ": ";
|
||||
if (token == exact_str)
|
||||
std::cerr << "True";
|
||||
else
|
||||
std::cerr << "False";
|
||||
std::cerr << std::endl;
|
||||
#endif
|
||||
return token == exact_str;
|
||||
}
|
||||
// startswith comparator function
|
||||
static bool
|
||||
_startsWith (const std::string& token, std::string prefix) {
|
||||
#if SERIAL_LISTENER_DEBUG
|
||||
std::cerr << "In startsWith callback(" << token.length() << "): ";
|
||||
std::cerr << token << " starts with " << prefix;
|
||||
std::cerr << "?: ";
|
||||
if (token.substr(0,prefix.length()) == prefix)
|
||||
std::cerr << "True";
|
||||
else
|
||||
std::cerr << "False";
|
||||
std::cerr << std::endl;
|
||||
#endif
|
||||
return token.substr(0,prefix.length()) == prefix;
|
||||
}
|
||||
// endswith comparator function
|
||||
static bool
|
||||
_endsWith (const std::string& token, std::string postfix) {
|
||||
#if SERIAL_LISTENER_DEBUG
|
||||
std::cerr << "In endsWith callback(";
|
||||
std::cerr << token.length();
|
||||
std::cerr << "): " << token;
|
||||
std::cerr << " ends with " << postfix << "?: ";
|
||||
if (token.substr(token.length()-postfix.length()) == postfix)
|
||||
std::cerr << "True";
|
||||
else
|
||||
std::cerr << "False";
|
||||
std::cerr << std::endl;
|
||||
#endif
|
||||
return token.substr(token.length()-postfix.length()) == postfix;
|
||||
}
|
||||
// contains comparator function
|
||||
static bool
|
||||
_contains (const std::string& token, std::string substr) {
|
||||
return token.find(substr) != std::string::npos;
|
||||
}
|
||||
|
||||
// Gets some data from the serial port
|
||||
void readSomeData (std::string &temp, size_t this_many) {
|
||||
// Make sure there is a serial port
|
||||
if (this->serial_port_ == NULL) {
|
||||
this->handle_exc(SerialListenerException("Invalid serial port."));
|
||||
}
|
||||
// Make sure the serial port is open
|
||||
if (!this->serial_port_->isOpen()) {
|
||||
this->handle_exc(SerialListenerException("Serial port not open."));
|
||||
}
|
||||
temp = this->serial_port_->read(this_many);
|
||||
}
|
||||
// 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
|
||||
void callback ();
|
||||
// Determines how much to read on each loop of listen
|
||||
size_t determineAmountToRead ();
|
||||
|
||||
// Tokenizer
|
||||
TokenizerType tokenize;
|
||||
|
||||
// Exception handler
|
||||
ExceptionCallback handle_exc;
|
||||
|
||||
// Default handler
|
||||
FilterPtr default_filter;
|
||||
DataCallback _default_handler;
|
||||
ComparatorType default_comparator;
|
||||
void default_handler(const std::string &token);
|
||||
|
||||
// Persistent listening variables
|
||||
bool listening;
|
||||
char serial_port_padding[7];
|
||||
serial::Serial * serial_port_;
|
||||
boost::thread listen_thread;
|
||||
std::string data_buffer;
|
||||
size_t chunk_size_;
|
||||
|
||||
// Callback related variables
|
||||
// filter id, token
|
||||
// filter id == 0 is going to be default handled
|
||||
ConcurrentQueue<std::pair<FilterPtr,TokenPtr> >
|
||||
callback_queue;
|
||||
boost::thread callback_thread;
|
||||
|
||||
// Mutex for locking use of filters
|
||||
boost::mutex filter_mux;
|
||||
// vector of filter ids
|
||||
std::vector<FilterPtr> filters;
|
||||
|
||||
};
|
||||
|
||||
/*!
|
||||
* This is the a filter that provides a wait function for blocking until a
|
||||
* match is found.
|
||||
*
|
||||
* This should probably not be created manually, but instead should be
|
||||
* constructed using SerialListener::createBlockingFilter(ComparatorType)
|
||||
* function which returns a BlockingFilter instance.
|
||||
*
|
||||
* \see serial::SerialListener::ComparatorType,
|
||||
* serial::SerialListener::createBlockingFilter
|
||||
*/
|
||||
class BlockingFilter
|
||||
{
|
||||
public:
|
||||
BlockingFilter (ComparatorType comparator, SerialListener &listener) {
|
||||
this->listener_ = &listener;
|
||||
DataCallback cb = boost::bind(&BlockingFilter::callback, this, _1);
|
||||
this->filter_ptr = this->listener_->createFilter(comparator, cb);
|
||||
}
|
||||
|
||||
virtual ~BlockingFilter () {
|
||||
this->listener_->removeFilter(filter_ptr);
|
||||
this->result = "";
|
||||
this->cond.notify_all();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Waits a given number of milliseconds or until a token is matched. If a
|
||||
* token is matched it is returned, otherwise an empty string is returned.
|
||||
*
|
||||
* \param ms Time in milliseconds to wait on a new token.
|
||||
*
|
||||
* \return std::string token that was matched or "" if none were matched.
|
||||
*/
|
||||
std::string wait(long ms) {
|
||||
this->result = "";
|
||||
boost::unique_lock<boost::mutex> lock(this->mutex);
|
||||
this->cond.timed_wait(lock, boost::posix_time::milliseconds(ms));
|
||||
return this->result;
|
||||
}
|
||||
|
||||
FilterPtr filter_ptr;
|
||||
|
||||
void callback(const std::string& token) {
|
||||
#if SERIAL_LISTENER_DEBUG
|
||||
std::cerr << "In BlockingFilter callback(" << token.length() << "): ";
|
||||
std::cerr << token << std::endl;
|
||||
#endif
|
||||
this->cond.notify_all();
|
||||
this->result = token;
|
||||
}
|
||||
|
||||
private:
|
||||
SerialListener * listener_;
|
||||
boost::condition_variable cond;
|
||||
boost::mutex mutex;
|
||||
std::string result;
|
||||
|
||||
};
|
||||
|
||||
/*!
|
||||
* This is the a filter that provides a wait function for blocking until a
|
||||
* match is found. It will also buffer up to a given buffer size of tokens so
|
||||
* that they can be counted or accessed after they are matched by the filter.
|
||||
*
|
||||
* This should probably not be created manually, but instead should be
|
||||
* constructed using SerialListener::createBufferedFilter(ComparatorType)
|
||||
* function which returns a BufferedFilter instance.
|
||||
*
|
||||
* The internal buffer is a circular queue buffer, so when the buffer is full,
|
||||
* the oldest token is dropped and the new one is added. Additionally, when
|
||||
* wait is a called the oldest available token is returned.
|
||||
*
|
||||
* \see serial::SerialListener::ComparatorType,
|
||||
* serial::SerialListener::createBufferedFilter
|
||||
*/
|
||||
class BufferedFilter
|
||||
{
|
||||
public:
|
||||
BufferedFilter (ComparatorType comparator, size_t buffer_size,
|
||||
SerialListener &listener)
|
||||
: buffer_size_(buffer_size)
|
||||
{
|
||||
this->listener_ = &listener;
|
||||
DataCallback cb = boost::bind(&BufferedFilter::callback, this, _1);
|
||||
this->filter_ptr = this->listener_->createFilter(comparator, cb);
|
||||
}
|
||||
|
||||
virtual ~BufferedFilter () {
|
||||
this->listener_->removeFilter(filter_ptr);
|
||||
this->queue.clear();
|
||||
this->result = "";
|
||||
}
|
||||
|
||||
/*!
|
||||
* Waits a given number of milliseconds or until a matched token is
|
||||
* available in the buffer. If a token is matched it is returned, otherwise
|
||||
* an empty string is returned.
|
||||
*
|
||||
* \param ms Time in milliseconds to wait on a new token. If ms is set to 0
|
||||
* then it will try to get a new token if one is available but will not
|
||||
* block.
|
||||
*
|
||||
* \return std::string token that was matched or "" if none were matched.
|
||||
*/
|
||||
std::string wait(long ms) {
|
||||
if (ms == 0) {
|
||||
if (!this->queue.try_pop(this->result)) {
|
||||
this->result = "";
|
||||
}
|
||||
} else {
|
||||
if (!this->queue.timed_wait_and_pop(this->result, ms)) {
|
||||
this->result = "";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Clears the buffer of any tokens.
|
||||
*/
|
||||
void clear() {
|
||||
queue.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns the number of tokens waiting in the buffer.
|
||||
*/
|
||||
size_t count() {
|
||||
return queue.size();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns the capacity of the buffer.
|
||||
*/
|
||||
size_t capacity() {
|
||||
return buffer_size_;
|
||||
}
|
||||
|
||||
FilterPtr filter_ptr;
|
||||
|
||||
void callback(const std::string &token) {
|
||||
#if SERIAL_LISTENER_DEBUG
|
||||
std::cerr << "In BufferedFilter callback(" << token.length() << "): ";
|
||||
std::cerr << token << std::endl;
|
||||
#endif
|
||||
std::string throw_away;
|
||||
if (this->queue.size() == this->buffer_size_) {
|
||||
this->queue.wait_and_pop(throw_away);
|
||||
}
|
||||
this->queue.push(token);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t buffer_size_;
|
||||
SerialListener * listener_;
|
||||
ConcurrentQueue<std::string> queue;
|
||||
std::string result;
|
||||
|
||||
};
|
||||
|
||||
} // namespace serial
|
||||
|
||||
#endif // SERIAL_LISTENER_H
|
||||
237
serial.cmake
237
serial.cmake
@ -1,158 +1,133 @@
|
||||
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 Setup
|
||||
cmake_minimum_required(VERSION 2.4.6)
|
||||
|
||||
project(Serial)
|
||||
if(COMMAND cmake_policy)
|
||||
cmake_policy(SET CMP0003 NEW)
|
||||
endif(COMMAND cmake_policy)
|
||||
|
||||
## Configurations
|
||||
project(Serial)
|
||||
|
||||
# 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)
|
||||
## Configurations
|
||||
|
||||
option(SERIAL_BUILD_TESTS "Build all of the Serial tests." OFF)
|
||||
option(SERIAL_BUILD_EXAMPLES "Build all of the Serial examples." OFF)
|
||||
# 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)
|
||||
|
||||
# Allow for building shared libs override
|
||||
IF(NOT BUILD_SHARED_LIBS)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
ENDIF(NOT BUILD_SHARED_LIBS)
|
||||
option(SERIAL_BUILD_TESTS "Build all of the Serial tests." OFF)
|
||||
option(SERIAL_BUILD_EXAMPLES "Build all of the Serial examples." OFF)
|
||||
|
||||
# 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))
|
||||
# Allow for building shared libs override
|
||||
IF(NOT BUILD_SHARED_LIBS)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
ENDIF(NOT BUILD_SHARED_LIBS)
|
||||
|
||||
## Configure the build system
|
||||
# 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))
|
||||
|
||||
# Add the include folder to the include path
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
## Configure the build system
|
||||
|
||||
# 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)
|
||||
# Add the include folder to the include path
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
IF(UNIX)
|
||||
list(APPEND SERIAL_SRCS src/impl/unix.cc)
|
||||
list(APPEND SERIAL_HEADERS include/serial/impl/unix.h)
|
||||
ELSE(UNIX)
|
||||
|
||||
ENDIF(UNIX)
|
||||
# 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)
|
||||
|
||||
# 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)
|
||||
## Build the Serial Library
|
||||
|
||||
link_directories(${Boost_LIBRARY_DIRS})
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
# Compile the Library
|
||||
add_library(serial ${SERIAL_SRCS})
|
||||
|
||||
set(SERIAL_LINK_LIBS ${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
${Boost_THREAD_LIBRARY})
|
||||
## Build Examples
|
||||
|
||||
## Build the Serial Library
|
||||
# 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)
|
||||
|
||||
# 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 tests
|
||||
|
||||
## Build Examples
|
||||
# If asked to
|
||||
IF(SERIAL_BUILD_TESTS)
|
||||
# Find Google Test
|
||||
enable_testing()
|
||||
find_package(GTest REQUIRED)
|
||||
include_directories(${GTEST_INCLUDE_DIRS})
|
||||
|
||||
# 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 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)
|
||||
|
||||
# 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)
|
||||
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
|
||||
DESTINATION include/serial)
|
||||
|
||||
INSTALL(FILES include/serial/serial.h
|
||||
include/serial/serial_listener.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
|
||||
cd bin && ./serial_tests
|
||||
@ -1,51 +1,41 @@
|
||||
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)
|
||||
cmake_minimum_required(VERSION 2.4.6)
|
||||
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
|
||||
|
||||
rosbuild_init()
|
||||
# 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)
|
||||
|
||||
#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)
|
||||
rosbuild_init()
|
||||
|
||||
include_directories(include)
|
||||
#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)
|
||||
|
||||
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)
|
||||
include_directories(include)
|
||||
|
||||
# Build the serial library
|
||||
rosbuild_add_library(${PROJECT_NAME} ${SERIAL_SRCS})
|
||||
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)
|
||||
|
||||
# Add boost dependencies
|
||||
rosbuild_add_boost_directories()
|
||||
rosbuild_link_boost(${PROJECT_NAME} system filesystem thread)
|
||||
# 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})
|
||||
# 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})
|
||||
# Create unit tests
|
||||
rosbuild_add_gtest(serial_tests tests/serial_tests.cc)
|
||||
target_link_libraries(serial_tests ${PROJECT_NAME})
|
||||
|
||||
endmacro(build_serial)
|
||||
|
||||
@ -1,217 +0,0 @@
|
||||
/* 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();
|
||||
}
|
||||
|
||||
@ -1,238 +0,0 @@
|
||||
/* 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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user