Greetings enthusiasts! I hope you all are well. In our last post, Software Exploration - DDS and the Trident #4 - A C++Node, I mentioned that we’d next discuss RO2 connectivity. However, I would like to take a slight turn from said course and briefly discuss another DDS variant that has proven to be very interesting: FastRTPS (by ePromisa). In this post, we will look at FastRTPS and specifically at fastrtpsgen, a code generation tool offered that builds the .idl support files and …example code that allows you to test your message types…fantastically useful.
One word…Solid. Adding the phrases up-to-date and actively supported also gives you some more reasons why I bring this pub/sub implementation of the DDS standard to your attention. It is also worth mentioning that ROS2 has FastRTPS as one of the 3 main DDS implementations, along with Connext and OpenSplice, available to the robotics researcher out-of-the-box.
Moving on then. Developed and hosted by ePromisa, a Spanish software company outside of Madrid, Spain, FastRTPS is a C++ implementation of the Real Time Publish Subscride protocol, as definedby the Object management Group consortium. It, like Connext, OpenDDS, and OpenSplice, is the implementation of the DDS standard allowing for: (from the github page)
- Configurable best-effort and reliable publish-subscribe communication policies for real-time applications.
- Plug and play connectivity so that any new applications are automatically discovered by any other members of the network.
- Modularity and scalability to allow continuous growth with complex and simple devices in the network.
- Configurable network behavior and interchangeable transport layer: Choose the best protocol and system input/output channel combination for each deployment.
- Two API Layers: a high-level Publisher-Subscriber one focused on usability and a lower-level Writer-Reader one that provides finer access to the inner workings of the RTPS protocol.
FastRTPS is being touted as …fast, faster than ZeroMQ used in the Pixhawk flight controllers. Suitable for real-time pub sub needs, FastRTPS is multi-platform, supporting Windows, Linux, Mac OS, iOS, Android, and Raspian builds, it’s FREE, Apache License 2.0, offers commercial support and fully compliant with best effort and reliable communications in both uni- an multicast implementations. For your YouTube fix: Interoperability.
FastRTPS also has a great documentation website. I encourage you to explore it thoroughly.
Let us now go to the github page and I’ll mention a few items. First, let’s take a look at the build. Now, ePromisa allows you the option of downloading the binaries from there company site or cloning the repo and build the latest source. I choose to build from source often, and this is what I will use for this post. Cloning the repo is as simple as the instructions they give. Once done, perform the build instructions as given, however, there are cmake flags you need to be aware of before building. Note, I am running Ubuntu 16.04 Linux 4.15.0-43-generic. Taking a look at the cmake command:
cmake -DTHIRDPARTY=ON -DBUILD_JAVA=ON -DCOMPILE_EXAMPLES=ON -DPERFORMANCE_TESTS=ON …
However, there are some issues here. The THIRDPARTY flag and JAVA=ON flags are good. The compile EXAMPLES and PERFORMANCE_TESTS=ON build fails. There is an issue with libgstreamer and the current linux build for the repo. This is remedied by ensuring that libgstreamer1.0-dev and libgstreamermm-1.0-dev are installed. After a successful make, do:
sudo make install
And we are set. If everything has gone well, you should be able to execute:
Some things should be jumping out here. Exciting things. Things like -example flag, and the support platform tags. So what does fastrtpsgen do for us? This Java application generates source code using the data types defined in our message .idls. This is needed for our data reader and writer methods in our target application. The tool parses the .idl files and generates code using FastCDR library (Common Data Representation). This abstracts away the serialization/deserialization underbelly and provides the initial implementation of a pub and sub using the RTPS library. Note: if you have build failures tryingto get fastrtpsgen compiled, check to ensure you have Java JDK and Gradle installed. If not, get them.
I want to point out the power of this tool. You get your pubsub support code, which is fantastic, but you can also have example code and a build structure for your target platform, that demonstrates the data reading and writiing and either creates a makefile or CMake file for your easy node compilation task. This makes writing and testing message types and experimental code so much easier than writing everything from scratch. Thie tool take an expert only friendly DDS workflow and bring it down to a more usable level for those of use who are not C++ and pubsub framework experts. (which is arguably almost everyone)
So what? Well, we can take the Trident DDS message definitions and very quickly generate readers and writers…that’s right. I can’t take the credit here, however, @jim_trezzo, @gilbert, and @spiderkeys first investigated FastRTPS and a generated ROVBeacon application some time ago. I am simply reporting on their work and my current work in getting this out to you all.
It can take some practice getting the directory structure to work with fastrtpsgen. The current openrovdds structure doesn’t lend itself well to the tool. The reason for this is that for reasons yet discovered, the tool can’t parse the layered .idl files. It fails even if you explicitly give the locations of the intended .idls as well. I will keep working on this until I either get it on the command line or create a script to do everything nicely for you. Anyway, let’s try a rov_depth example for this discussion and place all needed .idl files in a single directory for this demonstration. I’ve created an example repo on github. This simple repository has the three .idl files needed for this demonstration. I’ve also modified the .idls to match the syntax the ePromisa uses that differs from the RTI Connext implementation. This differences lies in the @key tag that identifies the topic and message as a keyed type. Long story short, your types need to match on both the publisher and subscriber nodes. Depth.idl has two keyed types, Depth and DepthConfig. This is coming out of the Trident. For FastRTPS, we need to move the //@key in the Depth.idl as follows:
string id; //@key
float depth; // Unit: meters
@Key string id; //@key
float depth; // Unit: meters
I keep the RTI //@key since it does not negatively affect the fastrtpsgen operation, and I want to see, in the near future, if the rtiddsgen tool can handle it as well. (mor on that later.) This would allow for a single .idl repo structure for the Trident that operates with FastRTPS and RTI without additional modification…one can hope.
We also need to remove the directory structures in the #includes in Depth.idl, FluidPressure_.idl and Header_.idl. Luckily, I’ve done all that for you.
Ok, here’s the process from clone to build:
$ cd DepthSensor_fastrtpsgen_example/
$fastrtpsgen -example Cmake idl/.
You should see:
You can also pick your architecture. Say -example x64Linux2.6gcc. This would give you a makefile_… that you would need to rename as Makefile, and then to build, type make. However, I like CMake, so what’s produced after the above fastrtpsgen command:
Excellent. We are getting there. Notice that a good deal of files were created. The primary .idl cxx geneated code that’s important to your individual application are the pubsubtypes and (name of idl file).h and .cxx. The (name of .idl file)_Publisher and _Subscriber.h and .cxx are only used for the examples that are created (name of .idl file)_PubSubMain.cxx. These are the functioning pub/sub example applications created. Since we’ve used CMake, we now want to build the test applications for the .idl files.
Ok, however, before we can build these applications, we need to make some modifications to the example code that was generated such that FastRTPS can communicate with RTI DDS. Note, we need to due this modification because the example code generated is really only for your message testing and examples of the readers and writers. Sadley, the examples do not use .xml for the participant profiles, but FastRTPS does have this implementation very similar, if not the same as RTI, which is awesome, and we will explore in the near future.
We need to now make a few adjustments. Due to time and patients, this changes would need to happen to each of the _Publisher.cxx and _Subscriber.cxx for each ofthe message types you’d want to test, however, we are just going to look at reading Depth data and not writing Depth or reading/writing the other types. But you can…
All modifications will be done in the DepthSubscriber.h and .cxx files.
Looking first at the DepthSubscriber.h:
At the bottom of the DepthSubscriber class, we see orov::DepthConfigPubSubType myType; We need to modify this as orov::DepthConfigPubSubType myDepthConfigType; and also add orov::DepthPubSubType myDepthType; This is due to the .idl parsing selecting the last @Key in the .idl as the key for the subscriber type. We have two @Key tags in the Depth.idl and need to add the second type so that we can read the Depth from the Trident as desired. Your class should look like:
Next we need to modify the DepthSubscriber.cxx. Our first set of modifications deal with FastRTPS memory policy limiting to 5000 bytes default allocation. Since we will need more than this, we add:
PParam.rtps.builtin.readerHistoryMemoryPolicy = PREALLOCATED_WITH_REALLOC_MEMORY_MODE;
PParam.rtps.builtin.writerHistoryMemoryPolicy = PREALLOCATED_WITH_REALLOC_MEMORY_MODE;
in the bool DepthSubscriber::init() method. We also need to modify the:
Rparam.topic.topicDataType = myType.getName();
Rparam.topic.topicDataType = myDepthType.getName();
Rparam.topic.topicName = “DepthPubSubTopic”;
Rparam.topic.topicName = “rov_depth”;
Last thing, we need to add the partition id, since the Trident partitions the topics via the first seven characters of the units UUID. For example:
Rparam.qos.m_partition.push_back( “4ec14b6” );
Save the file. It should look like:
Next, we need to add a modification to the void DepthSensorSubscriber::SubListener::onNewDataMessage(Subscriber* sub) method at the bottom of the .cxx file. Here, we need to add:
Also, so we can see the value, we need to add:
std::cout << "Depth: " << depth.depth() <<std::endl;
under the “Sample received” cout statement.
Save the file.
Ok, let’s build it.
$ mkdir build
$ cd build/
$ cmake …
In the build/ folder we now have Depth, PluidPressure_ Header_ and Time_ executable along with the compiled objects of each of the examples. Since we only modified Depth, and only care to see the subscriber data. We will only execute ./Depth. However, these examples have options. You can pass either publisher or subscriber to the application command line and invoke either the pub or sub. Let’s look at the subscriber for now.
$ ./Depth subscriber
After a moment we should see:
Waiting for Data, press Enter to stop the Subscriber.
Sample received, count=1
Sample received, count=2
Sample received, count=3
Fantastic. You can also use RTI Admin Console as described in earlier Software Exploration posts to investigate the readers and writers.
That’s all for now. Next post we’ll look at a node template for FastRTPS and … ROS2.
Thanks and remember, keep learning, exploring, changing.