The Orocos Core Primitives Manual

Date

Mar 25, 2011

Introduction

This Chapter describes the semantics of the services available as the OROCOS Core Primitives

The Core Primitives are:

  • Thread-safe C++ implementations for periodic, non periodic and event driven activities

  • Synchronous/Asynchronous OperationCaller invocations

  • Synchronous callback handling

  • Property trees

  • Time measurement

  • Application logging framework

  • Lock-free data exchange primitives such as FIFO buffers or shared data.

    The goal of the infrastructure is to keep applications deterministic and avoiding the classical pitfalls of letting application programmers freely use threads and mutexes as bare tools.

The following sections will first introduce the reader to creating Activities, which execute functions in a thread, in the system. Signals allow synchronous callback functions to be executed when other primitives are used. Operations are used to expose services.

Activities

An Activity executes a function when a ‘trigger’ occurs. Although, ultimately, an activity is executed by a thread, it does not map one-to-one on a thread. A thread may execute (‘serialise’) multiple activities. This section gives an introduction to defining periodic activities, which are triggered periodically, non periodic activities, which are triggered by the user, and slave activities, which are run when another activity executes.

Executing a Function Periodically

Note

When you use a TaskContext, the ExecutionEngine is the function to be executed periodically and you don’t need to write the classes below.

There are two ways to run a function in a periodically. By :

  • Implementing the RTT::base::RunnableInterface in another class ( functions initialize(), step() or loop()/breakLoop() and finalize() ). The RunnableInterface object (i.e. run_impl) can be assigned to a activity using activity.run( &run_impl ) or at construction time of an Activity : Activity activity(priority, period, &run_impl );.

    #include <rtt/RunnableInterface.hpp>
    #include <rtt/Activity.hpp>
    
    class MyPeriodicFunction
      : public RTT::base::RunnableInterface
    {
    public:
      // ...
      bool initialize() {
         // your init stuff
         myperiod = this->getActivity()->getPeriod();
         isperiodic = this->getActivity()->isPeriodic();
    
         // ...
         return true; // if all went well
      }
    
      // executed when isPeriodic() == true
      void step() {
         // periodic actions
      }
    
      // executed when isPeriodic() == false
      void loop() {
         // 'blocking' version of step(). Implement also breakLoop()
      }
    
      void finalize() {
         // cleanup
      }
    };
    
    // ...
    MyPeriodicFunction run_impl_1;
    MyPeriodicFunction run_impl_2;
    
    RTT::Activity activity( 15, 0.01 ); // priority=15, period=100Hz
    activity.run( &run_impl_1 );
    activity.start(); // calls 'step()'
    
    RTT::Activity npactivity(12); // priority=12, no period.
    npactivity.run( &run_impl_2);
    activity.start(); // calls 'loop()'
    
    // etc...
    
  • Inheriting from an Activity class and overriding the initialize(), step() and finalize() methods.

    class MyOtherPeriodicFunction
        : public RTT::Activity
    {
    public :
      MyOtherPeriodicFunction()
        : RTT::Activity( 15, 0.01 ) // priority=15, period=100Hz
      {
      }
    
      bool initialize() {
         // your init stuff
         double myperiod = this->getPeriod();
         // ...
         return true; // if all went well
      }
    
      void step() {
         // periodic actions
      }
    
      void finalize() {
         // cleanup
      }
      // ...
    };
    
    // When started, will call your step
    MyOtherPeriodicFunction activity;
    activity.start();
    

The Activity will detect if it must run an external RunnableInterface. If none was given, it will call its own virtual methods.

Non Periodic Activity Semantics

If you want to create an activity which reads file-IO, or displays information or does any other possibly blocking operation, the RTT::Activity implementation can be used with a period of zero (0). When it is start()’ed, its loop() method will be called exactly once and then it will wait, after which it can be start()’ed again. Analogous to a periodic Activity, the user can implement initialize(), loop() and finalize() functions in a RTT::base::RunnableInterface which will be used by the activity for executing the user’s functions. Alternatively, you can reimplement said functions in a derived class of Activity.

int priority = 5;

RTT::base::RunnableInterface* blocking_activity = ...
RTT::Activity activity( priority, blocking_activity );
activity.start(); // calls blocking_activity->initialize()

// now blocking_activity->loop() is called in a thread with priority 5.
// assume loop() finished...

activity.start();  // executes again blocking_activity->loop()

// calls blocking_activity->breakLoop() if loop() is still executing,
// when loop() returned, calls blocking_activity->finalize() :
activity.stop();

The Activity behaves differently when being non periodic in the way start() and stop() work. Only the first invocation of start() will invoke initialize() and then loop() once. Any subsequent call to start() will cause loop() to be executed again (if it finished in the first place).

Since the user’s loop() is allowed to block the user must reimplement the RunnableInterface::breakLoop() function. This function must do whatever necessary to let the user’s loop() function return (mostly set a flag). It must return true on success, false if it was unable to let the loop() function return (the latter is the default implementation’s return value). stop() then waits until loop() returns or aborts if breakLoop() returns false. When successful, stop() executes the finalize() function.

Selecting the Scheduler

There are at least two scheduler types in RTT: The real-time scheduler, ORO_SCHED_RT, and the not real-time scheduler, ORO_SCHED_OTHER. In some systems, both may map to the same scheduler.

When a RTT::Activity, it runs in the default ‘ORO_SCHED_OTHER’ scheduler with the lowest priority. You can specify another priority and scheduler type, by providing an extra argument during construction. When a priority is specified, the Activity selects the the ORO_SCHED_RT scheduler.

// Equivalent to Activity my_act(OS::HighestPriority, 0.001) :
Activity my_act(ORO_SCHED_RT, OS::HighestPriority, 0.001);

// Run in the default scheduler (not real-time):
Activity other_act ( 0.01 );

Custom or Slave Activities

If none of the above activity schemes fit you, you can always fall back on the RTT::extras::SlaveActivity, which lets the user control when the activity is executed. A special function bool execute() is implemented which will execute RunnableInterface::step() or RunnableInterface::loop() when called by the user. Three versions of the SlaveActivity can be constructed:

#include <rtt/SlaveActivity.hpp>

// With master
// a 'master', any ActivityInterface (even SlaveActivity):
RTT::Activity master_one(9, 0.001 );
// a 'slave', takes over properties (period,...) of 'master_one':
RTT::extras::SlaveActivity slave_one( &master_one );

slave_one.start();   // fail: master not running.
slave_one.execute(); // fail: slave not running.

master_one.start();  // start the master.
slave_one.start();   // ok: master is running.
slave_one.execute(); // ok: calls step(), repeat...

// Without master
// a 'slave' without explicit master, with period of 1KHz.
RTT::extras::SlaveActivity slave_two( 0.001 );
// a 'slave' without explicit master, not periodic.
RTT::extras::SlaveActivity slave_three;

slave_two.start();   // ok: start periodic without master
slave_two.execute(); // ok, calls 'step()', repeat...
slave_two.stop();

slave_three.start();   // start not periodic.
slave_three.execute(); // ok, calls 'loop()', may block !
// if loop() blocks, execute() blocks as well.

Note that although there may be a master, it is still the user’s responsibility to get a pointer to the slave and call execute().

There is also a trigger() function for slaves with a non periodic master. trigger() will in that case call trigger() upon the master thread, which will cause it to execute. The master thread is then still responsible to call execute() on the slave. In constrast, calling trigger() upon periodic slaves or periodic activities will always fail. Periodic activities are triggered internally by the elapse of time.

Configuring the Threads from Activities

Each Orocos Activity (periodic, non periodic and event driven) type has a thread() method in its interface which gives a non-zero pointer to a RTT::os::ThreadInterface object which provides general thread information such as the priority and periodicity and allows to control the real-timeness of the thread which runs this activity. A non periodic activity’s thread will return a period of zero.

An RTT::base::RunnableInterface can get the same information through the this->getActivity()->thread() method calls.

This example shows how to manipulate a thread.

#include "rtt/ActivityInterface.hpp"

using namespace RTT;

ORO_main( int argc, char** argv)
{
  // ... create any kind of Activity like above.

  RTT::base::ActivityInterface* act = ...

  // stop the thread and all its activities:
  act->thread()->stop();
  // change the period:
  act->thread()->setPeriod( 0.01 );

  // ORO_SCHED_RT: real-time  ORO_SCHED_OTHER: not real-time.
  act->thread()->setScheduler(ORO_SCHED_RT);

  act->thread()->start();

  // act is running...

  return 0;
}

Signals

An RTT::internal::Signal is an object to which one can connect callback functions. When the Signal is raised, the connected functions are called one after the other. An Signal can carry data and deliver it to the function’s arguments.

Any kind of function can be connected to the signal as long as it has the same signature as the Signal. ‘Raising’, ‘firing’ or ‘emitting’ an Orocos Signal is done by using operator().

Signal Basics

This example shows how a handler is connected to an Signal.

#include <rtt/internal/Signal.hpp>

using boost::bind;

class SafetyStopRobot {
public:
   void handle_now() {
       std::cout << " Putting the robot in a safe state fast !" << std::endl;
   }
};

SafetyStopRobot safety;

Now we will connect the handler function to a signal. Each event-handler connection is stored in a Handle object, for later reference and connection management.

// The <..> means the callback functions must be of type "void foo(void)"
RTT::internal::Signal<void(void)> emergencyStop;
// Use ready() to see if the event is initialised.
assert( emergencyStop.ready() );
RTT::Handle emergencyHandle;
RTT::Handle notifyHandle;

// boost::bind is a way to connect the method of an object instance to
// an event.
std::cout << "Register appropriate handlers to the Emergency Stop Signal\n";
emergencyHandle =
  emergencyStop.connect( bind( &SafetyStopRobot::handle_now, &safety));
assert( emergencyHandle.connected() );

Finally, we emit the event and see how the handler functions are called:

std::cout << "Emit/Call the event\n";
emergencyStop();

The program will output these messages:

Register appropriate handlers to the Emergency Stop Signal
Emit the event
 Putting the robot in a safe state fast !

If you want to find out how boost::bind works, see the Boost bind manual. You must use bind if you want to call C++ class member functions to ‘bind’ the member function to an object :

ClassName object;
boost::bind( &ClassName::FunctionName, &object)

Where ClassName::FunctionName must have the same signature as the Signal. When the Signal is called,

object->FunctionName( args )

is executed by the Signal.

When you want to call free ( C ) functions, you do not need bind :

Signal<void(void)> event;
void foo() { ... }
event.connect( &foo );

You must choose the type of RTT::internal::Signal upon construction. This can no longer be changed once the RTT::internal::Signal is created. If the type changes, the event() method must given other arguments. For example :

RTT::internal::Signal<void(void)> e_1;
e_1();

RTT::internal::Signal<void(int)>  e_2;
e_2( 3 );

RTT::internal::Signal<void(double,double,double)>  positionSignal;
positionSignal( x, y, z);

Furthermore, you need to setup the connect call differently if the Signal carries one or more arguments :

SomeClass someclass;

Signal<void(int, float)> event;

// notice that for each Signal argument, you need to supply _1, _2, _3, etc...
event.connect( boost::bind( &SomeClass::foo, someclass, _1, _2 ) );

event( 1, 2.0 );

Important

The return type of callbacks is ignored and can not be recovered.

On setup() and the RTT::Handle object

Signal connections can be managed by using a Handle which both connect() and setup() return :

RTT::internal::Signal<void(int, float)> event;
RTT::Handle eh;

// store the connection in 'eh'
eh = event.connect( ... );
assert( eh.connected() );

// disconnect the function(s) :
eh.disconnect();
assert( !eh.connected() );

// reconnect the function(s) :
eh.connect();
// connected again !

Handle objects can be copied and will all show the same status. To have a connection setup, but not connected, one can write :

RTT::internal::Signal<void(int, float)> event;
RTT::Handle eh;

// setup : store the connection in 'eh'
eh = event.setup( ... );
assert( !eh.connected() );

// now connect the function(s) :
eh.connect();
assert( eh.connected() );  // connected !

If you do not store the connection of setup(), the connection will never be established and no memory is leaked. If you do not use ‘eh’ to connect and destroy this object, the connection is also cleaned up. If you use ‘eh’ to connect and then destroy ‘eh’, you can never terminate the connection, except by destroying the Signal itself.

Time Measurement and Conversion

The TimeService

The RTT::os::TimeService is implemented using the Singleton design pattern. You can query it for the current (virtual) time in clock ticks or in seconds. The idea here is that it is responsible for synchronising with other (distributed) cores, for doing, for example compliant motion with two robots. This functionality is not yet implemented though.

When the RTT::extras::SimulationThread is used and started, it will change the TimeService’s clock with each period ( to simulate time progress). Also other threads (!) In the system will notice this change, but time is guaranteed to increase monotonously.

Usage Example

Also take a look at the interface documentation.

#include <rtt/os/TimeService.hpp>
#include <rtt/Time.hpp>

TimeService::ticks timestamp = RTT::os::TimeService::Instance()->getTicks();
//...

Seconds elapsed = TimeService::Instance()->secondsSince( timestamp );

Attributes

Attributes are class members which contain a (constant) value. Orocos can manipulate a classes attribute when it is wrapped in an RTT::Attribute class. This storage allows it to be read by the scripting engine, to be displayed on screen or manipulated over a network connection.

The advantages of this class come clear when building Orocos Components, since it allows a component to make internal data to its scripts.

// an attribute, representing a double of value 1.0:
RTT::Attribute<double> myAttr(1.0);
myAttr.set( 10.9 );
double a = myAttr.get();

// read-only attribute:
RTT::Constant<double> pi(3.14);
double p = pi.get();

Properties

Properties are more powerful than attributes (above) since they can be stored to an XML format, be hierarchically structured and allow complex configuration.

Introduction

Orocos provides configuration by properties through the RTT::Property class. They are used to store primitive data (float, strings,…) in a hierarchies (using RTT::PropertyBag). A Property can be changed by the user and has immediate effect on the behaviour of the program. Changing parameters of an algorithm is a good example where properties can be used. Each parameter has a value, a name and a description. The user can ask any PropertyBag for its contents and change the values as they see fit. Java for example presents a Property API. The Doxygen Property API should provide enough information for successfully using them in your Software Component.

Note

Reading and writing a properties value can be done in real-time. Every other transaction, like marshaling (writing to disk), demarshaling (reading from disk) or building the property is not a real-time operation.

// a property, representing a double of value 1.0:

RTT::Property<double> myProp("Parameter A","A demo parameter", 1.0); // not real-time !
myProp = 10.9; // real-time
double a = myProp.get(); // real-time

Properties are mainly used for two purposes. First, they allow an external entity to browse their contents, as they can form hierarchies using PropertyBags. Second, they can be written to screen, disk, or any kind of stream and their contents can be restored later on, for example after a system restart. The next sections give a short introduction to these two usages.

Grouping Properties in a PropertyBag

First of all, a RTT::PropertyBag is not the owner of the properties it owns, it merely keeps track of them, it defines a logical group of properties belonging together. Thus when you delete a bag, the properties in it are not deleted, when you clone() a bag, the properties are not cloned themselves. PropertyBag is thus a container of pointers to Property objects.

If you want to duplicate the contents of a PropertyBag or perform recursive operations on a bag, you can use the helper functions we created and which are defined in PropertyBag.hpp (see Doxygen documentation). These operations are however, most likely not real-time.

Use add to add Properties to a bag and getProperty(name) to mirror a RTT::Property<T>. Mirroring allows you to change and read a property which is stored in a PropertyBag: the property object’s value acts like the original. The name and description are not mirrored, only copied upon initialisation:

Marshalling and Demarshalling Properties (Serialization)

Marshalling is converting a property C++ object to a format suitable for transportation or storage, like XML. Demarshalling reconstructs the property again from the stored format. In Orocos, the RTT::marsh::Marshaller interface defines how properties can be marshalled. The available marshallers (property to file) in Orocos are the RTT::marsh::TinyMarshaller, RTT::marsh::XMLMarshaller, RTT::marsh::XMLRPCMarshaller, RTT::marsh::INIMarshaller and the RTT::marsh::CPFMarshaller (only if Xerces is available).

The inverse operation (file to property) is currently supported by two demarshallers: RTT::marsh::TinyDemarshaller and the RTT::marsh::CPFDemarshaller (only if Xerces is available). They implement the RTT::marsh::Demarshaller interface.

The (de-)marshallers know how to convert native C++ types, but if you want to store your own classes in a Property ( like BirthDate in the example above ), the class must be added to the Orocos type system.

In order to read/write portably (XML) files, use the RTT::marsh::PropertyMarshaller and RTT::marsh::PropertyDemarshaller classes which use the default marshaller behind the scenes.

Extra Stuff

Buffers and DataObjects

The difference between Buffers and DataObjects is that DataObjects always contain a single value, while buffers may be empty, full or contain a number of values. Thus a RTT::internal::DataObject always returns the last value written (and a write always succeeds), while a buffer may implement a FIFO queue to store all written values (and thus can get full).

Buffers

The RTT::base::BufferInterface<T> provides the interface for Orocos buffers. Currently the RTT::base::BufferLockFree<T> is a typed buffer of type T and works as a FIFO queue for storing elements of type T. It is lock-free, non blocking and read and writes happen in bounded time. It is not subject to priority inversions.

#include <rtt/BufferLockFree.hpp>

// A Buffer may also contain a class, instead of the simple
// double in this example
// A buffer with size 10:
RTT::base::BufferLockFree<double> my_Buf( 10 );
if ( my_Buf.Push( 3.14 ) ) {
   // ok. not full.
}
double  contents;
if ( my_Buf.Pop( contents ) ) {
   // ok. not empty.
   // contents == 3.14
}

Both Push() and Pop() return a boolean to indicate failure or success.

DataObjects

The data inside the RTT::base::DataObjects can be any valid C++ type, so mostly people use classes or structs, because these carry more semantics than just (vectors of) doubles. The default constructor of the data is called when the DataObject is constructed. Here is an example of creating and using a DataObject :

#include <rtt/DataObjectInterfaces.hpp>

// A DataObject may also contain a class, instead of the simple
// double in this example
RTT::base::DataObjectLockFree<double> my_Do("MyData");
my_Do.Set( 3.14 );
double  contents;
my_Do.Get( contents );   // contents == 3.14
contents  = my_Do.Get(); // equivalent

The virtual RTT::base::DataObjectInterface interface provides the Get() and Set() methods that each DataObject must have. Semantically, Set() and Get() copy all contents of the DataObject.

Logging

Orocos applications can have pretty complex start-up and initialisation code. A logging framework, using RTT::Logger helps to track what your program is doing.

Note

Logging can only be done in the non-real-time parts of your application, thus not in the Real-time Periodic Activities !

There are currently 8 log levels :

ORO_LOGLEVEL

Logger::enum

Description

-1

na

Completely disable logging

0

Logger::Never

Never log anything (to console)

1

Logger::Fatal

Only log Fatal errors. System will abort immediately.

2

Logger::Critical

Only log Critical or worse errors. System may abort shortly after.

3

Logger::Error

Only log Errors or worse errors. System will come to a safe stop.

4

Logger::Warning

[Default] Only log Warnings or worse errors. System will try to resume anyway.

5

Logger::Info

Only log Info or worse errors. Informative messages.

6

Logger::Debug

Only log Debug or worse errors. Debug messages.

7

Logger::RealTime

Log also messages from possibly Real-Time contexts. Needs to be confirmed by a function call to Logger::allowRealTime().

Table: Logger Log Levels

You can change the amount of log info printed on your console by setting the environment variable ORO_LOGLEVEL to one of the above numbers :

export ORO_LOGLEVEL=5

The default is level 4, thus only warnings and errors are printed.

The minimum log level for the orocos.log file is Logger::Info. It will get more verbose if you increase ORO_LOGLEVEL, but will not go below Info. This file is always created if the logging infrastructure is used. You can inspect this file if you want to know the most useful information of what is happening inside Orocos.

If you want to disable logging completely, use

export ORO_LOGLEVEL=-1

before you start your program.

For using the RTT::Logger class in your own application, consult the API documentation.

#include <rtt/Logger.hpp>

Logger::In in("MyModule");
log( Error ) << "An error Occured : " << 333 << "." << endlog();
log( Debug ) << debugstring << data << endlog();
log() << " more debug info." << data << endlog();
log() << "A warning." << endlog( Warning );

As you can see, the Logger can be used like the standard C++ input streams. You may change the Log message’s level using the LogLevel enums in front (using log() ) or at the end (using endlog()) of the log message. When no log level is specified, the previously set level is used. The above message could result in :

0.123 [ ERROR  ][MyModule] An error Occured : 333
0.124 [ Debug  ][MyModule] <contents of debugstring and data >
0.125 [ Debug  ][MyModule]  more debug info. <...data...>
0.125 [ WARNING][MyModule] A warning.