Rock
the Robot Construction Kit
We’ll cover in this page how to define your task’s interface. All statements presented in this page are to be included in a task context definition, i.e. between ‘do’ and ‘end’ in
task_context "ClassName" do ... end
The only constraint is that the task name must be different from the project name. This is because, on the generated C++ code, the project name is mapped into a namespace and the task name as a class name. Having a class and a namespace have the same name is generates quite a bit of ambiguity on the C++ side, so it is forbidden.
The task interface is the focus of that page. Other issues, as using different triggering modes and subclassing between task contexts is covered later.

As a general rule of thumb, the components should communicate with each other only through ports. The properties and operations (as well as the state machine covered in the next page) are meant to be used by a coordination layer (in the Rock case, either Ruby scripts or the model-based management layer).
Unfortunately, oroGen has to cope with RTT limited support for basic types (int, long, …), and therefore the following limitations apply:
Some limitations on types used in the task interface
Ports are defined with
input_port('in', 'my_type'). doc('documentation string') output_port('out', 'another_type'). doc('documentation string')
Input ports can be used in the C++ code in two ways, which one you want to use depends on what you actually want to do.
// my_type is the declared type of the port my_type sample; while (_in.read(sample) == RTT::NewData) { // got a new sample, do something with it }
// my_type is the declared type of the port my_type sample; if (_in.read(sample)) { // got a sample, do something with it }
Finally, to write on an output, you use ‘write’:
// another_type is the declared type of the port
another_type data = calculateData();
_out.write(data);
Another operation of interest is the connected() predicate. It tests if there is a data provider that will send data to input ports (in.connected()) or if there is a listener component that will get the samples written on output ports.
For instance,
if (_out.connected()) { // generate the data for _out only if somebody may be interested by it. This // is useful if generating // the data is costly another_type data = calculateData(); _out.write(data); }
Properties are defined with
property('name', 'configuration_type'). doc 'what this property is about'
And can be accessed in the code with
configuration_type configuration_value = _name.get();
// Validate configuration_type, and change the value
_name.set(configuration_value)
The operations offer a mechanism from which a task context can expose functionality through remote method calls. They are defined with:
operation('commandName'). argument('arg0', '/arg/type'). argument('arg1', '/example/other_arg')
Additionally, a return type can be added with
operation('operationName'). returns('int'). argument('arg0', '/arg/type'). argument('arg1', '/example/other_arg')
Note the dot at the end of all but the last line. This dot is important and, if omitted, will lead to syntax errors.
In the generated task class, the operation simply maps to a C++ method:
return_type operationName(arg::type const& arg0, example::other_arg const& arg1);
the default return type is ‘void’.
By default, the operations are run into the callee thread, i.e. the thread of the task context on which the operation is defined. However, it basically means that, if the caller needs the result of the operation, it will have to wait until the operation is finished and therefore start to have quite a coupling with the task context that executes the operation.
If it is desirable, one can design the operation’s C++ method to be thread-safe and declare it as being executed in the caller thread instead of the callee thread. This is done with
operation('operationName'). returns('int'). argument('arg0', '/arg/type'). argument('arg1', '/example/other_arg'). runs_in_caller_thread
For each element of the interface, Orogen declares the corresponding RTT C++ object as an attribute of the task implementation. The attribute’s name is the object name with an underscore prepended. For instance, the operation declared with
operation('operationName')
is mapped to an attribute named _operationName, of type RTT::Operation.
The mappings are as follows:
Refer to the RTT’s documentation to learn more on the operations available on these objects.
When deployed in a system, a task context is assigned an activity. This activity defines how the component should be triggered.
In all generality, any component should be able to support any kind of activity. However, not giving any hint about what would be a “best” activity for a component makes it very hard for people to use such component.
In order to make life easier, oroGen allows you to propose a default activity for your task contexts. This is done with
default_activity "activity_type", activity_parameters
The available triggering mechanisms are detailed here. They can be declared as default activities with
default_activity 'periodic', 0.1 # periodic activity wit a period of 100ms default_activity 'fd_driven' # IO-driven activity default_activity 'triggered' # triggered by external events, i.e. explicit # calls to trigger() or data-driven task
Note that fd_driven and triggered do not usually need to be specified. ‘triggered’ is the default activity and ‘fd_driven’ is set as default activity if you use the fd_driven statement.
Finally, one can force the deployment to use an activity by replacing “default_activity” by “required_activity”.
Some components (e.g. the logger or the canbus components) may create new ports at runtime when one of their operations is called. While not strictly necessary in day-to-day operations, some of the model-based tooling (namely, rock-roby) may require hints that this creation is possible. This is done with the dynamic_input_port and dynamic_output_port declarations, possibly using a regular expression as name pattern and either a message type or nil for “type unknown”. For instance
dynamic_output_port /.*/, "canbus/Message"
declares, in the canbus::Task, that ports with arbitrary names might be added to the task interface, and that these ports will have the /canbus/Message type.