mirror of
https://github.com/wjwwood/serial.git
synced 2026-01-22 19:54:57 +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}")
|
/usr/local/include/serial "$ENV{NAMER_ROOT}")
|
||||||
|
|
||||||
find_library(serial_LIBRARIES serial /usr/lib /usr/local/lib
|
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
|
ifdef ROS_ROOT
|
||||||
include $(shell rospack find mk)/cmake.mk
|
include $(shell rospack find mk)/cmake.mk
|
||||||
else
|
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
|
|
||||||
59
serial.cmake
59
serial.cmake
@ -1,4 +1,5 @@
|
|||||||
macro(build_serial)
|
macro(build_serial)
|
||||||
|
|
||||||
## Project Setup
|
## Project Setup
|
||||||
cmake_minimum_required(VERSION 2.4.6)
|
cmake_minimum_required(VERSION 2.4.6)
|
||||||
|
|
||||||
@ -14,8 +15,12 @@ project(Serial)
|
|||||||
IF(EXISTS /usr/bin/clang)
|
IF(EXISTS /usr/bin/clang)
|
||||||
set(CMAKE_CXX_COMPILER /usr/bin/clang++)
|
set(CMAKE_CXX_COMPILER /usr/bin/clang++)
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "")
|
set(CMAKE_OSX_DEPLOYMENT_TARGET "")
|
||||||
# set(CMAKE_CXX_FLAGS "-ferror-limit=5 -std=c++0x -stdlib=libc++")
|
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")
|
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)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
ENDIF(EXISTS /usr/bin/clang)
|
ENDIF(EXISTS /usr/bin/clang)
|
||||||
|
|
||||||
@ -42,37 +47,19 @@ ENDIF(NOT DEFINED(LIBRARY_OUTPUT_PATH))
|
|||||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||||
|
|
||||||
# Add default source files
|
# Add default source files
|
||||||
set(SERIAL_SRCS src/serial.cc src/impl/unix.cc src/serial_listener.cc)
|
set(SERIAL_SRCS src/serial.cc)
|
||||||
# Add default header files
|
IF(WIN32)
|
||||||
set(SERIAL_HEADERS include/serial/serial.h include/serial/serial_listener.h)
|
list(APPEND SERIAL_SRCS src/impl/windows.cc)
|
||||||
|
ELSE(WIN32)
|
||||||
IF(UNIX)
|
|
||||||
list(APPEND SERIAL_SRCS src/impl/unix.cc)
|
list(APPEND SERIAL_SRCS src/impl/unix.cc)
|
||||||
list(APPEND SERIAL_HEADERS include/serial/impl/unix.h)
|
ENDIF(WIN32)
|
||||||
ELSE(UNIX)
|
# Add default header files
|
||||||
|
set(SERIAL_HEADERS include/serial/serial.h)
|
||||||
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
|
## Build the Serial Library
|
||||||
|
|
||||||
# Compile the Library
|
# Compile the Library
|
||||||
add_library(serial ${SERIAL_SRCS} ${SERIAL_HEADERS})
|
add_library(serial ${SERIAL_SRCS})
|
||||||
target_link_libraries(serial ${SERIAL_LINK_LIBS})
|
|
||||||
IF( WIN32 )
|
|
||||||
target_link_libraries(serial wsock32)
|
|
||||||
ENDIF( )
|
|
||||||
|
|
||||||
## Build Examples
|
## Build Examples
|
||||||
|
|
||||||
@ -82,12 +69,6 @@ IF(SERIAL_BUILD_EXAMPLES)
|
|||||||
add_executable(serial_example examples/serial_example.cc)
|
add_executable(serial_example examples/serial_example.cc)
|
||||||
# Link the Test program to the Serial library
|
# Link the Test program to the Serial library
|
||||||
target_link_libraries(serial_example serial)
|
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)
|
ENDIF(SERIAL_BUILD_EXAMPLES)
|
||||||
|
|
||||||
## Build tests
|
## Build tests
|
||||||
@ -99,18 +80,12 @@ IF(SERIAL_BUILD_TESTS)
|
|||||||
find_package(GTest REQUIRED)
|
find_package(GTest REQUIRED)
|
||||||
include_directories(${GTEST_INCLUDE_DIRS})
|
include_directories(${GTEST_INCLUDE_DIRS})
|
||||||
|
|
||||||
# Compile the Serial Listener Test program
|
# Compile the Serial Test program
|
||||||
add_executable(serial_listener_tests tests/serial_listener_tests.cc)
|
|
||||||
add_executable(serial_tests tests/serial_tests.cc)
|
add_executable(serial_tests tests/serial_tests.cc)
|
||||||
# Link the Test program to the serial library
|
# 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}
|
target_link_libraries(serial_tests ${GTEST_BOTH_LIBRARIES}
|
||||||
serial)
|
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)
|
add_test(AllTestsIntest_serial serial_tests)
|
||||||
ENDIF(SERIAL_BUILD_TESTS)
|
ENDIF(SERIAL_BUILD_TESTS)
|
||||||
|
|
||||||
@ -130,14 +105,14 @@ IF(NOT SERIAL_DONT_CONFIGURE_INSTALL)
|
|||||||
)
|
)
|
||||||
|
|
||||||
INSTALL(FILES include/serial/serial.h
|
INSTALL(FILES include/serial/serial.h
|
||||||
include/serial/serial_listener.h
|
|
||||||
DESTINATION include/serial)
|
DESTINATION include/serial)
|
||||||
|
|
||||||
IF(NOT CMAKE_FIND_INSTALL_PATH)
|
IF(NOT CMAKE_FIND_INSTALL_PATH)
|
||||||
set(CMAKE_FIND_INSTALL_PATH ${CMAKE_ROOT})
|
set(CMAKE_FIND_INSTALL_PATH ${CMAKE_ROOT})
|
||||||
ENDIF(NOT CMAKE_FIND_INSTALL_PATH)
|
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)
|
||||||
|
|
||||||
|
|||||||
@ -37,4 +37,4 @@ ifneq ($(MAKE),)
|
|||||||
else
|
else
|
||||||
cd build && make
|
cd build && make
|
||||||
endif
|
endif
|
||||||
cd bin && ./serial_listener_tests
|
cd bin && ./serial_tests
|
||||||
@ -1,4 +1,5 @@
|
|||||||
macro(build_serial)
|
macro(build_serial)
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 2.4.6)
|
cmake_minimum_required(VERSION 2.4.6)
|
||||||
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
|
include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
|
||||||
|
|
||||||
@ -23,29 +24,18 @@ set(SERIAL_SRCS src/serial.cc)
|
|||||||
if(UNIX)
|
if(UNIX)
|
||||||
list(APPEND SERIAL_SRCS src/impl/unix.cc)
|
list(APPEND SERIAL_SRCS src/impl/unix.cc)
|
||||||
else(UNIX)
|
else(UNIX)
|
||||||
list(APPEND SERIAL_SRCS src/impl/windows.cc)
|
list(APPEND SERIAL_SRCS src/impl/winows.cc)
|
||||||
endif(UNIX)
|
endif(UNIX)
|
||||||
list(APPEND SERIAL_SRCS src/serial_listener.cc)
|
|
||||||
|
|
||||||
# Build the serial library
|
# Build the serial library
|
||||||
rosbuild_add_library(${PROJECT_NAME} ${SERIAL_SRCS})
|
rosbuild_add_library(${PROJECT_NAME} ${SERIAL_SRCS})
|
||||||
|
|
||||||
# Add boost dependencies
|
|
||||||
rosbuild_add_boost_directories()
|
|
||||||
rosbuild_link_boost(${PROJECT_NAME} system filesystem thread)
|
|
||||||
|
|
||||||
# Build example
|
# Build example
|
||||||
rosbuild_add_executable(serial_example examples/serial_example.cc)
|
rosbuild_add_executable(serial_example examples/serial_example.cc)
|
||||||
target_link_libraries(serial_example ${PROJECT_NAME})
|
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
|
# Create unit tests
|
||||||
rosbuild_add_gtest(serial_tests tests/serial_tests.cc)
|
rosbuild_add_gtest(serial_tests tests/serial_tests.cc)
|
||||||
target_link_libraries(serial_tests ${PROJECT_NAME})
|
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})
|
|
||||||
|
|
||||||
endmacro(build_serial)
|
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