mc_rtc::Configuration
general purpose configurationFile logging is enabled/disable through the global
configuration. However, a controller implementation does
not need to worry about the logging options and will go through a single
interface for all file logging operations. Each controller instance uses a
separate log and each time a controller switch happens, a new log is created.
For convenience purpose, on platforms that support symbolic links, a latest
log file is created pointing to the latest created log.
By default, the following information are logged by all controllers:
Add log entries in your controller’s code:
logger().addLogEntry("entry_name", [this]() { /* compute, say x; */ return x; });
The provided callback will run after your controller run()
method.
You can log most types that your controller could manipulate as-is (e.g. sva::PTransformd
or sva::ForceVecd
) and strings. You can also logs vectors of these types.
Normally, an addLogEntry
call should have a corresponding removeLogEntry
call, e.g.:
logger().removeLogEntry("entry_name");
You can specify a logging source:
// In this example, "this" is the source
logger().addLogEntry("entry_A", this, [this]() { return a; });
logger().addLogEntry("entry_B", this, [this]() { return b; });
logger().addLogEntry("entry_C", this, [this]() { return c; });
// You can also group calls with the same source
logger().addLogEntries(this,
"entry_A", [this]() { return a; },
"entry_B", [this]() { return b; },
"entry_C", [this]() { return c; });
Then all the entries can be removed at once:
logger().removeLogEntries(this);
// This would yield the same result but you would need to keep the addLogEntry and removeLogEntry call in sync
logger().removeLogEntry("entry_A");
logger().removeLogEntry("entry_B");
logger().removeLogEntry("entry_C");
As seen above, a common pattern is to load a member or method called on this
instance, this can be done in a simple way. The main benefit of this approach is to avoid the performance caveat highlighted below.
struct MyObject
{
Eigen::Vector3d data_;
sva::PTransformd something_;
Eigen::Vector6d computeVector();
const sva::PTransformd & referenceBody();
void referenceBody(const std::string &);
void addToLogger(mc_rtc::Logger & logger)
{
// This is the actual syntax required in C++11
logger.addLogEntry<decltype(&MyObject::data_), &MyObject::data_>("data", this);
// We have the following macro helper
MC_RTC_LOG_HELPER("something", something_);
// Also works with methods
MC_RTC_LOG_HELPER("computeVector", computeVector);
// But for overloaded methods we must use another macro to work-around ambiguity issues
MC_RTC_LOG_GETTER("referenceBody", referenceBody);
}
};
The MC_RTC_LOG_HELPER
and MC_RTC_LOG_GETTER
make the following two assumptions:
this
is the logging sourcelogger
is the logger instance you are adding the data toThe Python interface is very similar:
# Add a log entry
self.logger().addLogEntry("my_entry", lambda: self.data)
# Remove data
self.logger().removeLogEntry("my_entry")
# Using partial to invoke a controller method
self.logger().addLogEntry("my_entry", partial(self.get_data))
By default, lambda expressions return by value. When possible, you might want to return a const reference to the object you are saving:
logger().addLogEntry("entry", [this]() -> const sva::PTransformd & { return trans_; }
Your controller will show the following message telling you what the latest log is, the message should look like the following:
Will log controller outputs to "/tmp/mc-control-MyControllerName-DATE.bin"
On systems that support symbolic links, it will also be available as /tmp/mc-control-MyControllerName-latest.bin
See the following pages for tools that work with the log your controller generates: