The Deployment Component

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.


Table of Contents

1. Introduction
1.1. Principle
2. Configuration Procedure
2.1. Where to look for component libraries
2.2. Which components to create and with which name
2.3. How each component is setup
3. Setting up a deployable component library
3.1. Additional Code
3.2. Compiling and linking a component library

1. Introduction

This document describes the Orocos OCL::DeploymentComponent for loading and configuring other components using an Orocos script or XML file. This component can only load components into the same process.

1.1. Principle

Each Orocos component can be compiled as a shared, dynamic loadable library. Each such library can define a special function which will allow the DeploymentComponent to create new instances of a component type. This principle is analogous to the plugin mechanism found in web browsers or other desktop applications.

A common usage scenario of the DeploymentComponent goes as follows. An initial Orocos application is created which contains only the DeploymentComponent and the OCL::TaskBrowser. When the application is started, the TaskBrowser prompts for commands which can be given to the DeploymentComponent.

Figure 1. Component Deployment Overview

Component Deployment Overview

Components are located on your disk using the 'import' statement, loaded using 'loadComponents' and configured using 'configureComponents'. These three steps can be described in an XML file format or using the command prompt.


Figure 1, “Component Deployment Overview” shows the basic steps. An XML file contains instructions for the DeploymentComponent where to look for components ('import statements'), which component types to create, which name they must be given and how their internal thread is configured (priorities, periods,...). Furthermore this file describes the network interconnections between all components and how data must be relayed from one component to another. The loadComponents("file.xml") method reads this file, looks up the components, creates them and stores the configuration parameters. One can apply the configuration (threads, properties, data connections,...) by calling configureComponents(). After this step, the components (and the application as a whole) can be started.

The configuration does not need to be stored in XML format. One can apply the same configuration by using the scripting methods of the DeploymentComponent at the console prompt, or by listing them in an Orocos script.

2. Configuration Procedure

Figure 2. Deployment Procedure

Deployment Procedure

The Deployment component API consists of import, loadComponents, configureComponents and startComponents.


The configuration format defines the instructions one can use to load and configure Orocos components. One can divide the instructions in three groups:

  • Where to look for component libraries

  • Which components to create and with which name

  • How each component is setup

Let's demonstrate this principle with a simple application example as shown in Figure 3, “Deployment Example Application”. We want to setup an application with three components: a Reporting component, a 'Controller' and a 'Plant'. The Plant component provides access to the hardware, the Controller component contains the control algorithm. The Reporting component is here to sense the values exchanged and write them to a file.

Figure 3. Deployment Example Application

Deployment Example Application

A Reporter component monitors communication between Plant and Controller. The Deployment component itself is not shown.


2.1. Where to look for component libraries

The import statement causes all component libraries in a given directory to be pre-loaded. It does not cause any component to be created, but allows the OCL::DeploymentComponent to know where the component libraries are located. This function works recursively and may be called for multiple paths.

In XML, the import statement looks like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "cpf.dtd">
<properties>
  <!-- ....  -->

  <simple name="Import" type="string"><value>/usr/local/lib</value></simple>

</properties>

The script method equivalent is:

  import("/usr/local/lib")

Each library found under the given location is pre-loaded. The import statement allows you to load non-Orocos libraries as well. If a library contains one or more Orocos components, the contained component types become available in the next step.

To see the effects of the import function, the available types can be queried by invoking the displayComponentTypes (script) method:

 (type 'ls' for context info) :displayComponentTypes()
      Got :displayComponentTypes()
 = I can create the following component types:
   OCL::ConsoleReporting
   OCL::FileReporting
(void)

2.2. Which components to create and with which name

Import makes components available, but does not create an specific instance yet. In order to add a component of a given type to the current application, use the loadComponent function:

In XML, the loadComponent statement of a reporting component would look like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "cpf.dtd">
<properties>
  <!-- ... import statements locate Orocos reporting library ...  -->
  <simple name="Import" type="string"><value>/usr/local/lib</value></simple>

  <struct name="Reporter" type="OCL::FileReporting">
  </struct>

</properties>

This line causes the DeploymentComponent to look up the OCL::FileReporting type, and if found, creates a component of that type with the name "Reporter". This component is added as a peer component to the DeploymentComponent such that it becomes immediately available to the application. This step can be repeated any number of times with any number of types.

Alternatively, the type may be a filename if that file contains only one component, which is exported using the createComponent function.

The script method equivalent is:

  loadComponent("Reporter", "OCL::FileReporting")

2.3. How each component is setup

Now that one or more component instances are created, you can configure them by connecting components, assigning threads, configuration values and program scripts. Again, you can do this using XML or the scripting language.

In XML, one adds additional elements to the component struct.

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE properties SYSTEM "cpf.dtd">
<properties>
  <simple name="Import" type="string"><value>/usr/local/lib</value></simple>

  <struct name="Reporter" type="OCL::FileReporting">

    <struct name="Activity" type="PeriodicActivity">
      <simple name="Period" type="double"><value>0.005</value></simple>
      <simple name="Priority" type="short"><value>0</value></simple>
      <simple name="Scheduler" type="string"><value>ORO_SCHED_OTHER</value></simple>
    </struct>

    <simple name="PropertyFile" type="string"><value>file-reporting.cpf</value></simple>

    <struct name="Peers" type="PropertyBag">
      <simple type="string"><value>Controller</value></simple>
    </struct>
  </struct>

  <struct name="Controller" type="ControllerType">

    <struct name="Activity" type="PeriodicActivity">
      <simple name="Period" type="double"><value>0.001</value></simple>
      <simple name="Priority" type="short"><value>99</value></simple>
      <simple name="Scheduler" type="string"><value>ORO_SCHED_RT</value></simple>
    </struct>

    <simple name="AutoConf" type="boolean"><value>1</value></simple>
    <simple name="AutoStart" type="boolean"><value>1</value></simple>

    <simple name="PropertyFile" type="string"><value>controller.cpf</value></simple>

    <struct name="Ports" type="PropertyBag">
      <simple name="SensorValues" type="string"><value>SensorValuesConnection</value></simple>
      <simple name="SteeringSignals" type="string"><value>DriveConnection</value></simple>
    </struct>

    <struct name="Peers" type="PropertyBag">
      <simple type="string"><value>Plant</value></simple>
    </struct>

    <simple name="ProgramScript" type="string"><value>controller-program.ops</value></simple>
    <simple name="StateMachineScript" type="string"><value>controller-states.ops</value></simple>
  </struct>

  <struct name="Plant" type="PlantType">
    <struct name="Activity" type="NonPeriodicActivity">
      <simple name="Priority" type="short"><value>0</value></simple>
      <simple name="Scheduler" type="string"><value>ORO_SCHED_RT</value></simple>
    </struct>
    <simple name="AutoStart" type="boolean"><value>1</value></simple>
    <struct name="Ports" type="PropertyBag">
      <simple name="Position" type="string"><value>SensorValuesConnection</value></simple>
      <simple name="Velocity" type="string"><value>DriveConnection</value></simple>
    </struct>
  </struct>
</properties>

The first section of all three components sets up the active behaviour of the component. Both have periodic activities, which run with a given period, priority and in a scheduler. The Controller and Plant run in a real-time scheduler, the Reporter doesn't. The activities are created and attached to each component during the configureComponents() step of the DeploymentComponent.

The next section of the Controller contains the AutoConf and AutoStart elements. If AutoConf is set to 1, the DeploymentComponent will call the component's configure() method during configureComponents(), after the properties are loaded. If AutoStart is set to 1, the component's start() method will be called during startComponents().

The PropertyFile section specifies from which file each component is configured. The CPF files contain XML properties as well which are read during the configureComponents() step of the DeploymentComponent.

The last section of the Reporter component lists its peers. The Reporter has one peer, the Controller, with which it will be able to communicate and monitor its data ports. The Controller component has the Plant as peer, which means it can control it. It has two data ports listed as well (SensorValues and SteeringSignals), which are added to two connection objects. These connections show up in the Plant component's Ports section as well. And it shows that the SensorValues Port is connected to the Position Port and the SteeringSignals Port is connected to the Velocity Port. If other component's ports in the same file refer to the same connection object, the ports are connected to each other by the DeploymentComponent during the configureComponents() step. The Component Builder's Manual goes into detail about the possible connection mechanisms.

The Controller has at the end two additional elements describing which script files must be loaded into that component. Both Orocos program scripts and state machine scripts can be loaded. This is again done during the configureComponents() step.

3. Setting up a deployable component library

This section explains how to prepare a component library for deployment. It is demonstrated with an example.

3.1. Additional Code

There exist three C macros for preparing a component library. The simplest way is when the resulting library will contain only one component type. Assume we have written the OCL::HelloWorld component ( in the OCL C++ namespace) which is compiled in the orocos-helloworld.so library. The following code is added to HelloWorld.cpp:

  #include "HelloWorld.hpp"
  #include <ocl/ComponentLoader.hpp>

  /* ... Hello World implementation file ... */

  // You must specify the namespace:
  ORO_CREATE_COMPONENT( OCL::HelloWorld )

This macro inserts a function into the library which will allow the DeploymentComponent to create OCL::HelloWorld components.

In case multiple components are defined in the same library, two other macros must be used. One macro for each component type and one macro once for the whole library. Say your library has components NS::ComponentX and NS::ComponentZ in namespace NS. In order to export both components, you could write in ComponentX.cpp:

  #include "ComponentX.hpp"
  #include <ocl/ComponentLoader.hpp>

  /* ... ComponentX implementation file ... */
  // once:
  ORO_CREATE_COMPONENT_TYPE
  // For the ComponentX type:
  ORO_LIST_COMPONENT_TYPE( NS::ComponentX )

and in ComponentY.cpp the same but without the ORO_CREATE_COMPONENT_TYPE macro:

  #include "ComponentY.hpp"
  #include <ocl/ComponentLoader.hpp>

  /* ... ComponentY implementation file ... */

  // For the ComponentY type:
  ORO_LIST_COMPONENT_TYPE( NS::ComponentY )

For each additional component in the same library, the ORO_LIST_COMPONENT_TYPE macro is added. It is allowed to put all the ORO_LIST_COMPONENT_TYPE macros in a single file.

3.2. Compiling and linking a component library

In order to have a working library, care must be taken of the compilation flags. You may compile your library static or shared. But a static library will not be dynamically loadable. In the final executable the DeploymentComponent will be able to find the linked in components and setup the application using the XML file.

The macros need some help to figure out if you are compiling a shared or static library. You need to define the OCL_DLL_EXPORT macro when compiling for a shared library. If this macro is not defined, it is assumed that you are compiling for a static library.

The compilation flag of a component for a shared library might look like:

  CFLAGS= -O2 -Wall -fPIC -DOCL_DLL_EXPORT
  LDFLAGS= -fPIC

The compilation flag of a component for a static library lacks both options :

  CFLAGS= -O2 -Wall
  LDFLAGS=