Replacing Events

RTT 2.0 no longer supports the RTT::Event class. This page explains how to adapt your code for this.

Rationale

RTT::Event was broken in some subtle ways, especially the unreliable asynchronous delivery and the danger of untrusted clients made the Event fragile. It was choosen to be replaced by an OutputPort or an Operation, depending on the use case.
  • Replace an Event by an OutputPort if you want to broadcast to many components. Any event sender->receiver connection can set the buffering policy, or encapsulate a transport to another (remote) process.
  • Replace an Event by an Operation if you want to react to an interface call *inside* your component, for example, in a state machine script or in C++.

Replacing by an OutputPort

Output ports differ from RTT::Event in that they can take only one value as an argument. If your 1.x Event contained multiple arguments, they need to be taken together in a new struct that you create yourself. Both sender and receiver must know and understand this struct.

For the simple case, when your Event only had one argument:

// RTT 1.x
class MyTask: public TaskContext
{
   RTT::Event<void(int)> samples_processed;
 
   MyTask() : TaskContext("task"), samples_processed("samples_processed") 
   {
      events()->addEvent( &samples_processed );
   }
   // ... your other code here...
};  
Becomes:
// RTT 2.x
class MyTask: public TaskContext
{
   RTT::OutputPort<int> samples_processed;
 
   MyTask() : TaskContext("task"), samples_processed("samples_processed") 
   {
      ports()->addPort( samples_processed ); // note: RTT 2.x dropped the '&'
   }
   // ... your other code here...
};  

Note: the rtt2-converter tool does not do this replacement, see the Operation section below.

Components wishing to receive the number of samples processed, need to define an InputPort<int> and connect their input port to the output port above.

Reacting to event data in scripts

When using the RTT scripting service's state machine, you can react to data arriving on the port. You could for example load this script in the above component:
StateMachine SM {
 
   var int total = 0;
 
   initial state INIT {
     entry {
     }
     // Reads samples_processed and stores the result in 'total'.
     // Only if the port return 'NewData', this branch will be evaluated.
     transition samples_processed( total ) if (total > 0 ) select PROCESSING;
   }
 
   state PROCESSING {
     entry { /* processing code, use 'total' */
     }
   }
 
   final state FINI {}

The transition from state INIT to state PROCESSING will only by taken if samples_processed.read( total ) == NewData and if total > 0. Note: When your TaskContext is periodically executing, the read( total ) statement will be re-tried and overwritten in case of OldData and NewData. Only if the connection of samples_processed is completely empty (never written to or reset), total will not be overwritten.

Replacing by an Operation

Operations are can take the same signature as RTT::Event. The difference is that only the component itself can attach callbacks to an Operation, by means of the signals() function.

For example:

// RTT 1.x
class MyTask: public TaskContext
{
   RTT::Event<void(int, double)> samples_processed;
 
   MyTask() : TaskContext("task"), samples_processed("samples_processed") 
   {
      events()->addEvent( &samples_processed );
   }
   // ... your other code here...
};  
Becomes:
// RTT 2.x
class MyTask: public TaskContext
{
   RTT::Operation<void(int,double)> samples_processed;
 
   MyTask() : TaskContext("task"), samples_processed("samples_processed") 
   {
      provides()->addOperation( samples_processed ); // note: RTT 2.x dropped the '&'
 
      // Attaching a callback handler to the operation object:
      Handle h = samples_processed.signals( &MyTask::react_foo, this );
   }
   // ... your other code here...
 
   void react_foo(int i, double d) {
       cout << i <<", " << d <<endl;
   }
};  

Note: the rtt2-converter tool only does this replacement automatically. Ie. it assumes all your Event objects were only used in the local component. See the RTT 2.0 Renaming table for this tool.

Since an Operation object is always local to the component, no other components can attach callbacks. If your Operation would return a value, the callback functions needs to return it too, but it will be ignored and not received by the caller.

The callback will be executed in the same thread as the operation's function (ie OwnThread vs ClientThread).

Reacting to operations in scripts

When using the RTT scripting service's state machine, you can react to calls on the Operation. You could for example load this script in the above component:
StateMachine SM {
 
   var int total = 0;
 
   initial state INIT {
     entry {
     }
     // Reacts to the samples_processed operation to be invoked
     // and stores the argument in total. If the Operations takes multiple
     // arguments, also here multiple arguments must be given.
     transition samples_processed( total ) if (total > 0 ) select PROCESSING;
   }
 
   state PROCESSING {
     entry { /* processing code, use 'total' */
     }
   }
 
   final state FINI {}

The transition from state INIT to state PROCESSING will only by taken if samples_processed( total ) was called by another component (using a Method object, see Methods vs Operations and if the argument in that call > 0. Note: when samples_processed would return a value, your script can not influence that return value since the return value is determined by the function tied to the Operation, not by signal handlers.

NOTE: RTT 2.0.0-beta1 does not yet support the script syntax.