Hello world 3 - RTT Tutorial: Data Ports¶
The source code of this tutorial can be found in the GitHub repository.
In this tutorial we will create 2 different components and show you how they can send
and receive data from each other. Both components are defined in the HelloWorld.ccp
file.
It is recommended to first read Data Flow Ports before starting this tutorial.
Contents of HelloWorld.cpp
:
namespace Example
{
/**
* Every component inherits from the 'TaskContext' class. This base
* class allow a user to add a primitive to the interface and contain
* an ExecutionEngine which executes application code.
*/
class Hello
: public TaskContext
{
protected:
/**
* @name Input-Output ports
* @{
*/
/**
* OutputPorts publish data.
*/
OutputPort<double> output;
/**
* InputPorts read data.
*/
InputPort< double > input;
/** @} */
/**
* Since read() requires an argument, we provide this
* attribute to hold the read value.
*/
double read_helper;
public:
/**
* This example sets the interface up in the Constructor
* of the component.
*/
Hello(std::string name)
: TaskContext(name, PreOperational),
// Name, initial value
output("output", 0.0),
// Name
input("input"),
read_helper(0.0)
{
this->addAttribute("read_helper", read_helper);
this->ports()->addPort( output ).doc("Data producing port.");
this->ports()->addPort( input ).doc("Data consuming port.");
}
bool configureHook()
{
// configuration only succeeds if the input port is connected
return input.connected();
}
void updateHook()
{
while ( input.read(read_helper) == NewData )
{
log(Info) << "Received data : " << read_helper <<endlog();
output.write( read_helper );
}
}
};
/**
* World is the component that shows how to use the interface
* of the Hello component.
*
* This component is a pure producer of values.
*/
class World
: public TaskContext
{
protected:
/**
* This port object must be connected to Hello's port.
*/
OutputPort<double> output;
/** @} */
double value;
public:
World(std::string name)
: TaskContext(name),
output("output"),
value( 0.0 )
{
this->ports()->addPort( output ).doc("World's data producing port.");
}
void updateHook() {
output.write( value );
++value;
}
};
}
ORO_CREATE_COMPONENT_LIBRARY()
ORO_LIST_COMPONENT_TYPE( Example::Hello )
ORO_LIST_COMPONENT_TYPE( Example::World )
Contents of start.ops
// Start this script by using:
// deployer-gnulinux -s start.ops -linfo
import("hello_3_dataports")
loadComponent("hello","Example::Hello")
loadComponent("world","Example::World")
var double period = 0.5
var int priority = 0
setActivity("hello", period, priority , ORO_SCHED_OTHER )
setActivity("world", period, priority , ORO_SCHED_OTHER )
// Exercise: Create a ConnPolicy variable and fill in the
// 'type', 'size' and 'lock_policy' fields to create a locked
// buffer of size 10.
connect("world.output","hello.input", ConnPolicy() )
hello.configure()
hello.start()
Tutorial 3¶
Note
This tutorial assumes that you have installed Orocos through the pre-compiled packages distributed via ROS in Ubuntu. If you don’t have it installed, try following the instructions from Installation options.
First, compile the application as shown below.
Note
ROS is not needed to run Orocos or to follow this tutorial, but it is a convenient way to quickly get started.
# You can change the next two settings in accordance to your setup export RTT_TUTORIALS_WS=${HOME}/orocos_tutorials_ws export ROS_DISTRO=kinetic # Get the repository with the exercises on place mkdir -p ${RTT_TUTORIALS_WS}/src cd ${RTT_TUTORIALS_WS}/src git clone https://github.com/orocos-toolchain/rtt_examples.git cd .. # Build the examples using ROS catkin tools source /opt/ros/${ROS_DISTRO}/setup.bash catkin build
Creating multiple components¶
In this tutorial two components are defined in the same package. The ORO_CREATE_COMPONENT
macro
we used in the previous tutorials only allows for one component to be registered in a package. You can
add more than one component in a package like this:
ORO_CREATE_COMPONENT_LIBRARY()
ORO_LIST_COMPONENT_TYPE( Example::Hello )
ORO_LIST_COMPONENT_TYPE( Example::World )
Reading and writing Ports¶
Components can use RTT::InputPort
and RTT::OutputPort
objects to send and receive data, a
template parameter speciefies the type of data that the component wants to send or receive. When input
and output ports are connected, they need to have the same type. You can add ports to a component
using the addPort
function.
It is considered good practice to check if the ports of the TaskContext are connected in
the configureHook
function:
bool configureHook()
{
// configuration only succeeds if the input port is connected
return input.connected();
}
The data from an input port can be read using the read()
function, which is typically used in
the updateHook
:
void updateHook()
{
if (input.read(read_helper) = RTT::NewData)
{
// Do something.
}
}
In the above example, the data that is read is stored in the read_helper
member variable of the
TaskContext. The read
function returns either RTT::NewData
, RTT::NoData
, or RTT::OldData
.
Writing to an output port is as simple as:
output.write(value);
Connecting ports¶
Run the application with the Orocos deployer:
deployer-gnulinux -lInfo start.ops
In the start.ops
file both the Hello
and World
components are created:
import("hello_3_dataports")
loadComponent("hello","Example::Hello")
loadComponent("world","Example::World")
var double period = 0.5
var int priority = 0
setActivity("hello", period, priority , ORO_SCHED_OTHER )
setActivity("world", period, priority , ORO_SCHED_OTHER )
Connecting the input port of hello
to the output port of world
, can be done with
the connect
method, and requires a ConnPolicy
as well (we use the default here):
connect("world.output","hello.input", ConnPolicy() )
When we now start the two components, you should see the data being printed from the
updateHook
of the hello
component:
hello.configure()
hello.start()
world.start()
Now stop the world
component, and update its period to 0.1, so it runs 5 times faster,
and restart it:
world.stop()
setActivity("hello", 0.1, 0 , ORO_SCHED_OTHER )
world.start()
You should now notice that some data is not being printed, the default ConnPolicy
does
not do any buffering. You can create a ConnPolicy
that does do buffering, and use that
to connect the two ports:
var ConnPolicy cp
cp.type = CIRCULAR_BUFFER
cp.size = 10
cp.lock_policy = LOCKED
connect("world.output","hello.input", cp )
Now you should see all data printed from the updateHook
method of the hello
component