Skip to content
Snippets Groups Projects
Commit 3b391ca7 authored by Joseph Mirabel's avatar Joseph Mirabel Committed by Olivier Stasse
Browse files

Add real time logger

parent 9c2530f1
No related branches found
No related tags found
No related merge requests found
......@@ -28,6 +28,7 @@ fwd.hh
null-ptr.hh
contiifstream.h
debug.h
real-time-logger.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})
ADD_LIBRARY(${LIBRARY_NAME}
SHARED
debug/debug.cpp
debug/real-time-logger.cpp
dgraph/entity.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)
DYNAMIC_GRAPH_TEST(signal-time-dependent)
DYNAMIC_GRAPH_TEST(value)
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();
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment