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

Serial Listener changes compile against the example reference, time to merge with John.

This commit is contained in:
William Woodall 2012-01-11 23:42:42 -06:00
parent 7c0c976033
commit dfd1837cfc
3 changed files with 276 additions and 247 deletions

View File

@ -36,10 +36,10 @@ int main(void) {
// Method #2: // Method #2:
// comparator - blocking // comparator - blocking
{ {
BlockingFilter f2 = BlockingFilterPtr f2 =
listener.createBlockingFilter(SerialListener::endsWith("post")); listener.createBlockingFilter(SerialListener::endsWith("post"));
for (size_t i = 0; i < 3; i++) { for (size_t i = 0; i < 3; i++) {
std::string token = f2.wait(100); // Wait for 100 ms or a matched token std::string token = f2->wait(100); // Wait for 100 ms or a matched token
if (token == "") if (token == "")
std::cout << "Found something ending with 'post'" << std::endl; std::cout << "Found something ending with 'post'" << std::endl;
else else
@ -54,18 +54,18 @@ int main(void) {
// comparator, token buffer size - blocking // comparator, token buffer size - blocking
{ {
// Give it a comparator, then a buffer size of 10 // Give it a comparator, then a buffer size of 10
BufferedFilter f3 = BufferedFilterPtr f3 =
listener.createBufferedFilter(SerialListener::contains("substr"), 10); listener.createBufferedFilter(SerialListener::contains("substr"), 10);
SerialListener::sleep(75); // Sleep 75ms, should have about 7 SerialListener::sleep(75); // Sleep 75ms, should have about 7
std::cout << "Caught " << f3.count(); std::cout << "Caught " << f3->count();
std::cout << " tokens containing 'substr'" << std::endl; std::cout << " tokens containing 'substr'" << std::endl;
for(size_t i = 0; i < 20; ++i) { for(size_t i = 0; i < 20; ++i) {
std::string token = f3.wait(5); // Pull message from the buffer std::string token = f3->wait(5); // Pull message from the buffer
if (token == "") // If an empty string is returned, a timeout occured if (token == "") // If an empty string is returned, a timeout occured
break; break;
} }
f3.clear(); // Empties the buffer f3->clear(); // Empties the buffer
if (f3.wait(0) == "") // Non-blocking wait if (f3->wait(0) == "") // Non-blocking wait
std::cout << "We won the race condition!" << std::endl; std::cout << "We won the race condition!" << std::endl;
else else
std::cout << "We lost the race condition..." << std::endl; std::cout << "We lost the race condition..." << std::endl;

View File

@ -178,160 +178,31 @@ public:
*/ */
typedef boost::shared_ptr<Filter> FilterPtr; typedef boost::shared_ptr<Filter> FilterPtr;
/*! class BlockingFilter;
* 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 * Shared Pointer of BlockingFilter, returned by
* match is found. It will also buffer up to a given buffer size of tokens so * SerialListener::createBlockingFilter.
* 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 * \see serial::BlockingFilter, SerialListener::createBlockingFilter
* 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 typedef boost::shared_ptr<BlockingFilter> BlockingFilterPtr;
{
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 () { class 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 * Shared Pointer of BufferedFilter, returned by
* available in the buffer. If a token is matched it is returned, otherwise * SerialListener::createBufferedFilter.
* an empty string is returned. *
* * \see serial::BufferedFilter, SerialListener::createBufferedFilter
* \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 typedef boost::shared_ptr<BufferedFilter> BufferedFilterPtr;
* 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. * This is a general exception generated by the SerialListener class.
* *
* Check the SerialListenerException::what function for the cause. * Check the SerialListenerException::what function for the cause.
*
* \param e_what is a std::string that describes the cause of the error. * \param e_what is a std::string that describes the cause of the error.
*/ */
class SerialListenerException : public std::exception { class SerialListenerException : public std::exception {
@ -489,98 +360,115 @@ public:
/***** Filter Functions ******/ /***** Filter Functions ******/
/*! /*!
* Setups up a filter that calls a callback when a comparator returns true. * 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 * 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 * 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 * 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. * match, the callback is called with the resulting line.
* *
* \param comparator This is a comparator for detecting if a line matches. * \param comparator This is a comparator for detecting if a line matches.
* The comparartor receives a std::string reference and must return a true * The comparartor receives a std::string reference and must return a true
* if it matches and false if it doesn't. * if it matches and false if it doesn't.
* *
* \param callback This is the handler for when a match occurs. It is given * \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. * a std::string reference of the line that matched your comparator.
* *
* \return boost::shared_ptr<Filter> so you can remove it later. * \return boost::shared_ptr<Filter> so you can remove it later.
* *
* \see SerialListener::stopListeningFor * \see SerialListener::removeFilter
*/ */
FilterPtr FilterPtr
listenFor (ComparatorType comparator, DataCallback callback); createFilter (ComparatorType comparator, DataCallback callback);
/*! /*!
* Blocks until the comparator returns true or until the timeout occurs. * Creates a BlockingFilter which blocks until the comparator returns true.
* *
* \param comparator ComparatorType function that should return true if the * The user provides a comparator, and every time a line is
* given std::string matches otherwise false. * 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 timeout in milliseconds before timing out and returning false. * \param comparator This is a comparator for detecting if a line matches.
* Defaults to 1000 milliseconds or 1 second. * The comparartor receives a std::string reference and must return a true
* if it matches and false if it doesn't.
* *
* \return std::string the token that was matched, returns an empty string * \return BlockingFilterPtr So you can call BlockingFilter::wait on it.
* if the timeout occurs first. *
* i.e. if (listenForOnce(...) != "") // Got match * \see SerialListener::removeFilter, serial::BlockingFilter,
* serial::BlockingFilterPtr
*/ */
std::string BlockingFilterPtr
listenForOnce (ComparatorType comparator, size_t timeout = 1000); createBlockingFilter (ComparatorType comparator);
/*! /*!
* Writes to the seiral port then blocks until the comparator returns true * Creates a BlockingFilter blocks until the comparator returns true.
* or until the timeout occurs.
* *
* This function creates a filter, writes the data, then waits for it to * The user provides a comparator, and every time a line is
* match atleast once. * 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 to_be_written const std::string reference of data to be written to * \param comparator This is a comparator for detecting if a line matches.
* the serial port. * The comparartor receives a std::string reference and must return a true
* if it matches and false if it doesn't.
* *
* \param comparator ComparatorType function that should return true if the * \param buffer_size This is the number of tokens to be buffered by the
* given std::string matches otherwise false. * BufferedFilter, defaults to 1024.
* *
* \param timeout in milliseconds before timing out and returning false. * \return BlockingFilter So you can call BlockingFilter::wait on it.
* Defaults to 1000 milliseconds or 1 second.
* *
* \return std::string the token that was matched, returns an empty string * \see SerialListener::removeFilter, serial::BufferedFilter,
* if the timeout occurs first. * serial::BufferedFilterPtr
* i.e. if (listenForOnce(...) != "") // Got match
*/ */
std::string BufferedFilterPtr
listenForOnce (ComparatorType comparator, size_t timeout = 1000); createBufferedFilter (ComparatorType comparator, size_t buffer_size = 1024);
/*! /*!
* Blocks until the given string is detected or until the timeout occurs. * Removes a filter by a given FilterPtr.
* *
* \param token std::string that should be watched for, this string must * \param filter_ptr A shared pointer to the filter to be removed.
* match the message exactly.
* *
* \param timeout in milliseconds before timing out and returning false. * \see SerialListener::createFilter
* 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_ptr A shared pointer to the filter.
*
* \see SerialListener::listenFor
*/
void
stopListeningFor (FilterPtr filter_ptr);
/*!
* Stops listening for anything, but doesn't stop reading the serial port.
*/ */
void void
stopListeningForAll (); 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 ******/ /***** Hooks and Handlers ******/
@ -912,6 +800,156 @@ private:
}; };
} /*!
* 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;
}
FilterPtr filter_ptr;
void callback(const std::string& token) {
this->cond.notify_all();
this->result = token;
}
private:
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(&BufferedFilter::callback, this, _1);
this->filter_ptr = 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(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;
}
FilterPtr filter_ptr;
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);
}
private:
size_t buffer_size;
boost::shared_ptr<SerialListener> listener;
ConcurrentQueue<std::string> queue;
std::string result;
};
} // namespace serial
#endif // SERIAL_LISTENER_H #endif // SERIAL_LISTENER_H

View File

@ -111,7 +111,7 @@ SerialListener::stopListening() {
this->serial_port = NULL; this->serial_port = NULL;
// Delete all the filters // Delete all the filters
this->stopListeningForAll(); this->removeAllFilters();
} }
size_t size_t
@ -186,7 +186,8 @@ SerialListener::listen() {
/***** Filter Functions *****/ /***** Filter Functions *****/
FilterPtr FilterPtr
SerialListener::listenFor(ComparatorType comparator, DataCallback callback) { SerialListener::createFilter(ComparatorType comparator, DataCallback callback)
{
FilterPtr filter_ptr(new Filter(comparator, callback)); FilterPtr filter_ptr(new Filter(comparator, callback));
boost::mutex::scoped_lock l(filter_mux); boost::mutex::scoped_lock l(filter_mux);
@ -195,50 +196,40 @@ SerialListener::listenFor(ComparatorType comparator, DataCallback callback) {
return filter_ptr; return filter_ptr;
} }
typedef boost::shared_ptr<boost::condition_variable> shared_cond_var_ptr_t; BlockingFilterPtr
SerialListener::createBlockingFilter(ComparatorType comparator) {
return BlockingFilterPtr(
new BlockingFilter(comparator, boost::shared_ptr<SerialListener>(this)));
}
inline void BufferedFilterPtr
listenForOnceCallback(const std::string &token, SerialListener::createBufferedFilter(ComparatorType comparator,
shared_cond_var_ptr_t cond, size_t buffer_size)
boost::shared_ptr<std::string> result)
{ {
(*result) = token; return BufferedFilterPtr(
cond->notify_all(); new BufferedFilter(comparator,
} buffer_size,
boost::shared_ptr<SerialListener>(this)));
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) == token;
} }
void void
SerialListener::stopListeningFor(FilterPtr filter_ptr) { SerialListener::removeFilter(FilterPtr filter_ptr) {
boost::mutex::scoped_lock l(filter_mux); boost::mutex::scoped_lock l(filter_mux);
filters.erase(std::find(filters.begin(),filters.end(),filter_ptr)); filters.erase(std::find(filters.begin(),filters.end(),filter_ptr));
} }
void void
SerialListener::stopListeningForAll() { 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); boost::mutex::scoped_lock l(filter_mux);
filters.clear(); filters.clear();
callback_queue.clear(); callback_queue.clear();