YARP built-in types can be sent through Ports.
Sometimes you need to send custom datatypes, in YARP objects that can be sent through ports are called Portable
objects. Port Power, Going Further with Ports shows how you can manually implement a portable object. This tutorial shows how you can automate this process using the Thrift compiler.
Suppose we have two modules that wish to share a SharedData
struct that contains a string and a vector. Normally you would define the data in a C++ header file and write serialization/deserialization methods to allow it to be sent and received to/from a port (this process is called marshalling/demarshalling). This process requires some YARP specific expertise, is error-prone and tedious. The idea here is that instead of manually writing the class you define the structure using an intermediate language (the Thrift IDL language), then you ask a compiler (the yarpidl_thrift compiler) to generate the class for you, including all the required code.
Let's see how to do it.
We start by defining our ShareData
structure. Open a text editor and type the following:
name this file SharedData.thrift
save and close it.
In thrift's syntax this specifies that SharedData
is a struct that contains a text field of type string and a content field which type is a vector of floats. Thrift in fact supports much more options, see Thrift IDL in YARP: advanced tutorial.
Now the yarpidl_thrift
compiler can be invoked to generate both SharedData.h
and SharedData.cpp
.
Type:
This will generate two files: SharedData.h
and SharedData.cpp
.
In case you are interested you can inspect them to see that they define a C++ class with some extra code. The good thing is that you don't actually need to bother about the details, but you can readily use the class in your code.
Now we can use these files in a YARP program.
This code is straightforward. We define a simple sender executable that open a port and periodically write a SharedData
object.
As usual we start with the CMake code, write your CMakeLists.txt
:
Now we write the code for the sender in sender.cpp
:
Now compile the code
Now you can run yarp read on a separate console to inspect the content that is being transmitted on the port, the output should be something like this:
yarp: Port /tmp/port/1 active at tcp://127.0.0.1:10003 yarp: Receiving input from /sender to /tmp/port/1 using tcp "Hello from sender" (0.0 0.0) "Hello from sender" (0.0 0.0)
It is simple to write a receiver:
Append the following to the CMakeLists.txt
This is the receiver code in receiver.cpp
:
YARP provides CMake supports to automate the invocation of yarpidl_thrift
. This is convenient in large projects when we generate several files and we do not want to keep track of all of them individually.
We can use yarp_idl_to_dir
to tell CMake to parse SharedData.thrift
with the thrift compiler. All generated files will be placed in separate include
and src
directories.
Now you just have to execute CMake. The variable ALLOW_IDL_GENERATION
controls if the thrift compiler is executed to generate SharedData.h/cpp
or not. It will be off by default if there is already generated code in the desired directory, you'll need to turn it on to overwrite that.
The code generation step is required only when SharedData.thrift
is modified.
The Thrift IDL also allows defining modules interfaces, this is explained here:
YARP supports ROS types. An alternative method to define portable objects in YARP is to use the ROS syntax (i.e. through a .msg file). The disadvantage of this approach is that you cannot use native YARP types, but the advantage is that your messages will be compatible with ROS and readable from a topic. This tutorial shows how you do this:
Code for this tutorial can be found here: example/idl/portable