1
0
mirror of https://github.com/wjwwood/serial.git synced 2026-01-22 03:34:53 +08:00

Still working on Serial listener refactor, not working, fixing to make big changes.

This commit is contained in:
William Woodall 2012-01-11 23:07:58 -06:00
parent c8e7841223
commit 7c0c976033
9 changed files with 646 additions and 609 deletions

View File

@ -5,59 +5,80 @@
using namespace serial;
void default_handler(std::string line) {
std::cout << "default_handler got a: " << line << std::endl;
void default_handler(std::string token) {
std::cout << "default_handler got a: " << token << std::endl;
}
void callback(std::string line) {
std::cout << "callback got a: " << line << std::endl;
void callback(std::string token) {
std::cout << "callback got a: " << token << std::endl;
}
#if 1
int main(void) {
// Assuming this device prints the string 'pre-substr-post\r' at 100Hz
Serial serial("/dev/tty.usbmodemfd1231", 115200);
SerialListener listener;
// Set the time to live for messages to 10 milliseconds
listener.setTimeToLive(10);
listener.startListening(serial);
listener.listenFor(SerialListener::startsWith("V="), callback);
// 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"));
serial.write("?$1E\r");
if (!listener.listenForStringOnce("?$1E")) {
std::cerr << "Didn't get conformation of device version!" << std::endl;
return 1;
// 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
{
BlockingFilter 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.
serial.write("?V\r");
serial.write("# 1\r");
while (true) {
// Sleep 100 ms
SerialListener::sleep(100);
// Method #3:
// comparator, token buffer size - blocking
{
// Give it a comparator, then a buffer size of 10
BufferedFilter 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;
}
#endif
#if 0
int main(void) {
Serial serial("/dev/tty.usbmodemfd1231", 115200);
serial.write("?$1E\r");
SerialListener::sleep(10);
// if ("?$1E\r" != serial.read(5)) {
// std::cerr << "Didn't get conformation of device version!" << std::endl;
// return 1;
// }
serial.write("?V\r");
serial.write("# 1\r");
while (true) {
std::cout << serial.read(5) << std::endl;
}
}
#endif

View File

@ -37,7 +37,7 @@
#include "serial/serial.h"
namespace {
namespace serial {
class Serial::Serial_pimpl {
public:
@ -84,7 +84,17 @@ public:
flowcontrol_t getFlowcontrol () const;
private:
// Serial handle
int fd;
// Parameters
std::string port;
int baudrate;
long timeout;
bytesize_t bytesize;
parity_t parity;
stopbits_t stopbits;
flowcontrol_t flowcontrol;
};

View File

@ -33,10 +33,13 @@
* This provides a cross platform interface for interacting with Serial Ports.
*/
#ifndef SERIAL_H
#define SERIAL_H
#include <string>
#include <memory> // std::shared_ptr
#include <sstream>
namespace serial {
/*!
@ -191,7 +194,7 @@ public:
* \see Serial::read(size_t)
*/
size_t
read (const std::string &buffer, size_t size = 1);
read (std::string &buffer, size_t size = 1);
/*! Write bytes from the data to the serial port by given length.
*
@ -368,7 +371,7 @@ private:
// Pimpl idiom, d_pointer
class Serial_pimpl;
std::shared_ptr<Serial_pimpl> pimpl;
Serial_pimpl * pimpl;
};

View File

@ -38,6 +38,7 @@
// STL
#include <queue>
#include <stdint.h>
// Serial
#include <serial/serial.h>
@ -45,19 +46,32 @@
// Boost
#include <boost/function.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread.hpp>
namespace serial {
/*!
* This is a general function type that is used as the callback prototype
* for asynchronous functions like the default handler callback and the
* 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
* 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
@ -65,11 +79,11 @@ namespace serial {
typedef boost::function<void(const std::string&)> DataCallback;
/*!
* This is a general function type that is used as the comparator callback
* 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
* 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
@ -79,10 +93,10 @@ typedef boost::function<bool(const std::string&)> ComparatorType;
/*!
* This function type describes the prototype for the logging callbacks.
*
* The function takes a std::string reference and returns nothing. It is
* called from the library when a logging message occurs. This
* allows the library user to hook into this and integrate it with their own
* logging system. It can be set with any of the set<log level>Handler
* The function takes a std::string reference and returns nothing. It is
* called from the library when a logging message occurs. This
* allows the library user to hook into this and integrate it with their own
* logging system. It can be set with any of the set<log level>Handler
* functions.
*
* \see SerialListener::setInfoHandler, SerialListener::setDebugHandler,
@ -104,27 +118,214 @@ typedef boost::function<void(const std::exception&)> ExceptionCallback;
/*!
* This function type describes the prototype for the tokenizer callback.
*
* The function should take a std::string reference and tokenize it into a
* several std::string's and store them in the given
* std::vector<std::string> reference. There are some default ones or the
* user can create their own.
* 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 std::string'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
* 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 all complete messages, or:
* "msg1\rpartial_msg2" -> ["msg1","partial_msg2"] for partial messages.
* 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
* \see SerialListener::setTokenizer, serial::delimeter_tokenizer,
* serial::TokenPtr
*/
typedef boost::function<void(std::string&,std::vector<std::string>&)>
typedef boost::function<void(const std::string&, std::vector<TokenPtr>&)>
TokenizerType;
/*! This is a convenience alias for boost::uuids::uuid. */
typedef boost::uuids::uuid uuid_type; // uuid_t is already taken! =(
/*!
* 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;
};
/*!
* 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;
/*!
* 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,
boost::shared_ptr<SerialListener> listener)
: listener(listener)
{
DataCallback cb = boost::bind(&BlockingFilter::callback, this, _1);
this->filter_ptr = 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(size_t ms) {
this->result = "";
boost::unique_lock<boost::mutex> lock(this->mutex);
this->cond.timed_wait(lock, boost::posix_time::milliseconds(ms));
return this->result;
}
private:
void callback(const std::string& token) {
this->cond.notify_all();
this->result = token;
}
FilterPtr filter_ptr;
boost::shared_ptr<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,
boost::shared_ptr<SerialListener> listener)
: listener(listener), buffer_size(buffer_size)
{
DataCallback cb = boost::bind(&BlockingFilter::callback, this, _1);
this->filter_ptr = listener.createFilter(comparator, cb);
}
virtual ~BufferedFilter () {
this->listener.removeFilter(filter_ptr);
this->queue.clear();
this->result = "";
this->cond.notify_all();
}
/*!
* 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(size_t 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;
}
private:
void callback(const std::string &token) {
std::string throw_away;
if (this->queue.size() == buffer_size)
this->queue.wait_and_pop(throw_away);
this->queue.push(token);
}
FilterPtr filter_ptr;
size_t buffer_size;
boost::shared_ptr<SerialListener> listener;
ConcurrentQueue<std::string> queue;
std::string result;
};
/*!
* This is a general exception generated by the SerialListener class.
@ -220,13 +421,6 @@ public:
}
};
namespace filter_type {
typedef enum {
nonblocking, blocking
} FilterType;
}
/*!
* Listens to a serial port, facilitates asynchronous reading
*/
@ -245,22 +439,11 @@ public:
/***** Configurations ******/
/*!
* Sets the time-to-live (ttl) for messages waiting to be processsed.
*
* Messages are processed before checking for expiration, therefore they
* will always be passed through filters once before being removed
* due to ttl expiration. The default value for this is 10 ms.
*
* \param ms Time in milliseconds until messages are purged from the buffer.
*/
void setTimeToLive (size_t ms = 10);
/*!
* 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<std::string> of data tokens.
* 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.
*
@ -268,10 +451,21 @@ public:
*
* \see serial::TokenizerType, serial::delimeter_tokenizer
*/
void setTokenizer (TokenizerType 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 ******/
/*!
@ -280,7 +474,8 @@ public:
* \param serial_port Pointer to a serial::Serial object that is used to
* retrieve new data.
*/
void startListening (serial::Serial &serial_port);
void
startListening (serial::Serial &serial_port);
/*!
* Stops the listening thread and blocks until it completely stops.
@ -288,38 +483,11 @@ public:
* This function also clears all of the active filters from listenFor and
* similar functions.
*/
void stopListening ();
void
stopListening ();
/***** Filter Functions ******/
/*!
* Blocks until the given string is detected or until the timeout occurs.
*
* \param token std::string that should be watched for, this string must
* match the message exactly.
*
* \param timeout in milliseconds before timing out and returning false.
* Defaults to 1000 milliseconds or 1 second.
*
* \return bool If true then the token was detected before the token, false
* if the token was not heard and the timeout occured.
*/
bool listenForStringOnce (std::string token, size_t timeout = 1000);
/*!
* Blocks until the comparator returns true or until the timeout occurs.
*
* \param comparator ComparatorType function that should return true if the
* given std::string matches otherwise false.
*
* \param timeout in milliseconds before timing out and returning false.
* Defaults to 1000 milliseconds or 1 second.
*
* \return bool If true then the token was detected before the token, false
* if the token was not heard and the timeout occured.
*/
bool listenForOnce (ComparatorType comparator, size_t timeout = 1000);
/*!
* Setups up a filter that calls a callback when a comparator returns true.
*
@ -335,23 +503,84 @@ public:
* \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::uuids::uuid a unique identifier used to remove the filter.
* \return boost::shared_ptr<Filter> so you can remove it later.
*
* \see SerialListener::stopListeningFor
*/
uuid_type listenFor (ComparatorType comparator, DataCallback callback);
FilterPtr
listenFor (ComparatorType comparator, DataCallback callback);
/*!
* Blocks until the comparator returns true or until the timeout occurs.
*
* \param comparator ComparatorType function that should return true if the
* given std::string matches otherwise false.
*
* \param timeout in milliseconds before timing out and returning false.
* Defaults to 1000 milliseconds or 1 second.
*
* \return std::string the token that was matched, returns an empty string
* if the timeout occurs first.
* i.e. if (listenForOnce(...) != "") // Got match
*/
std::string
listenForOnce (ComparatorType comparator, size_t timeout = 1000);
/*!
* Writes to the seiral port then blocks until the comparator returns true
* or until the timeout occurs.
*
* This function creates a filter, writes the data, then waits for it to
* match atleast once.
*
* \param to_be_written const std::string reference of data to be written to
* the serial port.
*
* \param comparator ComparatorType function that should return true if the
* given std::string matches otherwise false.
*
* \param timeout in milliseconds before timing out and returning false.
* Defaults to 1000 milliseconds or 1 second.
*
* \return std::string the token that was matched, returns an empty string
* if the timeout occurs first.
* i.e. if (listenForOnce(...) != "") // Got match
*/
std::string
listenForOnce (ComparatorType comparator, size_t timeout = 1000);
/*!
* Blocks until the given string is detected or until the timeout occurs.
*
* \param token std::string that should be watched for, this string must
* match the message exactly.
*
* \param timeout in milliseconds before timing out and returning false.
* Defaults to 1000 milliseconds or 1 second.
*
* \return bool If true then the token was detected before the token, false
* if the token was not heard and the timeout occured.
*/
bool
listenForStringOnce (std::string token, size_t timeout = 1000);
/*!
* Removes a filter by a given uuid.
*
* The uuid for a filter is returned by the listenFor function.
*
* \param filter_uuid The uuid of the filter to be removed.
* \param filter_ptr A shared pointer to the filter.
*
* \see SerialListener::listenFor
*/
void stopListeningFor (uuid_type filter_uuid);
void
stopListeningFor (FilterPtr filter_ptr);
/*!
* Stops listening for anything, but doesn't stop reading the serial port.
*/
void stopListeningForAll ();
void
stopListeningForAll ();
/***** Hooks and Handlers ******/
@ -368,8 +597,9 @@ public:
*
* \see serial::DataCallback, SerialListener::setInfoHandler
*/
void setDefaultHandler(DataCallback default_handler) {
this->default_handler = default_handler;
void
setDefaultHandler (DataCallback default_handler) {
this->_default_handler = default_handler;
}
/*!
@ -421,7 +651,8 @@ public:
*
* \see serial::LoggingCallback
*/
void setInfoHandler(LoggingCallback info_handler) {
void
setInfoHandler (LoggingCallback info_handler) {
this->info = info_handler;
}
@ -438,7 +669,8 @@ public:
*
* \see serial::LoggingCallback, SerialListener::setInfoHandler
*/
void setDebugHandler(LoggingCallback debug_handler) {
void
setDebugHandler (LoggingCallback debug_handler) {
this->debug = debug_handler;
}
@ -455,7 +687,8 @@ public:
*
* \see serial::LoggingCallback, SerialListener::setInfoHandler
*/
void setWarningHandler(LoggingCallback warning_handler) {
void
setWarningHandler (LoggingCallback warning_handler) {
this->warn = warning_handler;
}
@ -491,12 +724,10 @@ public:
* \see SerialListener::setTokenizer, serial::TokenizerType
*/
static TokenizerType
delimeter_tokenizer (std::string delimeter);
// delimeter tokenizer function
static void
_delimeter_tokenizer (std::string &data, std::vector<std::string> &tokens,
std::string delimeter);
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.
@ -519,11 +750,9 @@ public:
* serial::ComparatorType
*/
static ComparatorType
exactly (std::string exact_str);
// exact comparator function
static bool
_exactly (const std::string&, std::string);
exactly (std::string exact_str) {
return boost::bind(&SerialListener::_exactly, _1, exact_str);
}
/*!
* This returns a comparator that looks for a given prefix.
@ -549,12 +778,6 @@ public:
return boost::bind(&SerialListener::_startsWith, _1, prefix);
}
// exact comparator function
static bool
_startsWith (const std::string& token, std::string prefix) {
return token.substr(0,prefix.length()) == prefix;
}
/*!
* This returns a comparator that looks for a given postfix.
*
@ -579,12 +802,6 @@ public:
return boost::bind(&SerialListener::_endsWith, _1, postfix);
}
// endswith comparator function
static bool
_endsWith (const std::string& token, std::string postfix) {
return token.substr(token.length()-postfix.length()) == postfix;
}
/*!
* This returns a comparator that looks for a given substring in the token.
*
@ -607,42 +824,55 @@ public:
*/
static ComparatorType
contains (std::string substr) {
return boost::bind(&SerialListener::_contains, _1, substr);
return boost::bind(_contains, _1, substr);
}
private:
// 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) {
return token == exact_str;
}
// startswith comparator function
static bool
_startsWith (const std::string& token, std::string prefix) {
return token.substr(0,prefix.length()) == prefix;
}
// endswith comparator function
static bool
_endsWith (const std::string& token, std::string postfix) {
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;
}
private:
// Gets some data from the serial port
void readSomeData (std::string&, size_t);
// Takes newly tokenized data and processes them
void addNewTokens(std::vector<std::string> &new_tokens,
std::vector<uuid_type> &new_uuids,
std::string &left_overs);
// Runs the new tokens through the filters
void filterNewTokens (std::vector<uuid_type> new_uuids);
// Runs a list of tokens through one filter
std::vector<std::pair<uuid_type,uuid_type> >
filter(uuid_type filter_uuid, std::vector<uuid_type> &token_uuids);
void filterNewTokens (std::vector<TokenPtr> new_tokens);
// Given a filter_id and a list of tokens, return list of matched tokens
void filter (FilterPtr filter, std::vector<TokenPtr> &tokens);
// Function that loops while listening is true
void listen ();
// Target of callback thread
void callback ();
// Prune old tokens
void pruneTokens ();
// Erases one token
void eraseToken (uuid_type&);
// Erases several tokens
void eraseTokens (std::vector<uuid_type>&);
// Determines how much to read on each loop of listen
size_t determineAmountToRead ();
// Hanlder for listen for once
typedef boost::shared_ptr<boost::condition_variable> shared_cond_var_ptr_t;
void notifyListenForOnce (shared_cond_var_ptr_t cond_ptr);
// Tokenizer
TokenizerType tokenize;
@ -656,38 +886,30 @@ private:
ExceptionCallback handle_exc;
// Default handler
DataCallback default_handler;
FilterPtr default_filter;
DataCallback _default_handler;
ComparatorType default_comparator;
void default_handler(const std::string &token);
// Persistent listening variables
bool listening;
serial::Serial * serial_port;
boost::thread listen_thread;
std::string data_buffer;
boost::mutex token_mux;
std::map<const uuid_type, std::string> tokens;
std::map<const uuid_type, boost::posix_time::ptime> ttls;
size_t chunk_size;
// Callback related variables
// filter uuid, token uuid
ConcurrentQueue<std::pair<uuid_type,uuid_type> > callback_queue;
// filter id, token
// filter id == 0 is going to be default handled
ConcurrentQueue<std::pair<FilterPtr,TokenPtr> >
callback_queue;
boost::thread callback_thread;
// For generating random uuids
boost::uuids::random_generator random_generator;
boost::uuids::nil_generator nil_generator;
// Setting for ttl on messages
boost::posix_time::time_duration ttl;
// map<uuid, filter type (blocking/non-blocking)>
std::vector<uuid_type> filters;
// map<uuid, comparator>
std::map<const uuid_type, ComparatorType> comparators;
// map<uuid, callback>
std::map<const uuid_type, DataCallback> callbacks;
// Mutex for locking use of filters
boost::mutex filter_mux;
boost::mutex callback_mux;
// vector of filter ids
std::vector<FilterPtr> filters;
};
}

View File

@ -13,7 +13,9 @@ project(Serial)
# Use clang if available
IF(EXISTS /usr/bin/clang)
set(CMAKE_CXX_COMPILER /usr/bin/clang++)
set(CMAKE_CXX_FLAGS -ferror-limit=5)
set(CMAKE_OSX_DEPLOYMENT_TARGET "")
# set(CMAKE_CXX_FLAGS "-ferror-limit=5 -std=c++0x -stdlib=libc++")
set(CMAKE_CXX_FLAGS "-ferror-limit=5")
ENDIF(EXISTS /usr/bin/clang)
option(SERIAL_BUILD_TESTS "Build all of the Serial tests." OFF)
@ -43,6 +45,13 @@ set(SERIAL_SRCS src/serial.cc src/serial_listener.cc)
# Add default header files
set(SERIAL_HEADERS include/serial/serial.h include/serial/serial_listener.h)
IF(UNIX)
list(APPEND SERIAL_SRCS src/impl/unix.cc)
list(APPEND SERIAL_HEADERS include/serial/impl/unix.h)
ELSE(UNIX)
ENDIF(UNIX)
# Find Boost, if it hasn't already been found
IF(NOT Boost_FOUND OR NOT Boost_SYSTEM_FOUND OR NOT Boost_FILESYSTEM_FOUND OR NOT Boost_THREAD_FOUND)
find_package(Boost COMPONENTS system filesystem thread REQUIRED)
@ -61,7 +70,7 @@ set(SERIAL_LINK_LIBS ${Boost_SYSTEM_LIBRARY}
add_library(serial ${SERIAL_SRCS} ${SERIAL_HEADERS})
target_link_libraries(serial ${SERIAL_LINK_LIBS})
IF( WIN32 )
target_link_libraries(serial wsock32)
target_link_libraries(serial wsock32)
ENDIF( )
# Check for OS X and if so disable kqueue support in asio
@ -97,8 +106,10 @@ IF(SERIAL_BUILD_TESTS)
# Compile the Serial Listener Test program
add_executable(serial_listener_tests tests/serial_listener_tests.cc)
# Link the Test program to the serial library
target_link_libraries(serial_listener_tests ${GTEST_BOTH_LIBRARIES}
serial)
target_link_libraries(serial_listener_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)
ENDIF(SERIAL_BUILD_TESTS)

View File

@ -2,127 +2,128 @@
using namespace serial;
Serial_pimpl::Serial_pimpl (const std::string &port, int baudrate,
Serial::Serial_pimpl::Serial_pimpl (const std::string &port, int baudrate,
long timeout, bytesize_t bytesize,
parity_t parity, stopbits_t stopbits,
flowcontrol_t flowcontrol)
: port(port), baudrate(baudrate), timeout(timeout), bytesize(bytesize),
parity(parity), stopbits(stopbits), flowcontrol(flowcontrol)
{
this->fd = -1;
}
Serial_pimpl::~Serial_pimpl () {
Serial::Serial_pimpl::~Serial_pimpl () {
if (this->isOpen())
this->close();
}
void
Serial::Serial_pimpl::open () {
}
void
Serial_pimpl::open () {
}
void
Serial_pimpl::close () {
Serial::Serial_pimpl::close () {
this->fd = -1;
}
bool
Serial_pimpl::isOpen () {
Serial::Serial_pimpl::isOpen () {
return false;
}
size_t
Serial_pimpl::read (unsigned char* buffer, size_t size = 1) {
Serial::Serial_pimpl::read (unsigned char* buffer, size_t size) {
return 0;
}
std::string
Serial::Serial_pimpl::read (size_t size) {
return "";
}
size_t
Serial::Serial_pimpl::read (std::string &buffer, size_t size) {
return 0;
}
size_t
Serial::Serial_pimpl::write (unsigned char* data, size_t length) {
return 0;
}
size_t
Serial::Serial_pimpl::write (const std::string &data) {
return 0;
}
void
Serial::Serial_pimpl::setPort (const std::string &port) {
}
std::string
Serial_pimpl::read (size_t size = 1) {
}
size_t
Serial_pimpl::read (std::string &buffer, size_t size = 1) {
}
size_t
Serial_pimpl::write (unsigned char* data, size_t length) {
}
size_t
Serial_pimpl::write (const std::string &data) {
Serial::Serial_pimpl::getPort () const {
return this->port;
}
void
Serial_pimpl::setPort (const std::string &port) {
}
std::string
Serial_pimpl::getPort () const {
}
void
Serial_pimpl::setTimeout (long timeout) {
Serial::Serial_pimpl::setTimeout (long timeout) {
}
long
Serial_pimpl::getTimeout () const {
Serial::Serial_pimpl::getTimeout () const {
return this->timeout;
}
void
Serial_pimpl::setBaudrate (int baudrate) {
Serial::Serial_pimpl::setBaudrate (int baudrate) {
}
int
Serial_pimpl::getBaudrate () const {
Serial::Serial_pimpl::getBaudrate () const {
return this->baudrate;
}
void
Serial_pimpl::setBytesize (bytesize_t bytesize) {
Serial::Serial_pimpl::setBytesize (bytesize_t bytesize) {
}
bytesize_t
Serial_pimpl::getBytesize () const {
Serial::Serial_pimpl::getBytesize () const {
return this->bytesize;
}
void
Serial_pimpl::setParity (parity_t parity) {
Serial::Serial_pimpl::setParity (parity_t parity) {
}
parity_t
Serial_pimpl::getParity () const {
Serial::Serial_pimpl::getParity () const {
return this->parity;
}
void
Serial_pimpl::setStopbits (stopbits_t stopbits) {
Serial::Serial_pimpl::setStopbits (stopbits_t stopbits) {
}
stopbits_t
Serial_pimpl::getStopbits () const {
Serial::Serial_pimpl::getStopbits () const {
return this->stopbits;
}
void
Serial_pimpl::setFlowcontrol (flowcontrol_t flowcontrol) {
Serial::Serial_pimpl::setFlowcontrol (flowcontrol_t flowcontrol) {
}
flowcontrol_t
Serial_pimpl::getFlowcontrol () const {
Serial::Serial_pimpl::getFlowcontrol () const {
return this->flowcontrol;
}

View File

@ -6,127 +6,128 @@
#include "serial/impl/unix.h"
#endif
using namespace serial;
Serial::Serial (const std::string &port, int baudrate,
long timeout, bytesize_t bytesize,
parity_t parity, stopbits_t stopbits,
flowcontrol_t flowcontrol)
: impl(new Serial_pimpl(port,baudrate,timeout,bytesize,parity,stopbits,
flowcontrol))
{
pimpl = new Serial_pimpl(port,baudrate,timeout,bytesize,parity,stopbits,
flowcontrol);
}
Serial::~Serial () {
delete impl;
delete pimpl;
}
void
Serial::open () {
this->impl->open ();
this->pimpl->open ();
}
void
Serial::close () {
this->impl->close ();
this->pimpl->close ();
}
bool
Serial::isOpen () {
return this->impl->isOpen ();
return this->pimpl->isOpen ();
}
size_t
Serial::read (unsigned char* buffer, size_t size = 1) {
return this->impl->read (buffer, size);
Serial::read (unsigned char* buffer, size_t size) {
return this->pimpl->read (buffer, size);
}
std::string
Serial::read (size_t size = 1) {
return this->impl->read (size);
Serial::read (size_t size) {
return this->pimpl->read (size);
}
size_t
Serial::read (std::string &buffer, size_t size = 1) {
return this->impl->read (buffer, size);
Serial::read (std::string &buffer, size_t size) {
return this->pimpl->read (buffer, size);
}
size_t
Serial::write (unsigned char* data, size_t length) {
return this->impl->write (data, length);
return this->pimpl->write (data, length);
}
size_t
Serial::write (const std::string &data) {
return this->impl->write (data);
return this->pimpl->write (data);
}
void
Serial::setPort (const std::string &port) {
this->impl->setPort (port);
this->pimpl->setPort (port);
}
std::string
Serial::getPort () const {
return this->impl->getPort ();
return this->pimpl->getPort ();
}
void
Serial::setTimeout (long timeout) {
this->impl->setTimeout (timeout);
this->pimpl->setTimeout (timeout);
}
long
Serial::getTimeout () const {
return this->impl->getTimeout ();
return this->pimpl->getTimeout ();
}
void
Serial::setBaudrate (int baudrate) {
this->impl->setBaudrate (baudrate);
this->pimpl->setBaudrate (baudrate);
}
int
Serial::getBaudrate () const {
return this->impl->getBaudrate ();
return this->pimpl->getBaudrate ();
}
void
Serial::setBytesize (bytesize_t bytesize) {
this->impl->setBytesize (bytesize);
this->pimpl->setBytesize (bytesize);
}
bytesize_t
Serial::getBytesize () const {
return this->impl->getBytesize ();
return this->pimpl->getBytesize ();
}
void
Serial::setParity (parity_t parity) {
this->impl->setParity (parity);
this->pimpl->setParity (parity);
}
parity_t
Serial::getParity () const {
return this->impl->getParity ();
return this->pimpl->getParity ();
}
void
Serial::setStopbits (stopbits_t stopbits) {
this->impl->setStopbits (stopbits);
this->pimpl->setStopbits (stopbits);
}
stopbits_t
Serial::getStopbits () const {
return this->impl->getStopbits ();
return this->pimpl->getStopbits ();
}
void
Serial::setFlowcontrol (flowcontrol_t flowcontrol) {
this->impl->setFlowcontrol (flowcontrol);
this->pimpl->setFlowcontrol (flowcontrol);
}
flowcontrol_t
Serial::getFlowcontrol () const {
return this->impl->getFlowcontrol ();
return this->pimpl->getFlowcontrol ();
}

View File

@ -20,23 +20,35 @@ inline void defaultExceptionCallback(const std::exception &error) {
throw(error);
}
inline bool defaultComparator(const std::string &token) {
return true;
}
using namespace serial;
/***** Listener Class Functions *****/
SerialListener::SerialListener() : listening(false) {
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;
this->info = defaultInfoCallback;
this->debug = defaultDebugCallback;
this->warn = defaultWarningCallback;
this->default_handler = NULL;
// 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"));
// Set default ttl
this->setTimeToLive();
}
SerialListener::~SerialListener() {
@ -48,37 +60,17 @@ SerialListener::~SerialListener() {
void
SerialListener::callback() {
try {
// <filter uuid, token uuid>
std::pair<uuid_type,uuid_type> pair;
DataCallback _callback;
// <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 default handler
if (pair.first.is_nil()) {
if (this->default_handler)
this->default_handler(this->tokens[pair.second]);
// Else use provided callback
} else {
bool go = false;
// Grab the callback as to not hold the mutex while executing
{
boost::mutex::scoped_lock l(callback_mux);
// Make sure the filter hasn't been removed
if ((go = (this->callbacks.count(pair.first) > 0)))
_callback = this->callbacks[pair.first];
}
// Execute callback
if (go)
_callback(this->tokens[pair.second]);
}
pair.first->callback((*pair.second));
} catch (std::exception &e) {
this->handle_exc(e);
}// try callback
} // if listening
// Erase the used and executed callback
this->eraseToken(pair.second);
} // if popped
} // while (this->listening)
} catch (std::exception &e) {
@ -86,12 +78,6 @@ SerialListener::callback() {
}
}
void
SerialListener::setTimeToLive(size_t ms) {
using namespace boost::posix_time;
this->ttl = time_duration(milliseconds(ms));
}
void
SerialListener::startListening(Serial &serial_port) {
if (this->listening) {
@ -121,32 +107,19 @@ SerialListener::stopListening() {
listen_thread.join();
callback_thread.join();
callback_queue.clear();
this->data_buffer = "";
this->tokens.clear();
this->ttls.clear();
this->serial_port = NULL;
// Delete all the filters
this->stopListeningForAll();
}
void
SerialListener::stopListeningForAll() {
boost::mutex::scoped_lock l(filter_mux);
filters.clear();
comparators.clear();
boost::mutex::scoped_lock l2(callback_mux);
callbacks.clear();
callback_queue.clear();
}
size_t
SerialListener::determineAmountToRead() {
// TODO: Make a more intelligent method based on the length of the things
// filters are looking for. i.e.: if the filter is looking for 'V=XX\r'
// filters are looking for. e.g.: if the filter is looking for 'V=XX\r'
// make the read amount at least 5.
return 5;
return this->chunk_size;
}
void
@ -163,130 +136,26 @@ SerialListener::readSomeData(std::string &temp, size_t this_many) {
}
void
SerialListener::addNewTokens(std::vector<std::string> &new_tokens,
std::vector<uuid_type> &new_uuids,
std::string &left_overs)
{
std::vector<std::string>::iterator it_new;
for (it_new=new_tokens.begin(); it_new != new_tokens.end(); it_new++) {
// The last token needs to be put back in the buffer always:
// (in the case that the delimeter is \r)...
// In the case that the string ends with \r the last element will be
// empty (""). In the case that it does not the last element will be
// what is left over from the next message that hasn't sent
// everything. Ex.: "?$1E\r" -> ["?$1E", ""] and
// "?$1E\r$1E=Robo" -> ["?$1E","$1E=Robo"]
if (it_new == new_tokens.end()-1) {
left_overs = (*it_new);
continue;
}
// If the token is empty ignore it
if ((*it_new).length() == 0)
continue;
// Create a new uuid
uuid_type uuid = random_generator();
// Put the new uuid in the list of new uuids
new_uuids.push_back(uuid);
// Create a uuid, token pair
std::pair<const uuid_type,std::string> token_pair(uuid,(*it_new));
// Create a uuid, ttl pair
using namespace boost::posix_time;
std::pair<const uuid_type,ptime>
ttl_pair(uuid,ptime(microsec_clock::local_time()));
// Insert the new pairs
{
boost::mutex::scoped_lock l(token_mux);
this->tokens.insert(token_pair);
ttls.insert(ttl_pair);
}
} // for (it_new=new_tokens.begin(); it_new != new_tokens.end(); it_new++)
}
void
SerialListener::eraseToken(uuid_type &uuid) {
boost::mutex::scoped_lock l(token_mux);
this->tokens.erase(uuid);
this->ttls.erase(uuid);
}
void
SerialListener::eraseTokens(std::vector<uuid_type> &uuids) {
std::vector<uuid_type>::iterator it;
for (it=uuids.begin(); it != uuids.end(); it++) {
this->eraseToken((*it));
}
}
// TODO: Investigate possible race condition where the filter processing takes
// longer than the ttl
void
SerialListener::filterNewTokens(std::vector<uuid_type> new_uuids) {
SerialListener::filterNewTokens (std::vector<TokenPtr> new_tokens) {
// Iterate through the filters, checking each against new tokens
std::vector<std::pair<uuid_type,uuid_type> > tbd;
boost::mutex::scoped_lock l(filter_mux);
std::vector<uuid_type>::iterator it;
boost::mutex::scoped_lock lock(filter_mux);
std::vector<FilterPtr>::iterator it;
for (it=filters.begin(); it!=filters.end(); it++) {
std::vector<std::pair<uuid_type,uuid_type> > temp =
this->filter((*it), new_uuids);
if (temp.size() > 0)
tbd.insert(tbd.end(), temp.begin(), temp.end());
this->filter((*it), new_tokens);
} // for (it=filters.begin(); it!=filters.end(); it++)
// Dispatch
std::vector<std::pair<uuid_type,uuid_type> >::iterator it_tbd;
for (it_tbd = tbd.begin(); it_tbd != tbd.end(); it_tbd++) {
callback_queue.push((*it_tbd));
}
}
// <filter,token>
std::vector<std::pair<uuid_type,uuid_type> >
SerialListener::filter(uuid_type filter_uuid,
std::vector<uuid_type> &token_uuids)
{
std::vector<uuid_type> to_be_erased;
std::vector<std::pair<uuid_type,uuid_type> > to_be_dispatched;
// Iterate through the token uuids and run each against the filter
std::vector<uuid_type>::iterator it;
for (it=token_uuids.begin(); it!=token_uuids.end(); it++) {
bool matched = false;
uuid_type token_uuid = (*it);
if (this->comparators[filter_uuid](this->tokens[token_uuid])) {
matched = true;
to_be_dispatched.push_back(std::make_pair(filter_uuid,token_uuid));
}
}
return to_be_dispatched;
}
// <filter_ptr,token_ptr>
void
SerialListener::pruneTokens() {
// Iterate through the buffered tokens
std::vector<uuid_type> to_be_erased;
std::map<const uuid_type,std::string>::iterator it;
{
boost::mutex::scoped_lock l(token_mux);
for (it = this->tokens.begin(); it != this->tokens.end(); it++) {
uuid_type uuid = (*it).first;
using namespace boost::posix_time;
// If the current time - the creation time is greater than the ttl,
// then prune it
if (ptime(microsec_clock::local_time())-this->ttls[uuid] > this->ttl) {
// If there is a default handler pass it on
if (this->default_handler) {
boost::mutex::scoped_lock l(callback_mux);
callback_queue.push(std::make_pair(nil_generator(),uuid));
} else {
// Otherwise delete it
to_be_erased.push_back(uuid);
}
}
}
SerialListener::filter (FilterPtr filter, std::vector<TokenPtr> &tokens)
{
// Iterate through the token uuids and run each against the filter
std::vector<TokenPtr>::iterator it;
for (it=tokens.begin(); it!=tokens.end(); it++) {
TokenPtr token = (*it);
if (filter->comparator((*token)))
callback_queue.push(std::make_pair(filter,token));
}
// Remove any lines that need to be erased
// (this must be done outside the iterator to prevent problems incrementing
// the iterator)
this->eraseTokens(to_be_erased);
}
void
@ -302,17 +171,11 @@ SerialListener::listen() {
// Add the new data to the buffer
this->data_buffer += temp;
// Call the tokenizer on the updated buffer
std::vector<std::string> new_tokens;
std::vector<TokenPtr> new_tokens;
this->tokenize(this->data_buffer, new_tokens);
// Add the new tokens to the new token buffer, get a list of new uuids
// to filter once, and put left_overs in the data buffer.
std::vector<uuid_type> new_uuids;
this->addNewTokens(new_tokens, new_uuids, this->data_buffer);
// Run the new tokens through existing filters
this->filterNewTokens(new_uuids);
this->filterNewTokens(new_tokens);
}
// Look for old data and pass to the default handler or delete
this->pruneTokens();
// Done parsing lines and buffer should now be set to the left overs
} // while (this->listening)
} catch (std::exception &e) {
@ -320,154 +183,64 @@ SerialListener::listen() {
}
}
/***** Filter Functions *****/
FilterPtr
SerialListener::listenFor(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;
}
typedef boost::shared_ptr<boost::condition_variable> shared_cond_var_ptr_t;
inline void
listenForOnceCallback(const std::string &token,
shared_cond_var_ptr_t cond,
boost::shared_ptr<std::string> result)
{
(*result) = token;
cond->notify_all();
}
std::string
SerialListener::listenForOnce(ComparatorType comparator, size_t ms) {
boost::shared_ptr<std::string> result(new std::string(""));
shared_cond_var_ptr_t cond(new boost::condition_variable());
boost::mutex mutex;
DataCallback callback = boost::bind(listenForOnceCallback,_1,cond,result);
FilterPtr filter_id = this->listenFor(comparator, callback);
boost::unique_lock<boost::mutex> lock(mutex);
cond->timed_wait(lock, boost::posix_time::milliseconds(ms)));
this->stopListeningFor(filter_id);
// If the callback never got called then result will be "" because tokens
// can never be ""
return (*result);
}
bool
SerialListener::listenForStringOnce(std::string token, size_t milliseconds) {
return this->listenForOnce(exactly(token), milliseconds);
return this->listenForOnce(exactly(token), milliseconds) == token;
}
void
SerialListener::notifyListenForOnce(shared_cond_var_ptr_t cond_ptr) {
cond_ptr->notify_all();
}
bool
SerialListener::listenForOnce(ComparatorType comparator, size_t ms)
{
shared_cond_var_ptr_t cond_ptr(new boost::condition_variable());
boost::mutex mut;
// Create blocking filter
const uuid_type uuid = random_generator();
{
boost::mutex::scoped_lock l(filter_mux);
filters.push_back(uuid);
comparators.insert(std::make_pair(uuid,comparator));
}
{
boost::mutex::scoped_lock l(callback_mux);
callbacks.insert(std::make_pair(uuid,
boost::bind(&SerialListener::notifyListenForOnce, this, cond_ptr)));
}
// Run this filter through all tokens onces
std::vector<uuid_type> token_uuids;
std::map<const uuid_type,std::string>::iterator it;
for (it = tokens.begin(); it != tokens.end(); it++)
token_uuids.push_back((*it).first);
std::vector<std::pair<uuid_type,uuid_type> > pairs =
this->filter(uuid, token_uuids);
// If there is at least one
if (pairs.size() > 0) {
// If there is more than one find the oldest
size_t index = 0;
if (pairs.size() > 1) {
using namespace boost::posix_time;
ptime oldest_time = ttls[pairs[index].second];
size_t i = 0;
std::vector<std::pair<uuid_type,uuid_type> >::iterator it;
for (it = pairs.begin(); it != pairs.end(); it++) {
if (ttls[(*it).second] < oldest_time) {
oldest_time = ttls[(*it).second];
index = i;
}
i++;
}
}
// Either way put the final index into the callback queue
callback_queue.push(pairs[index]);
}
bool result = false;
// Wait
boost::unique_lock<boost::mutex> lock(mut);
using namespace boost::posix_time;
if (cond_ptr->timed_wait(lock, milliseconds(ms)))
result = true;
// Destroy the filter
{
boost::mutex::scoped_lock l(filter_mux);
filters.erase(std::find(filters.begin(),filters.end(),uuid));
comparators.erase(uuid);
}
{
boost::mutex::scoped_lock l(callback_mux);
callbacks.erase(uuid);
}
return result;
}
uuid_type
SerialListener::listenFor(ComparatorType comparator, DataCallback callback)
{
// Create Filter
uuid_type uuid = random_generator();
std::pair<const uuid_type,ComparatorType>
comparator_pair(uuid, comparator);
std::pair<const uuid_type,DataCallback>
callback_pair(uuid, callback);
{
boost::mutex::scoped_lock l(filter_mux);
filters.push_back(uuid);
comparators.insert(comparator_pair);
}
{
boost::mutex::scoped_lock l(callback_mux);
callbacks.insert(callback_pair);
}
// Run this filter through all tokens onces
std::vector<uuid_type> token_uuids;
std::map<const uuid_type,std::string>::iterator it;
for (it = tokens.begin(); it != tokens.end(); it++)
token_uuids.push_back((*it).first);
std::vector<std::pair<uuid_type,uuid_type> > pairs =
this->filter(uuid, token_uuids);
// Dispatch
std::vector<std::pair<uuid_type,uuid_type> >::iterator it_cb;
for (it_cb = pairs.begin(); it_cb != pairs.end(); it_cb++) {
callback_queue.push((*it_cb));
}
return uuid;
}
void
SerialListener::stopListeningFor(uuid_type filter_uuid) {
// Delete filter
SerialListener::stopListeningFor(FilterPtr filter_ptr) {
boost::mutex::scoped_lock l(filter_mux);
filters.erase(std::find(filters.begin(),filters.end(),filter_uuid));
comparators.erase(filter_uuid);
boost::mutex::scoped_lock l2(callback_mux);
callbacks.erase(filter_uuid);
}
TokenizerType
SerialListener::delimeter_tokenizer (std::string delimeter) {
return boost::bind(&SerialListener::_delimeter_tokenizer,
_1, _2, delimeter);
filters.erase(std::find(filters.begin(),filters.end(),filter_ptr));
}
void
SerialListener::_delimeter_tokenizer (std::string &data,
std::vector<std::string> &tokens,
std::string delimeter)
{
boost::split(tokens, data, boost::is_any_of(delimeter));
}
ComparatorType
SerialListener::exactly(std::string exact_str) {
return boost::bind(&SerialListener::_exactly, _1, exact_str);
}
bool
SerialListener::_exactly(const std::string &token, std::string exact_str) {
return token == exact_str;
SerialListener::stopListeningForAll() {
boost::mutex::scoped_lock l(filter_mux);
filters.clear();
callback_queue.clear();
}

View File

@ -22,8 +22,7 @@ class SerialListenerTests : public ::testing::Test {
protected:
virtual void SetUp() {
listener.listening = true;
listener.setTimeToLive(10);
listener.default_handler = default_handler;
listener.setDefaultHandler(default_handler);
listener.callback_thread =
boost::thread(boost::bind(&SerialListener::callback, &listener));
}
@ -49,13 +48,9 @@ protected:
}
void simulate_loop(std::string input_str) {
std::vector<std::string> new_tokens;
std::vector<TokenPtr> new_tokens;
listener.tokenize(input_str, new_tokens);
std::vector<uuid_type> new_uuids;
listener.addNewTokens(new_tokens, new_uuids, listener.data_buffer);
listener.filterNewTokens(new_uuids);
boost::this_thread::sleep(boost::posix_time::milliseconds(11));
listener.pruneTokens();
listener.filterNewTokens(new_tokens);
}
SerialListener listener;
@ -132,7 +127,7 @@ TEST_F(SerialListenerTests, listenForWorks) {
global_count = 0;
global_listen_count = 0;
boost::uuids::uuid filt_uuid =
FilterPtr filt_uuid =
listener.listenFor(listenForComparator, listenForCallback);
simulate_loop("\r+\rV=05:06\r?$1E\rV=06:05\r$1E=Robo");