The Orocos Component Builder's Manual
Open RObot COntrol Software
1.0.3
Copyright © 2002,2003,2004,2005,2006 Peter Soetens, FMTC
Orocos Version 1.0.3.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation, with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of this license can be found at http://www.fsf.org/copyleft/fdl.html.
Abstract
This document gives an introduction to building your own components for the Orocos ( Open RObot COntrol Software ) project.
Table of Contents
- 1. How to Read this Manual
- 2. Setting up the Component Interface
- 1. Introduction
- 2. Hello World !
- 3. Setting Up a Basic Task
- 3.1. Task Application Code
- 3.2. Starting Task Execution
- 3.3. Interfacing the TaskContext
- 3.4. Introducing the TaskContext's Interface
- 3.5. The Data Flow Interface
- 3.6. The Method Interface
- 3.7. Method Argument and Return Types
- 3.8. The Attributes and Properties Interface
- 3.9. The Command Interface
- 3.10. The Event Interface
- 4. Connecting TaskContexts
- 5. Deploying Components
- 6. Using Tasks
- 7. Advanced Techniques
- 3. Orocos Scripting Reference
- 4. Distributing Orocos Components with CORBA
- 5. Core Library Reference
- 6. OS Abstraction Reference
- 7. Hardware Device Interfaces
List of Figures
- 1.1. Orocos as Middleware
- 1.2. Real-Time Toolkit Layers
- 2.1. Tasks Run in Threads
- 2.2. Schematic Overview of a TaskContext
- 2.3. Schematic Overview of the Hello Component.
- 2.4. Schematic Overview of a TaskContext
- 2.5. Executing a TaskContext
- 2.6. Component Deployment Levels
- 2.7. Example Component Deployment.
- 3.1. State Change Semantics in Reactive Mode
- 3.2. State Change Semantics in Automatic Mode
- 5.1. Execution sequence diagram
- 5.2. Tasks Sending Commands
- 5.3. Event Handling
- 5.4. DataObjects versus Buffers
- 6.1. OS Interface overview
- 7.1. Device Interface Overview
List of Tables
List of Examples
- 2.1. TaskContext Data Flow Topology Example
- 2.2. TaskContext Peer Disconnection Example
- 3.1. string and array creation
- 3.2. StateMachine Definition Format
- 3.3. StateMachine Example (state.osd)
- 3.4. Program example (program.ops)
- 5.1. Example Periodic Activity Creation
- 5.2. Example Periodic Thread Interaction
- 5.3. Using Events
- 5.4. Event Types
- 5.5. Creating attributes
- 5.6. Using properties
- 5.7. Accessing a Buffer
- 5.8. Accessing a DataObject
- 5.9. Using the Logger class
- 6.1. Locking a Mutex
- 7.1. Using the name service
Table of Contents
This manual is for Software developers who wish to write their own software components using the Orocos Real-Time Toolkit. The possible communication primitives between components is defined and implemented in the Orocos Core Library (CoreLib). You can find the CoreLib Reference Chapter at the end to find out the precise semantics of our communication primitives. The Orocos hardware abstraction is included as well.
The most important Chapters to get started building a component are presented first. Orocos components are implemented using the 'TaskContext' class and the following Chapter explains step by step how to define the interface of your component, such that you can interact with your component from a user interface or other component.
For implementing algorithms within your component, the Orocos Scripting Chapter details how to write programs and state machines. "Advanced Users" may benefit from this Chapter as well since the scripting language allows to 'program' components without recompiling the source.
The Real-Time Toolkit allows deployment, distribution and the building of real-time software components. It is sometimes refered to as 'middleware' because it sits between the application and the Operating System. It takes care of the real-time communication and execution of software components.
The Real-Time Toolkit is structured in layers on top of the hardware target (CPU) and the devices (IO) and encompasses some libraries.
All Orocos software communicates through Operating System abstractions with the underlying OS or through Device Interfaces with the IO hardware. On the left side on top of the OS Abstraction is the Core Library, which presents infrastructure for writing realtime (periodic) activities, including many communication primitives. On top of that is the Real-Time Toolkit, which allows to define browsable components which are capable of receiving (remote) commands, process programs and hierarchical state machines and share realtime data with other components. Your application may use that infrastructure to define its own components and set up communication between them.
On the right side, lives the Device Abstraction stack. The Operating System already provides a device driver model, Orocos device drivers map that model to machine or robot control specific Device Interfaces. An Orocos Device combines a number of these interfaces into an object representing a physical entity. It may represent a complete machine, or just a mere Sensor or Axis, such that your application can communicate with physical meaningful devices ( instead of IO ports).
Because of the Openness of Orocos, your application does not need to take-all-or-leave-all. You can pick out the level of abstraction which suits you best for a given application (indicated by the gradient).
Table of Contents
- 1. Introduction
- 2. Hello World !
- 3. Setting Up a Basic Task
- 3.1. Task Application Code
- 3.2. Starting Task Execution
- 3.3. Interfacing the TaskContext
- 3.4. Introducing the TaskContext's Interface
- 3.5. The Data Flow Interface
- 3.6. The Method Interface
- 3.7. Method Argument and Return Types
- 3.8. The Attributes and Properties Interface
- 3.9. The Command Interface
- 3.10. The Event Interface
- 4. Connecting TaskContexts
- 5. Deploying Components
- 6. Using Tasks
- 7. Advanced Techniques
Abstract
This document describes the Orocos Task Infrastructure, which allows to design Real-Time tasks which transparantly communicate with each other.
This manual documents how multi-threaded components can be defined in Orocos such that they form a thread-safe robotics/machine control application. Each control component is defined as a "TaskContext", which defines the "context" in which the component task is executed. The context is built by the five Orocos primitives: Event, Property, Command, Method and Data Port. This document defines how a user can write his own task context and how it can be used in an application.
A task is a basic unit of functionality which executes one or more (real-time) programs in a single thread. The program can vary from a mere C function over a real-time program script to a real-time hierarchical state machine. The focus is completely on thread-safe time determinism. Meaning, that the system is free of priority-inversions, and all operations are lock-free (also data sharing and other forms of communication such as events and commands). Real-time tasks can communicate with non real-time tasks (and vice versa) transparantly.
The Orocos Task Infrastructure enables :
Lock free, thread-safe, inter-thread function calls.
Communication between hard Real-Time and non Real-Time threads.
Deterministic execution time during communication for the higher priority thread.
Synchronous and asynchronous communication between threads.
Interfaces for component distribution.
C++ class implementations for all the above.
This document relates to other manuals as such :
- Core Library
provides the Event infrastructure, activity to thread mapping, Properties and lock-free data exchange implementations.
- Execution Engine
provides a real-time program execution framework. It executes the real-time programs which interact with other tasks.
- Program Parser
provides a scripting language which is convertible to a form which can be accepted by the Execution Engine.
The Scripting manual gives more details about script syntax for state machines and programs.
Figure 2.2. Schematic Overview of a TaskContext

The Execution Flow is formed by Programs and State Machines sending commands, events,... to Peer Tasks. The Data Flow is the propagation of data from one task to another, where one producer can have multiple consumers and vice versa.
A task's interface consists of : Attributes and Properties, Commands, Methods, Events and Data Flow ports which are all public. The class TaskContext groups all these interfaces and serves as the basic building block of applications. A component developer 'builds' these interfaces using the instructions found in this manual.
This section introduces tasks through the "hello world" application, which can be downloaded from Orocos.org. It contains one TaskContext component which has one instance of each communication primitive.
The way we interact with TaskContexts during development of an Orocos application is through the Task Browser. The TaskBrowser is a powerful console tool which helps you to explore, execute and debug TaskContexts in running programs. All you have to do is to create a TaskBrowser and call its loop() method. When the program is started from a console, the TaskBrowser takes over user input and output.
| Note | |
|---|---|
The OCL::TaskBrowser is a component of its own which is found in the Orocos Component Library (OCL). |
#include <ocl/TaskBrowser.hpp>
#include <rtt/os/main.h>
// ...
using namespace Orocos;
int ORO_main( int, char** )
{
// Create your tasks
TaskContext* task = ...
// when all is setup :
TaskBrowser tbrowser( task );
tbrowser.loop();
return 0;
}
The TaskBrowser uses the GNU readline library to easily enter commands to the tasks in your system. This means you can press TAB to complete your commands or press the up arrow to scroll through previous commands.
0.016 [ Info ][main()] ./helloworld manually raises LogLevel to 'Info' (5). See also file 'orocos.log'.
0.017 [ Info ][main()] **** Creating the 'Hello' component ****
0.018 [ Info ][ConnectionC] Creating Asyn connection to the_event.
0.018 [ Info ][ExecutionEngine::setActivity] Hello is periodic.
0.019 [ Info ][main()] **** Starting the 'Hello' component ****
0.019 [ Info ][main()] **** Using the 'Hello' component ****
0.019 [ Info ][main()] **** Reading a Property: ****
0.019 [ Info ][main()] the_property = Hello World
0.019 [ Info ][main()] **** Sending a Command: ****
0.020 [ Info ][main()] Sending the_command : 1
0.020 [ Info ][main()] **** Calling a Method: ****
0.020 [ Info ][main()] Calling the_Method : Hello World
0.020 [ Info ][main()] **** Emitting an Event: ****
0.021 [ Info ][main()] **** Starting the TaskBrowser ****
0.021 [ Info ][TaskBrowser] Creating a BufferConnection from the_buffer_port to the_buffer_port with size 13
0.021 [ Info ][TaskBrowser] Connected Port the_buffer_port to peer Task Hello.
0.022 [ Info ][Hello] Creating a DataConnection from the_data_port to the_data_port
0.022 [ Info ][Hello] Connected Port the_data_port to peer Task TaskBrowser.
Switched to : Hello
0.023 [ Info ][main()] Entering Task Hello
0.023 [ Info ][Hello] Hello Command: World
0.023 [ Info ][Hello] Receiving Event: Hello World
This console reader allows you to browse and manipulate TaskContexts.
You can type in a command, event, method, expression or change variables.
(type 'help' for instructions)
TAB completion and HISTORY is available ('bash' like)
In Task Hello. (Status of last Command : none )
(type 'ls' for context info) :
The first [ Info ] lines are printed by the Orocos Logger, which has been configured to display informative messages to console. Normally, only warnings or worse are displayed by Orocos. You can always watch the log file 'orocos.log' in the same directory to see all messages. After the [Log Level], the [Origin] of the message is printed, and finally the message itself. These messages leave a trace of what was going on in the main() function before the prompt apeared.
Depending on what you type, the TaskBrowser will act differently. The built-in commands cd, help, quit and ls are seen as commands to the TaskBrowser itself, if you typed something else, it tries to evaluate your command to an expression and will print the result to the console. If you did not type an expression, it tries to parse it as a command to a (peer) task. If that also fails, it means you made a typo and it prints the syntax error to console.
In Task Hello. (Status of last Command : none ) (type 'ls' for context info) :1+1 Got :1+1 = 2
To display the contents of the current task, type ls, and switch to one of the listed peers with cd, while cd .. takes you one peer back in history. Since there are no peers other than the TaskBrowser itself, one can not cd anywhere in this example.
In Task Hello. (Status of last Command : none ) (type 'ls' for context info) :ls Listing Hello : Attributes : (Attribute) string the_attribute = Hello World (Attribute) string the_constant = Hello World (Property ) string the_property = Hello World (Hello World Description) Interface : Methods : the_method isRunning start stop trigger update Commands : the_command Events : the_event Objects : this ( ) the_data_port ( A Task Object. ) the_buffer_port ( A Task Object. ) Ports : the_data_port the_bufferPort Peers : TaskBrowser
| Note | |
|---|---|
To get a quick overview of the commands, type help. |
First you get a list of the Properties and Attributes (alphabetical) of the current component. Properties are meant for configuration and can be written to disk. Attributes are solely for run-time values. Each of them can be changed (except constants.)
Next, the interface of this component is listed: One method is present the_method, one command the_command and one event the_event. They all print a 'Hello World' string when invoked.
In the example, the current task has only three objects: this, the_data_port and the_buffer_port. The this object serves as the public interface of the Hello component. These objects contain methods, commands or events. The next two objects are created to represent the data ports of the Hello component and contain the operations to send or receive data or query connection status.
Last, the peers are shown, that is, the components which are connected to this component. The HelloWorld component is a stand-alone component and has only the TaskBrowser as a peer.
To get a list of the Task's interface, you can always type an object name, for example this.
In Task Hello. (Status of last Command : none ) (type 'ls' for context info) : this Got :this Printing Interface of 'Hello' : Command : bool the_command( string the_arg ) Hello Command Description the_arg : Use 'World' as argument to make the command succeed. Method : string the_method( ) Hello Method Description Event : void the_Event( string the_data ) Hello Event Description the_data : The data of this event.
Now we get more details about the commands, methods and events registered in the public interface. We see now that the the_command command takes one argument as a string, or that the the_method method returns a string. One can invoke each one of them:
In Task Hello. (Status of last Command : none )
(type 'ls' for context info) :the_method()
Got :the_method()
= Hello World
Methods are called directly and the TaskBrowser prints the result. The return value of the_method() was a string, which is "Hello World".
When a command is entered, it is sent to the Hello component, which will execute it on behalf of the sender. The different stages of its lifetime are displayed by the prompt. Hitting enter will refresh the status line:
In Task Hello. (Status of last Command : none ) (type 'ls' for context info) :the_command("World") Got :the_command("World") In Task Hello. (Status of last Command : queued) 1021.835 [ Info ][Hello] Hello Command: World (type 'ls' for context info) : 1259.900 [ Info ][main()] Checking Command: World In Task Hello. (Status of last Command : done ) (type 'ls' for context info) :
A Command might be rejected (return false) in case it received invalid arguments:
In Task Hello. (Status of last Command : done ) (type 'ls' for context info) :the_command("Belgium") Got :the_command("Belgium") In Task Hello. (Status of last Command : queued ) (type 'ls' for context info) : 1364.505 [ Info ][Hello] Hello Command: Belgium In Task Hello. (Status of last Command : fail ) (type 'ls' for context info) :
Besides sending commands to tasks, you can alter the attributes of any task, program or state machine. The TaskBrowser will confirm validity of the assignment with 'true' or 'false' :
In Task Hello. (Status of last Command : none ) (type 'ls' for context info) :the_attribute Got :the_attribute = Hello World In Task Hello. (Status of last Command : none ) (type 'ls' for context info) :the_attribute = "Veni Vidi Vici !" Got :the_attribute = "Veni Vidi Vici !" = true In Task Hello. (Status of last Command : none ) (type 'ls' for context info) :the_attribute Got :the_attribute = Veni Vidi Vici !
Finally, let's emit an Event. The Hello World Event requires a payload. A callback handler was registered by the component, thus when we emit it, it can react to it:
In Task Hello. (Status of last Command : none ) (type 'ls' for context info) :the_event(the_attribute) Got :the_event(the_attribute) = true In Task Hello. (Status of last Command : none ) (type 'ls' for context info) : 354.592 [ Info ][Hello] Receiving Event: Veni Vidi Vici !
The example above passed the the_attribute object as an argument to the event, and it was received by our task correctly. Events are related to commands, but allow broadcasting of data, while a command has a designated receiver.
The Data Ports can be accessed through the the_data_port and the_buffer_port object interfaces. Once again, we can inspect the interface of an object by typing its name:
In Task Hello. (Status of last Command : none ) (type 'ls' for context info) :the_data_port Got :the_data_port Printing Interface of 'the_data_port' : Method : string Get( ) Get the current value of this Data Port Method : void Set( string const& Value ) Set the current value of this Data Port Value : The new value.
The the_data_port object has two methods: Get() and Set(). Since data ports are used for sending unbuffered data packets between components, this makes sense. One can interact with the ports as such:
In Task Hello. (Status of last Command : none ) (type 'ls' for context info) :the_data_port.Set("World") Got :the_data_port.Set("World") = (void) In Task Hello. (Status of last Command : none ) (type 'ls' for context info) :the_data_port.Get() Got :the_data_port.Get() = World
When a value is Set(), it is sent to whatever is connected to that port, when we read the port using Get(), we see that the previously set value is present. The advantage of using ports is that they are completely thread-safe for reading and writing, without requiring user code. The Hello component also contains a the_buffer_port for buffered data transfer. You are encouraged to play with that port as well.
Remember that the TaskBrowser was a component as well ? When a user enters ls, the interface of the visited component is listed. It is also possible to get an 'outside' view of the visited component, through the eyes of an external component. The leave allows a view from within the TaskBrowser itself:
In Task Hello. (Status of last Command : none ) (type 'ls' for context info) :leave 1001.607 [ Info ][main()] Watching Task Hello Watching Task Hello. (Status of last Command : none ) (type 'ls' for context info) :ls Listing TaskBrowser : Attributes : (none) Interface : Methods : isRunning start stop trigger update Commands : (none) Events : (none) Objects : this ( ) the_data_port ( A Task Object. ) the_buffer_port ( A Task Object. ) Ports : the_data_port the_buffer_port Hello Peers : TaskBrowser
The following things are noteworthy: 'ls' shows now the contents of the TaskBrowser itself and no longer of the Hello Component. The TaskBrowser has the same ports as the component it visist: the_data_port and the_buffer_port. These were created at run-time and allow to write or read ports from the visited component.
One can enter the 'inside' view again by entering enter:
Watching Task Hello. (Status of last Command : none ) (type 'ls' for context info) :enter 1322.653 [ Info ][main()] Entering Task Hello In Task Hello. (Status of last Command : none ) (type 'ls' for context info) :
Last but not least, hitting TAB twice, will show you a list of possible completions, such as peers or commands :
In Task Hello. (Status of last Command : none )
(type 'ls' for context info) :
the_attribute the_event cd .. quit
the_buffer_port. the_method help this.
the_command the_property leave
the_constant TaskBrowser. list
the_data_port. cd ls
(type 'ls' for context info) :
TAB completion works even across peers, such that you can type a TAB completed command to another peer than the current peer.
In order to quit the TaskBrowser, enter quit:
In Task Hello. (Status of last Command : none )
(type 'ls' for context info) :quit
1575.720 [ Info ][ExecutionEngine::setActivity] Hello is disconnected from its activity.
1575.741 [ Info ][Logger] Orocos Logging Deactivated.
The TaskBrowser Component is application independent, so that your enduser-application might need a more suitable interface. However, for testing and inspecting what is happening inside your real-time programs, it is a very useful tool. The next sections show how you can add properties, commands, methods etc to a TaskContext.
| Note | |
|---|---|
If you want a more in-depth tutorial, see the 'task-intro' example for a TaskBrowser which visits a network of three TaskContexts. |
Tasks are implemented by the TaskContext class. It is useful speaking of a context because it defines the context in which an activity (a program) operates. It defines the interface of the task, its properties, its peer tasks and uses its ExecutionEngine to handle its programs and to accept commands from other tasks.
This section walks you through the definition of an example task in order to show you how you could build your own task.
| Important | |
|---|---|
The ready-to-execute code of this section can be found in the 'simple-task' package on the RTT Download section of the Orocos.org website. |
A TaskContext is constructed as :
#include <rtt/TaskContext.hpp> // we assume this is done in all the following code listings : using namespace RTT; TaskContext a_task("ATask");
The argument is the (unique) name of the task.
A task's interface consists of : Commands, Methods, Ports, Attributes and Properties and Events, which are all public. We will refer to them as members.
Figure 2.4. Schematic Overview of a TaskContext

The Execution Flow is formed by Programs and State Machines using the interface of Peer Tasks. The Data Flow is the propagation of data from one task to another, where one producer can have multiple consumers.
When a TaskContext is running, it accepts commands or events using its Execution Engine. The Execution Engine will check periodically for new commands in it's queue and execute programs which are running in the task. Thus to start using the task, one needs to start the Execution Engine. As long as it is not started, it will accept no commands, run no programs and react to no events.
The user may insert his application code in the
startup(),
update() and
shutdown() functions of a TaskContext
by inheriting from that class.
#include <rtt/TaskContext.hpp>
class MyTask
: public TaskContext
{
public:
MyTask(std::string name)
: TaskContext(name)
{
// see lateron what to put here.
}
/**
* This function contains the application's startup code.
* Return false to abort startup.
*/
bool startup() {
// ...
return true;
}
/**
* This function is periodically called.
*/
void update() {
// Your algorithm for periodic execution goes inhere
}
/**
* This function is called when the task is stopped.
*/
void shutdown() {
// Your cleanup code
}
};
When a TaskContext is started, startup() is
called once and may abort the startup sequence by returning
false, for example, because of misconfiguration. When all went
well, update() is called (a)periodically by
the ExecutionEngine, see below. When the task is stopped,
shutdown() is called after the last
update().
The functionality of a task, i.e. its algorithm, is executed by the Execution Engine. To run an ExecutionEngine, you need to use one of the ActivityInterface classes from the CoreLib, for example PeriodicActivity or NonPeriodicActivity. This relation is shown in Figure 2.5, “ Executing a TaskContext ”.
Figure 2.5. Executing a TaskContext

You can make a TaskContext '(re-)active' by creating an Activity object which executes its Execution Engine. The Execution Engine delegates all work to specific 'Processors' and user code in update().
The Activity classes will allocate a thread which executes the
Execution Engine. The choosen
ActivityInterface object will invoke
the Execution Engine, which will in turn invoke the
application's methods above.
#include <rtt/PeriodicActivity.hpp> using namespace RTT; TaskContext* a_task = new MyTask("the_task") // create a periodic activity with priority=5, period=1000Hz PeriodicActivity act(5, 0.001, a_task->engine() ); // ... start the execution engine of a_task : act.start(); // ... act.stop();
Which will run the Execution Engine of "ATask" with a timer frequency of 1kHz. This is the frequency at which state machines are evaluated, program steps taken, commands and events are accepted and executed and the application code is run. When the periodic activity is stopped again, all programs are stopped, state machines are brought into the final state and no more commands or events are accepted.
A TaskContext can also be run in a non periodic activity:
#include <rtt/NonPeriodicActivity.hpp> using namespace RTT; TaskContext* a_task = new MyTask("the_task") // create a non periodic activity with priority=5 NonPeriodicActivity nonpAct(5, a_task->engine() ); // ... start the execution engine of a_task : nonpAct.start(); // ... nonpAct.stop();
In case the Execution Engine waits for new Commands or Events to
come in to be executed. Each time such an event happens, the user's
application code (update()) is called as well.
| Warning | |
|---|---|
Non periodic activities should be used with care and with much thought. The ExecutionEngine will do absolutely nothing if no commands or asynchronous events come in. This may lead to surprising 'bugs' when program scripts or state machine scripts are executed, as they will only progress upon these events and seem to be stalled otherwise. |
During development of your TaskContext, it is handy to connect the TaskBrowser to your task such that you can interactively manipulate it and it's properties:
#include <ocl/TaskBrowser.hpp> // ... see above TaskBrowser browser(a_task); // Start the interactive console: browser.loop();
In which you can start/stop the task and manipulate every aspect of it's interface, as was seen in the previous section.
A TaskContext exists of a number of accessor methods which expose a specific part of the interface. These methods are:
a_task.ports();
a_task.methods();
a_task.attributes();
a_task.properties();
a_task.commands();
a_task.events();
The meaning of these methods are explained in the following sections.
| Purpose | |
|---|---|
The 'Data Flow' is a 'stream of data' between tasks. A classical control loop can be implemented using the Data Flow interface. The data is passed buffered or unbuffered from one task to another. |
![]() |
The Orocos Data Flow is implemented with the Port-Connector software pattern. Each task defines its data exchange ports and inter-task connectors transmit data from one port to another. A Port is defined by a name, unique within that task, the data type it wants to exchange and the buffered or un-buffered method of exchanging. Buffered exchange is done by "Buffer" Ports and un-buffered exchange is done by "Data" Ports.
A Data Port can offer read-only, write-only or read-write access to the unbuffered data. A Buffer Port can offer read-only, write-only and read-write access to the buffered data. The example below shows all these possiblities.
| Important | |
|---|---|
The ready-to-execute code of this section can be found in the 'dataflow-task' package on the RTT Download page of the Orocos.org website. |
Any kind of data can be exchanged (also user defined types) but for readability, only the 'double' C type is used here.
#include <rtt/Ports.hpp>
using namespace RTT;
class MyTask
: public TaskContext
{
// Read-only data port:
ReadDataPort<double> indatPort;
// Write-only data port:
WriteDataPort<double> outdatPort;
// Read-Write data port:
DataPort<double> rwdatPort;
// Read-only buffer port:
ReadBufferPort<double> inbufPort;
// Write-only buffer port:
WriteBufferPort<double> outbufPort;
// Read-Write buffer port:
BufferPort<double> rwbufPort;
public:
// ...
MyTask(std::string name)
: TaskContext(name),
indatPort("Data_R"),
outdatPort("Data_W", 1.0), // note: initial value
rwdatPort("Data_RW", 1.0),
inbufPort("SetPoint_X"),
outbufPort("Packet_1", 15), // note: buffer size
rwbufPort("Packet_2", 30)
{
this->ports()->addPort( &indatPort );
this->ports()->addPort( &outdatPort );
this->ports()->addPort( &rwdatPort );
this->ports()->addPort( &inbufPort );
this->ports()->addPort( &outbufPort );
this->ports()->addPort( &rwbufPort );
// more additions to follow, see below
}
// ...
};
The example starts with declaring all the ports of MyTask. A template parameter specifies the type of data the task wants to exchange through that port. Logically, if two tasks are connected, they must agree on this type. The constructor of MyTask initialises each port with a name. This name is used to 'match' ports between connected tasks. Orocos will warn at run-time if names or types do not match.
Write and read-write Buffers take besides a name, the
prefered buffer size as a parameter. In the example,
these are the values 15 and 30. Before the task is
connected to its peers, you can still change this
value with the setBufferSize()
function of the Port.
Finally, a 'write' port can take an initial value in the constructor as well. This value will be used when the connection between two ports is created in order to initialise the connection ( using 'connectPorts', see Section 4, “Connecting TaskContexts” ). If a 'write' port is connected to an existing connection, the initial value (and buffer size) are ignored and the settings of the existing connection are not touched. You can modify the initial value with the 'Set( value )' function in both Buffer and Data write ports.
| Note | |
|---|---|
Alternatively, you can connect buffers in your main() program by writing: #include <rtt/ConnectionFactory.hpp> // ... int buf_size = 100; ConnectionFactory<double> cf; ConnectionInterface::shared_ptr con = cf.createBuffer(a_task->ports()->getPort("SetPoint_X"), b_task->ports()->getPort("SetPoint_X"), buf_size); if (con) con->connect(); before the tasks are connected. This connection will then take precedence. |
The Data Flow interface is used by your task from within
the program scripts or its update()
method. Logically the script or method reads the inbound
data, calculates something and writes the outbound data.
#include <rtt/Ports.hpp>
using namespace RTT;
class MyTask
: public TaskContext
{
// ...Constructor sets up Ports, see above.
bool startup() {
// Check validity of (all) Ports:
if ( !indatPort.connected() ) {
// No connection was made !
return false;
}
if ( !outdatPort.connected() ) {
// ...
}
}
bool update() {
// Read and write the Data Flow:
// Unbuffered:
double val = indatPort.Get();
// calculate...
outdatPort.Set( val );
// Buffered:
if ( inbufPort.Pop( val ) ) {
// calculate...
} else {
// buffer empty.
}
if ( outbufPort.Push( val ) ) {
// ok.
} else {
// buffer full.
}
}
// ...
};
It is wise to check in the startup()
function if all necessary ports are connected()
( or ready() ).
At this point, the task startup can still be aborted by
returning false. Otherwise, a write to a port
will be discarded, while a read returns the initial value
or the default value. A Pop of a disconnected port will
always return false.
When a Port is connected, it becomes available to the Orocos scripting system such that (part of) the calculation can happen in a script. Also, the TaskBrowser can then be used to inspect the contents of the DataFlow online.
A small program script could be loaded into MyTask with the following contents:
program MyControlProgram {
double the_K = K // read task property, see later.
double setp_d
while ( true ) {
if ( SetPoint_X.Pop( setp_d ) ) { // read Buffer Port
double in_d = Data_R.Get() // read Data Port
double out_d = (setp_d - in_d) * K // Calculate
do Data_W.Set( out_d ) // write Data Port
}
do nothing // this is a 'wait' point.
}
} The program "MyControlProgram" starts with declaring two variables and reading the task's Property 'K'. Then it goes into an endless loop, trying to Pop a setpoint value from the "SetPoint_X" Buffer Port. If that succeeds (buffer not empty) the "Data_R" Data Port is read and a simple calculation is done. The result is written to the "Data_W" Data Port and can now be read by the other end. Alternatively, the result may be directly used by the Task in order to write it to a device or any non-task object. You can use methods (below) to send data from scripts back to the C++ implementation.
Remark that the program is executed within the Execution
Engine. In order to avoid the endless loop, a 'wait' point
must be present. The "do nothing" command inserts such a
wait point and is part of the Scripting syntax. If you plan
to use Scripting state machines, such a
while(true) loop and hence wait point
is not necessary. See the Scripting Manual for a full
overview of the syntax.
| Purpose | |
|---|---|
A task's methods are intended to be called 'synchronously' by the caller, i.e. are directly executed like a function. Use it to 'calculate' a result or change a parameter. |
![]() |
The easiest way to access a TaskContext's interface is through Methods. They resemble very much normal C or C++ functions, but they have the advantage to be usable in scripting or can be called over a network connection. They take arguments and return a value. The return value can in return be used as an argument for other Methods or stored in a variable. For all details, we refer to the Orocos Scripting Manual.
To add a TaskContext's method to the method interface, one proceeds similarly as when creating Data Ports. The data type is now replaced by a function signature, for example '
void(int, double)
' which is the signature of a function returning 'void' and having two arguments: an 'int' and a 'double'.
#include <rtt/Method.hpp>
using namespace RTT;
class MyTask
: public TaskContext
{
public:
void reset() { ... }
string getName() const { ... }
double changeParameter(double f) { ... }
// ...
Method<void(void)> resetMethod;
Method<string(void)> nameMethod;
Method<double(double)> paramMethod;
MyTask(std::string name)
: TaskContext(name),
resetMethod("reset", &MyTask::reset, this),
nameMethod("name", &MyTask::getName, this),
resetMethod("changeP", &MyTask::changeParameter, this)
{
// Add the method objects to the method interface:
this->methods()->addMethod( &resetMethod, "Reset the system.");
this->methods()->addMethod( &nameMethod, "Read out the name of the system.");
this->methods()->addMethod( &changeP,
"Change a parameter, return the old value.",
"New Value", "The new value for the parameter.");
// more additions to follow, see below
}
// ...
};
In the above example, we wish to add 3 class functions to the method interface: reset, getName and changeParameter. This can be done by constructing a Method object with the correct function signature for each such class function. Each Method object is initialised in the constructor with a name ("reset"), a pointer to the class function (&MyTask::reset) and a pointer to the class object (this). This setup allows the method objects resetMethod, nameMethod and paramMethod to be invoked just like one would call the functions directly.
After the method objects are constructed, we add methods to the method interface using the addMethod() function. The addMethod() function requires a a method object (&resetMethod), a description ("Reset the system.") and a name, description pair for each argument (such as in changeParameter).
Using this mechanism, any method of any class can be added to a task's method interface.
In order to easily invoke a task's methods from a C++ program, only only needs a pointer to a TaskContext object, for example using the 'getPeer()' class function.
// create a method:
TaskContext* a_task_ptr;
Method<void(void)> my_reset_meth
= a_task_ptr->methods()->getMethod<void(void)>("reset");
// Call 'reset' of a_task:
reset_meth(); Methods can also be given arguments and collect return values. Both constant arguments and variable arguments are supported.
// used to hold the return value:
string name;
Method<string(void)> name_meth =
a_task_ptr->methods()->getMethod<string(void)>("name");
// Call 'name' of a_task:
name = name_meth();
cout << "Name was: " << name << endl;
// hold return value.
double oldvalue;
Method<double(double)> mychange_1 =
a_task.methods()->create("changeP");
// Call 'changeParameter' of a_task with argument '1.0'
oldvalue = mychange_1( 1.0 );
// oldvalue now contains previous value.Up to 4 arguments can be given. If the signature was not correct, the method invocation will be ignored. One can check validity of a method object with the 'ready()' function:
Method<double(double)> mychange_1 = ...; assert( mychange_1.ready() );
The arguments can be of any class type and type qualifier (const, &, *,...). However, to be compatible with the Orocos Program Parser variables, it is best to follow the following guidelines :
Table 2.1. Method Return & Argument Types
| C++ Type | In C++ functions passed by | Maps to Parser variable type |
|---|---|---|
| Primitive C types : double, int, bool, char | value (no const, no reference ) | double, int, bool, char |
| C++ Container types : std::string, std::vector<double> | const & | string, array |
| Orocos Fixed Container types : RTT::Double6D, KDL::[Frame | Rotation | Twist | ... ] | const & | double6d, frame, rotation, twist, ... |
Summarised, every non-class argument is best passed by value, and every class type is best passed by const reference. The parser does handle references (&) in the arguments or return type as well.
| Purpose | |
|---|---|
A task's attributes and properties are intended to configure and tune a task with certain values. Properties have the advantage of being writable to an XML format, hence can store 'persistent' state. For example, a control parameter. Attributes are lightweight values which can be read and written during runtime, for example, the current measured temperature. |
![]() |
A TaskContext may have any number of attributes or properties, of any type. They can be used by programs in the TaskContext to get (and set) configuration data. The task allows to store any C++ value type and also knows how to handle Property objects. Attributes are plain variables, while properties can be written to and updated from an XML file.
An attribute can be added in the task's interface (AttributeRepository) like this :
#include <rtt/Property.hpp>
#include <rtt/Attribute.hpp>
class MyTask
: public TaskContext
{
Attribute<bool> aflag;
Attribute<int> max;
Constant<double> pi;
Property<std::string> param;
Property<double> value;
public:
// ...
MyTask(std::string name)
: TaskContext(name),
param("Param","Param Description","The String"),
value("Value","Value Description", 1.23 ),
aflag("aflag", false),
max( "max", 5 ),
pi( "pi", 3.14 )
{
// other code here...
this->attributes()->addAttribute( &aflag );
this->attributes()->addAttribute( &max );
this->attributes()->addConstant( &pi );
this->properties()->addProperty( ¶m );
this->properties()->addProperty( &value );
}
// ...
};
Which inserts an attribute of type bool and int, name 'aflag' and 'max' and initial value of false and 5 to the task's interface. A constant 'pi' is added as well. The methods return false if an attribute with that name already exists. Adding a Property is also straightforward. The property is added in a PropertyBag.
To get a value from the task, you can use the set() and get() methods :
bool result = aflag.get();
assert( result == false );
param.set("New String");
assert( param.get() == "New String" );While another task can access it through the attributes() interface:
Attribute<bool> the_flag = a_task->attributes()->getAttribute<bool>("aflag");
assert( the_flag.ready() );
bool result = the_flag.get();
assert( result == false );
Attribute<int> the_max = a_task->attributes()->getAttribute<int>("max");
assert( the_max.ready() );
the_max.set( 10 );
assert( the_max.get() == 10 );The attributes 'the_flag' and 'the_max' are called 'mirrors' of the original attributes of the task.
A program script can access the above attributes as in
// a program in "ATask" does : var double pi2 = pi * 2. var int myMax = 3 set max = myMax set Param = "B Value"
// an external (peer task) program does : var double pi2 = ATask.pi * 2. var int myMax = 3 set ATask.max = myMax
When trying to assign a value to a constant, the script parser will throw an exception, thus before the program is run. You must always specify the task's name (or 'task') when accessing a task's attribute, this is different from methods and commands, which may omit the task's name if the program is running within the task.
| Important | |
|---|---|
The same restrictions of Section 3.7, “Method Argument and Return Types” hold for the attribute types, when you want to access them from program scripts. |
See Section 6.1, “Task Property Configuration” for storing and loading the Properties to and from files, in order to store a TaskContext's state.
| Purpose | |
|---|---|
A task's commands are intended to be called 'asynchronously', thus sent by the caller to the receiver. Use it to 'reach a goal' in the receiver, typically this takes time to accomplish. Command functions are, in contrast with methods, executed by the receiver. |
![]() |
The command interface is very similar to the Method interface above.
To add a command to the Command Interface, one must create Command, objects :
#include <rtt/Command.hpp>
class MyTask
: public TaskContext
{
public:
/**
* The first command starts a cycle.
*/
bool startCycle() { ... }
bool cycleDone() const { ... }
Command<bool(void)> cycleCommand;
/**
* Another command cleans stuff up.
*/
bool cleanupMess(double f) { ... }
bool isMessCleaned() const { ... }
Command<bool(double)> messCommand;
public:
MyTask(std::string name)
: TaskContext(name),
cycleCommand("startCycle",
&MyTask::startCycle,
&MyTask::cycleDone, this),
messCommand( "cleanup",
&MyTask::cleanupMess,
&MyTask::isMessCleaned, this)
{
// ... other startup code here
this->commands()->addCommand( &cycleCommand,
"Start a new cycle.");
this->commands()->addCommand( &messCommand,
"Start cleanup operation.",
"cfactor", "A cfactor denoting the thoroughness.");
}
};Commands differ from Methods in that they take an extra function which is called the Completion Condition. It is a function which returns true when the command is done. The command itself also returns a boolean which indicates if it was accepted or not. Reasons to be rejected can be faulty arguments or that the system is not ready to accept a new command.
The Command object requires two member
pointers instead of one, which must both return a 'bool'. The
first one is the command function that does the actual work,
and the completion condition is a function having :
exactly the same arguments as the command,
OR only the first argument of the command,
OR no arguments at all.
Analogous to addMethod(),
addCommand adds the Command objects to
the TaskContext interface and also requires a string
describing the command, and two strings giving a name and
description for every argument.
Once a command is added to a TaskContext's interface, other tasks can make use of that command.
The Command class can be used to invoke commands as well. You can get such object from a task's interface:
Command<bool(void)> mycom
= a_task.commands()->getCommand<bool(void)>("startCycle");
// check if the command is ok:
assert( mycom.ready() );
// Send 'startCycle' to a_task (asynchronous).
bool result = mycom();
// next, check its status:
bool accepted = mycom.accepted(); // accepted by execution engine?
bool valid = mycom.valid(); // command was valid (well-formed)?
bool done = mycom.done(); // command was done?
Such commands can also be given arguments. Both constant arguments and variable arguments are supported:
// get a command:
Command<bool(double)> mycleanup_1 =
a_task.commands()->getCommand<bool(double)>("cleanup");
// Send 'cleanup' to a_task with argument '1.0'
bool result_1 = mycleanup_1( 1.0 );
bool d_arg = 5.0;
// Send 'cleanup' to a_task, reads contents of d_arg.
bool result_2 = mycleanup_1(d_arg);The current implementation supports up to 4 arguments. Since the use of 'structs' is allowed, this is enough for most applications.
The above lets you write in a program script :
do startCycle() do cleanup( 0.1 )
when the program is loaded in a_task.
Commands returning false will propagate that error to the program or function calling that command, which will cause the program to enter an error state, ie it stops its execution.
| Important | |
|---|---|
The same restrictions of Section 3.7, “Method Argument and Return Types” hold for the command and condition types, when you want to access them from program scripts. |
| Purpose | |
|---|---|
A task's events are intended to be 'emitted', thus published by the task to subscribers. Use it to 'notify' interested parties of a change in the system. |
![]() |
A task may register its events in its interface in order to be used by its state machines and other tasks as well. Events are defined and explained in the Orocos CoreLib Reference Manual.
Events can be easily added to a task's interface, much like methods are:
#include <rtt/Event.hpp>
class MyTask
: public TaskContext
{
// An event with a bool argument:
Event< void(bool) > turnSwitch;
// An event with three arguments:
Event< bool(double, double, double) > moveAxis;
public:
MyTask(std::string name)
: TaskContext(name),
turnSwitch( "turnSwitch" ),
moveAxis( "move" )
{
// ... other startup code here
// add it to the task's interface:
this->events()->addEvent( &turnSwitch,
"Turn switch description",
"d","Direction" );
this->events()->addEvent( &moveAxis,
"x","X axis position",
"y","Y axis position",
"z","Z axis direction");
}
};An Event object has the signature ('void(bool)') of the 'callback function' it will call when the event is 'emitted' (or 'fired'). The object is initialised with a name ("turnSwitch") and added to the interface ('addEvent').
Once events are added, they can be emitted using the Event object.
Event< bool(double, double, double) > move_event = a_task.events()->getEvent( "move" ); assert( move_event.ready() ); // emit the event 'move' with given args: move_event(1.0, 2.0, 3.0); // or with variable arguments: double a = 0.3, b = 0.2, c = 0.1; move_event(a, b, c);
Analogous to emitting an event, one can also react to an event in C++, using the Event interface. Event connections can be accessed through the Handle object. The first example shows how to setup a synchronous connection to the event of the previous examples:
#include <boost/bind.hpp>
/**
* Example: Connect a class method to an Event.
*/
class Reactor
{
public:
bool react_callback(double a1, double a2, double a3) {
// use a1,a2, a3
return false; // return value is ignored.
}
};
/**
* Example: Connect a 'C' function to an Event.
*/
bool foo_callback( double a1, double a2, double a3 ) {
// use a1, a2, a3
return false; // ignored.
}
// Class callback:
Reactor r;
Handle h
= a_task.events()->setupConnection("move")
.callback( &r, &Reactor::react_callback )
.handle();
assert( h.ready() );
h.connect(); // connect to event "move"
move_event(1.0, 2.0, 3.0); // see previous example.
// now Reactor::callback() was called.
h.disconnect(); // disconnect again.
// 'C' Function callback:
h = a_task.events()->setupConnection("move")
.callback( &foo_callback )
.handle();
h.connect();
move_event(4.0, 5.0, 6.0)
// now foo_callback is called with arguments. | Note | |
|---|---|
Using the |
Analogous to the event example in the CoreLib Reference Manual,
a class is made to react to the event. A connection
is setup between the "move" event and the react_callback
function of "r". The connection can be controlled using
the handle to connect or disconnect the reaction to events.
When connect() is called, every
event invocation will call react_callback()
with the given arguments. Using a 'C' function works analogous as shown
above.
A second example continues, but uses an asynchronous connection. First a new task (b_task) is created which will handle the event asynchronously. During setup, the EventProcessor of b_task's Execution Engine is used to process the event.
TaskContext b_task("BTask"); PeriodicActivity ptask_b(5, 0.1); // priority, period ptask_b.run( &b_task ); ptask_b.start(); Handle h3 = a_task.events()->setupConnection("move") .callback(&r, &react_callback, b_task.engine()->events() ).handle(); assert( h3.ready() ); h3.connect(); // connect asynchronously to event "move" move_event.emit(); // see previous example. // wait a bit... // now react_callback() was called from within b_task's execution engine.
Note that after passing the object and function, the EventProcessor
of b_task is added in the callback method, such
that the callback is executed in b_task's thread.
Events are as easy to use as methods (above) from within scripts, using the keyword do:
do ATask.move( 1.0, 2.0, 3.0 )
It is also possible to react to events from within a state machine in order to change state. We refer to the Program Parser Manual for syntax and examples.
A Real-Time system contains multiple concurrent tasks which must communicate to each other. TaskContext can be connected to each other such that they can communicate Real-Time data.

The addPeer and connectPeers
functions are used to connect TaskContexts and allow them
to use each other's interface. The connectPorts
funtion sets up the data flow between tasks.
We call connected TaskContexts "Peers" as there is no fixed
hierarchy. A connection from one TaskContext to its
Peer can be uni- or bi-directional. In a uni-directional connection (addPeer ),
only one peer can use the interface of the other, while
in a bi-directional connection (connectPeers), both can use
each others interface.
This allows to build strictly hierarchical topological
networks as well as complete flat or circular networks or any
kind of mixed network.
Peers are connected as such (hasPeer takes a string
argument ):
// bi-directional :
connectPeers( &a_task, &b_task );
assert( a_task.hasPeer( &b_task.getName() )
& b_task.hasPeer( &a_task.getName() ) );
// uni-directional :
a_task.addPeer( &c_task );
assert( a_task.hasPeer( &c_task.getName() )
& ! c_task.hasPeer( &a_task.getName() ) );
// Access the interface of a Peer:
Method<bool(void) m = a_task.getPeer( "CTask" )->methods()->getMethod<bool(void)>("aMethod");
// etc. See interface usage in previous sections.
Both connectPeers and addPeer
allow scripts or C++ code to use the interface of a connected Peer. connectPeers
does this connection in both directions.
From within a program script, peers can be accessed by merely prefixing their name to the member you want to access. A program within "ATask" could access its peers as such :
var bool result = CTask.aMethod()
The peer connection graph can be traversed at arbitrary depth. Thus you can access your peer's peers.
Data Flow between TaskContexts is setup by using connectPorts.
The direction of the data flow is imposed by the read/write direction of
the ports. The connectPorts(TaskContext* A, TaskContext* B) function
creates a connection between TaskContext ports when both ports
have the same name and type. It will never disconnect existing connections
and only tries to add ports to existing connections or create new
connections.
Before calling connectPorts, one may connect
individual ports using a_port.connectTo(&b_port);
when complexer data flow networks need to be formed.
Example 2.1. TaskContext Data Flow Topology Example
This diagram shows some possible topologies. Four tasks, "A", "B", "C" and "D" have each a port "MyData" and a port "MyData2". The example demonstrates that connections are always made from writer (sender) to reader (receiver).

Example data flow networks.
The first network has two writers and two readers for "MyData". It can be formed starting from "A" and adding "B","C" and "D" as peers of "A" respectively. Since the network started from "A" all peers share the same connection. The same network could have been formed starting from "B". The second diagram connects "A" to "C" and then "B" to "D". Two connections are now made and if the application tries to connect "B" to "C", this will fail since the "MyData" Port of "C" already participates in a connection.
The third network has one writer and three readers for "MyData2". It can now only be formed starting from "D" and adding "A","B" and "C" as peers to "D". Combining both network one and three is possible by merely invoking all the 'addPeer' methods in the correct order.
connectPorts tries to create connections in
both directions. If a task's Port already has a connection, any
task with compatible, unconnected ports will be added to that
connection. For example, if "a_task" and "b_task" exchange a
Data type "Data_X", and "c_task" reads "Data_X", the
connectPorts will forward "Data_X" to "c_task"
as well.
Tasks can be disconnected from a network by invoking
disconnect() on that task. It will
inform all its peers that it has left the network and
disconnect all its ports. This does not mean that
all data flow connections are deleted. As long as one task's
port still participates in a connection, the connections
exist. When the last port disconnects, the data flow connection
is cleaned up as well.
Example 2.2. TaskContext Peer Disconnection Example
(1) shows what would happen if tasks "A" and "B" are disconnected from the network. (2) shows what would happen if the connection itself is disconnected.

Disconnecting tasks: one can disconnect a whole task or disconnect only a port or connection of a task.
When A.disconnect() is called (1),
it removes its participation from the "MyData" connection.
The same happens for B.disconnect().
"C" and "D" read then from a connection which has no more
writers. Adding "A" again to the network would make "A"
again a writer of "MyData". If both "C" and "D" call disconnect
as well, the "MyData" connection is cleaned up.
a_task.disconnect();
assert( !a_task.hasPeer( &b_task.getName() )
&& !b_task.hasPeer( &a_task.getName() );
b_task.disconnect();
assert( !c_task.hasPeer( &b_task.getName() )
&& ! d_task.hasPeer( &b_task.getName() );
Data Flow connections can be disconnected (2) as well, in which case all ports are disconnected.
ConnectionInterface::shared_ptr con = a_task.ports()->getPort("MyData")->connection();
if (con)
con->disconnect();
assert( !a_task.ports()->getPort("MyData").connected() );
assert( !b_task.ports()->getPort("MyData").connected() );
assert( !c_task.ports()->getPort("MyData").connected() );
assert( !d_task.ports()->getPort("MyData").connected() );
An Orocos component can be used in both embedded (<1MB RAM) or big systems (128MB RAM), depending on how it is created or used. This is called Component Deployment as the target receives one or more component implementations. The components must be adapted as such that they fit the target.
Figure 2.6, “ Component Deployment Levels ” shows the distinction between the three levels of Component Deployment.
Figure 2.6. Component Deployment Levels

Three levels of using or creating Components can be accomplished in Orocos: Not distributed, embedded distributed and fully distributed.
If your application will not use distributed components and requires a very small footprint, the TaskCore can be used. The Orocos primitives apear directly in the interface and are called upon in a hard-coded way.
If you application requires a small footprint and distributed components, the C++ Interface of the TaskContext can be used in combination with a Distribution Library which does the network translation. It handles a predefined set of data types (mostly the 'C' types) and needs to be adapted if other data types need to be supported.
If footprint is of no concern to your application and you want to distribute any component completely transparantly, the TaskContext can be used in combination with a Remoting Library which does the network translation. A CORBA implementation of such a library is being developed on. It is a write-once, use-many implementation, which can pick up user defined types, without requiring modifications. It uses the Orocos Type System to manage user defined types.
A TaskCore is nothing more than a place holder for the
Execution Engine and application code functions
(startup(), update()
and shutdown() ). The Component
interface is built up by placing the Orocos primitives
as public class members in a TaskCore subclass. Each
component that wants to use this TaskCore must get a
'hard coded' pointer to it (or the interface it implements)
and invoke the command, method etc. Since Orocos is by
no means informed of the TaskCore's interface, it can not
distribute a TaskCore.
Instead of putting the Orocos primitives in the public interface of a subclass of TaskCore, one can subclass a TaskContext and register the primitives to the C++ Interface. This is a reduced interface of the TaskContext, which allows distribution by the use of a Distribution Library.
| Note | |
|---|---|
The code presented is for commands, but can be equally applied for methds by using methods()->addMethod( & method ) for events and for each other Orocos primitive. |
The process goes as such: A component inherits from TaskContext and has some Orocos primitives as class members. Instead of calling:
commands()->addCommand(&com, "Description", "Arg1","Arg1 Description",...);and providing a description for the primitive as well as each argument, one writes:
commands()->addCommand( &com );This is no more than a pointer registration, but already allows all C++ code to use the added primitive.
In order to access the interface of such a Component, the user code may use:
taskA->commands()->getCommand("Name");In order to distribute this component, an implementation of the Distribution Library is required. The specification of this library, and the application setup is in left to another design document.
In case you are building your components as instructed in this manual, your component is ready for distribution as-is, given a Remoting library is used. The Orocos CORBA package implements such a Remoting library.
Using the three levels of deployment in one application is possible as well. To save space or execution efficiency, one can use TaskCores to implement local (hidden) functionality and export publicly visible interface using a Taskcontext. Figure 2.7, “ Example Component Deployment. ” is an small example of a TaskContext which uses two TaskCores to delegate work to. The Execution Engines may run in one or multiple threads.
This section elaborates on the interface all Task Contexts have from a 'Task user' perspective.
As was seen in Section 3.8, “The Attributes and Properties Interface”, Property objects can be added to a task's interface. To read and write properties from or to files, you can use the TaskContext class' methods. It creates or reads files in the XML Component Property Format such that it is human readable and modifiable.
// ... TaskContext* a_task = ... a_task->readProperties( "PropertyFile.cpf" ); // ... a_task->writeProperties( "PropertyFile.cpf" );
Where readProperties() reads the file and updates the
task's properties and writeProperties() updates the
given file with the properties of the task. It is allowed to
share a single file with multiple tasks or update the task's
properties from multiple files.
Orocos supports two types of scripts:
An Orocos Program Script (ops) contains a Real-Time functional program which calls methods and sends commands to tasks, depending on classical functional logic.
An Orocos State machine Description (osd) script contains a Real-Time (hierarchical) state machine which dictates which program script snippets are executed upon which event.
Both are loaded at runtime into a task. The scripts are parsed to an object tree, which can then be executed by the ExecutionEngine of a task.










