Commit 3b391ca7 authored by Joseph Mirabel's avatar Joseph Mirabel Committed by Olivier Stasse
Browse files

Add real time logger

parent 9c2530f1
...@@ -28,6 +28,7 @@ fwd.hh ...@@ -28,6 +28,7 @@ fwd.hh
null-ptr.hh null-ptr.hh
contiifstream.h contiifstream.h
debug.h debug.h
real-time-logger.h
dynamic-graph-api.h dynamic-graph-api.h
......
// -*- mode: c++ -*-
// Copyright 2018, Joseph Mirabel LAAS-CNRS
//
// This file is part of dynamic-graph.
// dynamic-graph is free software: you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// dynamic-graph is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details. You should have
// received a copy of the GNU Lesser General Public License along with
// dynamic-graph. If not, see <http://www.gnu.org/licenses/>.
#ifndef DYNAMIC_GRAPH_LOGGER_REAL_TIME_H
# define DYNAMIC_GRAPH_LOGGER_REAL_TIME_H
# include <sstream>
# include <vector>
# include <boost/circular_buffer.hpp>
# include <boost/shared_ptr.hpp>
# include <dynamic-graph/config.hh>
namespace dynamicgraph
{
/// \ingroup debug
///
/// \brief Stream for the real-time logger.
class LoggerStream
{
public:
virtual void write (const char* c) = 0;
};
class LoggerIOStream : public LoggerStream
{
public:
LoggerIOStream (std::ostream& os) : os_ (os) {}
virtual void write (const char* c) { os_ << c; }
private:
std::ostream& os_;
};
typedef boost::shared_ptr <LoggerStream> LoggerStreamPtr_t;
class RealTimeLogger;
class RTLoggerStream
{
public:
RTLoggerStream (RealTimeLogger* logger, std::ostream& os) : logger_(logger), os_ (os) {}
template <typename T> RTLoggerStream& operator<< (T t) { os_ << t; return *this; }
~RTLoggerStream();
private:
RealTimeLogger* logger_;
std::ostream& os_;
};
/// \ingroup debug
///
/// \brief Main class of the real-time logger.
class DYNAMIC_GRAPH_DLLAPI RealTimeLogger
{
public:
/// \todo add an argument to preallocate the internal string to a given size.
RealTimeLogger (const std::size_t& bufferSize);
inline void clearOutputStreams () { outputs_.clear(); }
inline void addOutputStream (const LoggerStreamPtr_t& os) { outputs_.push_back(os); }
/// The function to be called by the thread who exports the outputs
//void spin ();
/// Write next message to output.
/// It does nothing if the buffer is empty.
/// \return true if it wrote something
bool spinOnce ();
/// Return an object onto which a real-time thread can write.
/// The message is considered finished when the object is destroyed.
RTLoggerStream front();
inline void frontReady() { backIdx_ = (backIdx_+1) % buffer_.size(); }
inline bool empty () const
{
return frontIdx_ == backIdx_;
}
inline bool full () const
{
return ((backIdx_ + 1) % buffer_.size()) == frontIdx_;
}
inline std::size_t size () const
{
if (frontIdx_ <= backIdx_)
return backIdx_ - frontIdx_;
else
return backIdx_ + buffer_.size() - frontIdx_;
}
inline std::size_t getBufferSize () { return buffer_.capacity(); }
~RealTimeLogger ();
private:
struct Data {
std::stringbuf buf;
};
std::vector<LoggerStreamPtr_t> outputs_;
std::vector<Data*> buffer_;
/// Index of the next value to be read.
std::size_t frontIdx_;
/// Index of the slot where to write next value (does not contain valid data).
std::size_t backIdx_;
std::ostream oss_;
};
} // end of namespace dynamicgraph
#endif //! DYNAMIC_GRAPH_LOGGER_REAL_TIME_H
...@@ -40,6 +40,7 @@ link_directories(${Boost_LIBRARY_DIRS}) ...@@ -40,6 +40,7 @@ link_directories(${Boost_LIBRARY_DIRS})
ADD_LIBRARY(${LIBRARY_NAME} ADD_LIBRARY(${LIBRARY_NAME}
SHARED SHARED
debug/debug.cpp debug/debug.cpp
debug/real-time-logger.cpp
dgraph/entity.cpp dgraph/entity.cpp
dgraph/factory.cpp dgraph/factory.cpp
......
/*
* Copyright 2018,
* Joseph Mirabel
*
* LAAS-CNRS
*
* This file is part of dynamic-graph.
* dynamic-graph is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* dynamic-graph is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. You should
* have received a copy of the GNU Lesser General Public License along
* with dynamic-graph. If not, see <http://www.gnu.org/licenses/>.
*/
#include <dynamic-graph/real-time-logger.h>
namespace dynamicgraph
{
RealTimeLogger::RealTimeLogger (const std::size_t& bufferSize)
: buffer_(bufferSize, NULL)
, oss_ (NULL)
{
for (std::size_t i = 0; i < buffer_.size(); ++i)
buffer_[i] = new Data;
}
RealTimeLogger::~RealTimeLogger ()
{
// Check that we are not spinning...
for (std::size_t i = 0; i < buffer_.size(); ++i) delete buffer_[i];
}
bool RealTimeLogger::spinOnce ()
{
if (empty()) return false;
Data* data = buffer_[frontIdx_];
frontIdx_ = (frontIdx_ + 1) % buffer_.size();
std::string str = data->buf.str();
// It is important to pass str.c_str() and not str
// because the str object may contains a '\0' so
// str.size() may be different from strlen(str.c_str())
for (std::size_t i = 0; i < outputs_.size(); ++i)
outputs_[i]->write (str.c_str());
return true;
}
RTLoggerStream RealTimeLogger::front ()
{
if (outputs_.empty() || full()) {
oss_.rdbuf(NULL);
return RTLoggerStream (NULL, oss_);
}
Data* data = buffer_[backIdx_];
//backIdx_ = (backIdx_+1) % buffer_.size();
// Reset position of cursor
data->buf.pubseekpos(0);
oss_.rdbuf(&data->buf);
return RTLoggerStream (this, oss_);
}
RTLoggerStream::~RTLoggerStream()
{
os_ << std::ends;
if (logger_ != NULL) logger_->frontReady();
}
}
...@@ -67,3 +67,4 @@ DYNAMIC_GRAPH_TEST(pool) ...@@ -67,3 +67,4 @@ DYNAMIC_GRAPH_TEST(pool)
DYNAMIC_GRAPH_TEST(signal-time-dependent) DYNAMIC_GRAPH_TEST(signal-time-dependent)
DYNAMIC_GRAPH_TEST(value) DYNAMIC_GRAPH_TEST(value)
DYNAMIC_GRAPH_TEST(signal-ptr) DYNAMIC_GRAPH_TEST(signal-ptr)
DYNAMIC_GRAPH_TEST(real-time-logger)
/*
* Copyright 2018,
* Joseph Mirabel
*
* LAAS-CNRS
*
* This file is part of dynamic-graph.
* dynamic-graph is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* dynamic-graph is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. You should
* have received a copy of the GNU Lesser General Public License along
* with dynamic-graph. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <dynamic-graph/real-time-logger.h>
#define BOOST_TEST_MODULE real_time_logger
#include <boost/test/unit_test.hpp>
#include <boost/test/output_test_stream.hpp>
#include <boost/thread.hpp>
#include <boost/thread/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace dynamicgraph;
BOOST_AUTO_TEST_CASE (monothread)
{
RealTimeLogger rtl (10);
rtl.addOutputStream (LoggerStreamPtr_t (new LoggerIOStream(std::cout)));
for (int i = 0; i < 9; ++i) rtl.front() << "Call number " << i << '\n';
BOOST_CHECK (rtl.full());
rtl.front() << "This call should not appear in the output" << '\n';
rtl.spinOnce();
BOOST_CHECK (!rtl.full());
rtl.front() << "This call should appear in the output" << '\n';
int spinNb = 0;
while (rtl.spinOnce()) { spinNb++; }
BOOST_CHECK(spinNb == 9);
rtl.front() << "This msg should be short." << '\n';
rtl.spinOnce();
}
bool requestShutdown = false;
void spin (RealTimeLogger* logger)
{
while (!requestShutdown || !logger->empty())
{
// If the logger did not write anything, it means the buffer is empty.
// Do a pause
if (!logger->spinOnce())
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
}
}
BOOST_AUTO_TEST_CASE (multithread)
{
RealTimeLogger rtl (10);
rtl.addOutputStream (LoggerStreamPtr_t (new LoggerIOStream(std::cout)));
boost::thread loggerThread (spin, &rtl);
for (int i = 0; i < 10; ++i) {
boost::this_thread::sleep(boost::posix_time::milliseconds(20));
rtl.front() << "Call number " << i << '\n';
BOOST_CHECK (!rtl.full());
}
rtl.front() << "This call should appear in the output" << '\n';
requestShutdown = true;
loggerThread.join();
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment