Software Exploration - DDS and the Trident #4 - A C++ node


#1

Greetings enthusiasts!

I hope you are all well.

I’ve promised two new subtopics: DDS in a stand-alone c++ executable, and ROS 2 connectivity. This thread we will discuss the DDS stand-alone: OpenDDS Node

For those of you following these software dives, as mentioned, the repo above is a work in progress. Enough said.

What is this thing? you ask. Well, the application code is a skeleton, or template of how to interact with the DDS publish/subscribe architecture. The application can do whatever you would like it to do, within computational feasibility. You can read topics, write topics, and process data within your network using the OCI implementation of the DDS standards. You can also hook up to other DDS providers, like RTI or PrismTech. In the node code on the repo, we are using OCI OpenDDS, which does not require a license to use and develop, to subscribe and publish to the RTI Connext DDS impl that supplies the pub/sub layer for the Trident.

Let’s first look at the directory structure of the Node:

OpenDDS_SkeletonNode
|–OpenDDS (submodule of project)
| |–IDL
| |–Scripts
| |–Source
| |–Cpp
| |–Generated
| |–IDL
|–Source
| |–Config
| |–Scripts
| |–Utility
|–ThirdPartyLibs

OpenDDS folder contains the IDL that we generate to communicate with the trident. For the example in the repo, I’ve reproduced the rov_depth topic IDL files, from the Openrov-dds folder in the GitLab repo from OpenROV. openrov-dds We also have the Scripts folder and Source folder. You only need to concern yourself with the IDL folder at the top layer and the Scripts folder. The other Source and its subsequent subfolders contain generated code that is populated by the build-idl-cpp.sh script found in Scripts.

The build script takes the initial IDL files in the high level IDL folder and generates all the needed code that DDS uses to marshal message types across the network. serialization/de-serialization, etc. As you will see in the main source files for the application, you need only call types from the TypeSupport files for the DDS message types. Fortunately, I’ve highlighted the areas in the code you need to modify for your own purposes…mostly.

We’ve added the IDLs needed to read depth data. All that is required is to the the build script.

  • ./build-idl-cpp.sh

All the needed code will be populated under the Generated and IDL folders in Source of the OpenDDS high level directory. Example output below. Do not mind the error/warnings. That is saying that we’ve match names of data values and sturcture types in the IDL, with only case differeing the two. This is from a newer impl of OpenDDS. Within the script, I’ve added the build flag -Cw with changes the error to a warning and continues compiling the needed support code for the IDL files.

Moving on.

With the DDS generated code for our data, let’s get out of the OpenDDS folder and move to the high level ThirdPartyLibs folder. There’s nothing here. This folder is a place holder for other things you may want to add to the node like another IMU, or image processing libraries, or whatever. You can take a look at the imu branches of the repo as examples. Beware however, those branches are very outdated.

Within the Source folder, we see a Config, Scripts, and Utility folders as well as the main, CApp, and CAppNodeImpl files. The Config folder holds a post build script that is run by cmake that copies the Config folder into the build folder of the project. The importance of the Config folder is that it contains logging configuration and, more importantly, RTPS configuration for the node. The utility folder holds the easy logging header, general methods that may or may not be helpful, and the cntrl-c sigint handling code. You shouldn’t have to bother with any of this.

Let’s now talk about the meat of the application code that lives in Source:

CAppNode.h/.cpp
CAppnodeimpl.h/.cpp
main.cpp

These five files constitute all application classes and logic to, in this example, read ROV depth information in real time as it’s being published from the Trident. Exciting.

CAppNode is the parent class of CAppNodeImpl. It includes all of the DDS related files needed for this example.

This class also des something very useful in the OpenDDS world. It inherits from DataReaderListener which is a abstract class that requires certain methods to be implemented in an application that would like to subscribe to topics. This methods:

are important…and it is up to the developer to implement them. In this example, and in most cases, the on_data_available method is where you will put the code to read and display/manipulate data received over the network. And we have.

You should not need to modify the CAppNode class and impl at all…for most things…mostly. The .cpp of this class, the impl file for those of you not so c++ knowledgeable, is where the virtual methods from DataListener are stubbed out. This allows us to override what functions we wish/need for our application.

This leads us to our actual application code that lives in CAppNodeImp and main.cpp

Let’s talk about main.cpp. This is out launcher, our application driver so to type. Things we need to beware of, DOMAIN_ID 0. This is needed since the Trident operated in Domain 0 in DDS world. There is also a missing parameter that needs to come in from the command line, however, I have not implemented it yet. That is the Partition ID. This is needed by the readers and writers for the Trident. Each trident has it’s own partition. You need to know what this is. I use RTI’s Admin console to explore the DDS topics and get the partition id. In a near future update, I will have this as a command line parameter.

main.cpp also uses unique pointers…enough confusing c++ stuff. Main set’s up the application, reads in command line information and starts the app.

Finally, getting to CAppNodeImpl.


OpenROV Trindent really open?
Anyway to get dive profile data?
#2

Note: I will use Doxygen soon…but until then…

The CAppNodeImpl inherits from CAppNode. We first initialize the domain participant, the publisher and subscribers for this node, the topic, which in this case is “rov_depth”, the data writers, is any, which we don’t use in this example, and the data readers, which we do use. More specifically, we override on_data_available.

Now this isn’t nearly as developed as it should be. But it serves as a good example of how to read data from the Trident using OpenDDS and not RTI Connext.

In the next post, I will give you a step by step build process for the node and show you example output. In the meantime, I will be improving the code and designing DDS application for the Trident so the community may capture data, video, and even control the Trident via these DDS application nodes. We will also look at ROS 2 in the coming months.

More to come,

Jim


#3

@Jim_N I tried the OCI OpenDDS-3.12 configure without the --java option and I got some errors about .Skipping *.mpc); it requires xerces, xsc or tao_idl_fe_gen.’

But I went ahead with make and got these errors :

  1. OpenDDS-3.12/ACE_wrappers/ace/Bound_Ptr.h:121:34: warning: ‘template class std::auto_ptr’ is deprecated [-Wdeprecated-declarations] explicit ACE_Strong_Bound_Ptr (auto_ptr p);
  2. ExcelRTD will not be built due to the following disabled make macros: qt4 microsoft
  3. Monitor_App will not be built due to the following disabled make macros: qt4

But your build-idl-cpp.sh script seemed to run OK!

When I ran configure with --java, the make gave 5 errors about package org.omg.CORBA not in the module graph. I wonder if the errors are related to the versions of SW I’m running.
Ubuntu v18.04 Thought OCI tested with Ubuntu 16.04
Perl v5.26.1 Prerequisites >= 5.8
Java v10.0.2 Prerequisites Java SE 5 thru 8
Used apt-get install default-jdk because I wasn’t able to install openjdk-7 - “Unable to locate package”

Also in configure, it didn’t see the installed Wireshark.
Skipping dissector (dissector.mpc); it requires wireshark_any.

You mentioned gitlab openrov-dds but I haven’t seen anything in there for a couple months… But I noticed the IDLs on the Trident RPi3 in /opt/openrov/dds/types/v1. Also in "/opt/openrov/dds/qos/v1/ there’s an openrov.xml QoS file. Could that somehow be used when the RTI Admin Console isn’t available to get the QoS?

Thanks loads for your effort! I would have never been able to get here on my own.

Bill


#4

Hi Bill,

Very happy you have taken the dive into this. A couple of comments.

These aren’t errors. That are messages from the config about what and what not was found and what will or will not be included in the build. I do not know what it all means at this point, as I am really not all that familiar, yet, with the levels of functionality OCI has in OpenDDS, However, there are some things that are somewhat concerning, and lead me to believe that not all of the examples and tools are well supported. For instance, the monitor tool used to work, in version 3.9. I have not been able to get it compiled for 3.12. I have qt4 and still can’t get the build script to work. Albeit, I haven’t spent much time on it, but it should be a non-issue to compile. The monitor is akin to RTI admin console.

Things like wireshark are valuable, so install wire shark and re-run the config. You should then be able to compile and have that tool available. Sadly, i suspect a number of the tools have not been well maintained. This is the bane of open source.

In regards to the gitlab, what’s in the trident is what matters, so glad you have found them on the pi. The .xml should give you all the info you need in regards to QoS. OpenDDS also has xml usage, but I haven’t dug into it yet. I am hopeful it will be similar to RTI, because is makes things alot easier. I would love to introduce it to the node code.

The big thing about OpenDDS is that they use MPC to build everything. I am not very knowledgeable on it yet, it looks like it makes project builds more to the point for the developer, however, I have yet to dig into any tutorial about it and OCI doesn’t make it very clear. One of my biggest gripes about tools like DDS is that they tend to be expert friendly and the cost of entry can be high. In addition, OCI has a model SDK, which looks promising, however, it too is not supported well and fails to install with the latest eclipse version.

That being typed, if the standard build succeeded, you should be good to go with the Node code. Not sure if you’ve done any qt development, but it’s not that difficult to write up a qt monitor for DDS with the skeleton node and would be super valuable to the community. I may go down that route soon as I have little confidence in getting the dated Monitor gui to work. Another important tool is a data recorder…

To conclude, wireshark and the monitor are two of the most valuable tools for DDS developers and should be looked into more deeply.

Let’s keep the conversation going. By all means fork the repo and create branches for different nodes that you’d like to see.

Keep it up!

I’ll start looking into ROS 2 as well.

Jim