Tutorial on the Component Interface

Open RObot COntrol Software

1.12.1

Orocos Version 1.12.1.

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 on the different aspects of interfacing an Orocos component. This is an excerpt from the Component Builder's Manual and walks through the 'Hello World' program.


Table of Contents

1.
1. Introduction
2. Hello World !
2.1. Setting up the TaskBrowser
2.2. Starting your First Application
2.3. Displaying a TaskContext
2.4. Listing the Interface
2.5. Calling a Method
2.6. Sending a Command
2.7. Changing Values
2.8. Emiting Events
2.9. Reading and Writing Data Ports
2.10. Advanced Component Browsing
2.11. Last Words

List of Figures

1.1. Components Run in Threads
1.2. Schematic Overview of a TaskContext
1.3. Schematic Overview of the Hello Component.

Chapter 1. 

1. Introduction

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 environment or "context" in which an application specific task is executed. The context is described 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.

Figure 1.1.  Components Run in Threads

Components Run in Threads

Components run in (periodic) threads and can communicate transparently. The Orocos RTT does a 'best effort' to deliver the highest performance to the highest priority threads.


A component 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 components can communicate with non real-time components (and vice verse) transparently.

[Note]Note

In this manual, the words task and component are used as equal words, meaning a software component built using the C++ TaskContext class.

The Orocos Component Model 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 chapter relates to other chapters as such :

Core Library

provides the Command and Event infrastructure, activity to thread mapping, Properties and lock-free data exchange implementations.

Execution Engine

executes the commands, events, real-time programs and scripts in a component.

Orocos Scripting

provides a real-time scripting language which is convertible to a form which can be accepted by the Execution Engine.

The Scripting chapter gives more details about script syntax for state machines and programs.

Figure 1.2.  Schematic Overview of a TaskContext

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 verse.


A component'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.

2. Hello World !

This section introduces tasks through the "hello world" application, which is included in the Orocos Component Library. It contains one TaskContext component, HelloWorld, which has one instance of each communication primitive.

Figure 1.3.  Schematic Overview of the Hello Component.

Schematic Overview of the Hello Component.

Our hello world component.


2.1. Setting up the TaskBrowser

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. 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.

[Note]Note

The 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;
  }
      

2.2. Starting your First Application

Now let's start the helloworld application. If you downloaded OCL and compiled it from source, You can do this by entering the helloworld subdirectory of your OCL build directory and running ./helloworld

In case you got OCL as a binary package, enter loadComponent("Hello","orocos-helloworld") at the prompt of the deployer application for your target: ORO_LOGLEVEL=5 deployer-gnulinux for example. This command loads the Orocos-HelloWorld component library and creates a component with name "Hello" (Requires OCL 1.4.1 or later). Finally, type cd Hello to start with the exercise.

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 appeared.

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
      

2.3. Displaying a TaskContext

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]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 the_data_port and the_buffer_port objects are created to represent the data ports of the Hello component. They allow you to to send or receive data to these ports and check if they are connected.

Last, the peers are shown, that is, the components which are known, and may be used, by this component. The HelloWorld component is a stand-alone component and has only the TaskBrowser as a peer.

2.4. Listing the Interface

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.

2.5. Calling a Method

 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". This works just like calling a 'C' function.

2.6. Sending a Command

When a command is entered, it is sent to the Hello component, which will execute it in its own threadon 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) :         
      

2.7. Changing Values

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 !
      

2.8. Emiting Events

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.

2.9. Reading and Writing Data Ports

The Data Ports can be accessed through the the_data_port and the_buffer_port object interfaces.

Initially, these ports are unconnected, as the HelloWorld component did not connect its ports to another component. Unconnected ports can hardly be used. In order to test them, you can instruct the TaskBrowser to connect to them. This is done by entering the .connect command (note the '.').

Since each port has an associated object, we can inspect the interface of a port 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.

2.10. Advanced Component Browsing

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. In this example, the TaskBrowser has the same ports as the component it visits: the_data_port and the_buffer_port. These were created when we issued the .connect previously. Otherwise, there would be no data ports.

One can return to the 'inside' view again by typing 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) :      
      

2.11. Last Words

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 end user-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]Note

If you want a more in-depth tutorial, see the 'task-intro' example for a TaskBrowser which visits a network of three TaskContexts.