5.3. Enabling Connext 7.5.0 or Later with any ROS RMW
It is easy to enable compatibility with standalone Connext 7.5.0 or later applications, even with non-Connext RMWs. This approach requires no changes to existing applications, and is configurable through simple XML changes.
Note
These instructions are for using standalone Connext applications with ROS 2 applications, regardless of the RMW used. This approach is ideal if you have an existing ROS 2 system that you cannot change, but you want to utilize the many features of Connext in additional applications.
Ensure these prerequisites:
ROS 2 Kilted
It is recommended to use Ubuntu 24.04 (LTS) and Debian packages for installation, since this platform is referenced throughout the examples. Substitute your platform for
x64Linux4gcc7.3.0throughout the examples if you are using another architecture.Existing ROS 2 application
The examples throughout this document are based on the Writing a simple publisher and subscriber (C++) and Creating custom msg and serv files (using the
cpp_pubsubpackage with the generated Num type) ROS 2 tutorials. If you have your own existing application, you should substitute your own types and topic names throughout the examples.Connext 7.5.0 or later
Only your standalone Connext applications must use Connext 7.5.0 or later. That is, your ROS 2 application does not need to use the Connext RMW at all or, if it does, the Connext RMW can be any version, but your standalone Connext application interoperating with your ROS 2 application must be 7.5.0 or later.
You can purchase Connext 7.5.0 (email sales@rti.com for more information) or download a free version.
5.3.1. Publisher/Subscriber Tutorial for Connext 7.5.0+ with any ROS RMW
Create a new directory (outside of your ROS 2 workspace) called
connext_ws.Navigate into the directory and copy the IDL file for your ROS 2 type into the newly created directory:
mkdir ~/connext_ws cd ~/connext_ws cp <ros2_ws>/install/tutorial_interfaces/share/tutorial_interfaces/msg/Num.idl .
Configure your Connext environment (note: it is recommended to do this in a separate terminal from the one where you have configured your ROS 2 environment):
source <rti_connext_install_directory>/resource/scripts/rtisetenv_x64Linux4gcc7.3.0.bash
Generate your Connext example application:
rtiddsgen -example x64Linux4gcc7.3.0 -language c++11 Num.idl
Edit Num_publisher.cxx and Num_subscriber.cxx to set the topic name to
topic:dds::topic::Topic< ::tutorial_interfaces::msg::Num> topic(participant, "topic");
Compile your Connext application:
make -f makefile_x64Linux4gcc7.3.0
In one terminal, run the Connext subscriber:
./objs/x64Linux4gcc7.3.0/Num_subscriber
In another terminal, run the ROS 2 publisher:
ros2 run cpp_pubsub talker
Verify the Connext subscriber does not receive data from the ROS 2 publisher:
::tutorial_interfaces::msg::Num subscriber sleeping up to 1 sec... ::tutorial_interfaces::msg::Num subscriber sleeping up to 1 sec... ::tutorial_interfaces::msg::Num subscriber sleeping up to 1 sec... ::tutorial_interfaces::msg::Num subscriber sleeping up to 1 sec... ::tutorial_interfaces::msg::Num subscriber sleeping up to 1 sec...
Terminate both applications with Ctrl-C.
In one terminal, run the Connext publisher:
./objs/x64Linux4gcc7.3.0/Num_publihser
In another terminal, run the ROS 2 subscriber:
ros2 run cpp_pubsub listener
Verify the ROS 2 subscriber does not receive data from the Connext publisher (there will be no output).
Terminate both applications with Ctrl-C.
Open
USER_QOS_PROFILES.xmland add the following to the <domain_participant_qos> tag:<property> <value> <element> <name>dds.ros.enable_interoperability</name> <value>TRUE</value> </element> </value> </property>
Relaunch the Connext subscriber application:
./objs/x64Linux4gcc7.3.0/Num_subscriber
Relaunch the ROS 2 publisher application:
ros2 run cpp_pubsub talker
Verify that the Connext subscriber application now starts receiving data from the ROS 2 publisher application:
::tutorial_interfaces::msg::Num subscriber sleeping up to 1 sec... [num: 0] ::tutorial_interfaces::msg::Num subscriber sleeping up to 1 sec... [num: 1] ::tutorial_interfaces::msg::Num subscriber sleeping up to 1 sec... [num: 2] ::tutorial_interfaces::msg::Num subscriber sleeping up to 1 sec... [num: 3]
Terminate the applications.
In one terminal, run the Connext publisher:
./objs/x64Linux4gcc7.3.0/Num_publisher
In another terminal, run the ROS 2 subscriber:
ros2 run cpp_pubsub listener
Verify the ROS 2 subscriber receives data:
[INFO] [1738350344.675343878] [minimal_subscriber]: I heard: '1' [INFO] [1738350345.672977712] [minimal_subscriber]: I heard: '2'
Terminate both applications with Ctrl-C.
5.3.2. Service/Client Tutorial for Connext 7.5.0+ with any ROS RMW
Create a new directory (outside of your ROS 2 workspace) called
connext_ws.Copy the request/reply example from the RTI workspace into your new directory:
mkdir ~/connext_ws cd ~/connext_ws cp -r <path to RTI workspace>/7.5.0/examples/connext_dds/c++11/hello_world_request_reply .
Copy the ROS 2 IDL into the source directory in the copied example:
cp <ros2_ws>//install/tutorial_interfaces/share/tutorial_interfaces/srv/AddThreeInts.idl hello_world_request_reply/src/
Configure your Connext environment (note: it is recommended to do this in a separate terminal from the one where you have configured your ROS 2 environment):
source <rti_connext_install_directory>/resource/scripts/rtisetenv_x64Linux4gcc7.3.0.bash
Navigate into
hello_world_request_reply/srcand generate new type files usingAddThreeInts.idl:cd hello_world_request_reply/src rtiddsgen -create typefiles -platform x64Linux4gcc7.3.0 -language c++11 AddThreeInts.idl
Make a copy of
PrimeNumberRequester.cxxnamedAddThreeIntsRequester.cxxand a copy ofPrimeNumberReplier.cxxnamedAddThreeIntsReplier.cxx:cp PrimeNumberRequester.cxx AddThreeIntsRequester.cxx cp PrimeNumberReplier.cxx AddThreeIntsReplier.cxx
Open
AddThreeIntsRequester.cxxand make the following changes to use the AddThreeInts type instead of the Prime type:Note
require_matching_service_on_send_requestmust be set to false in theRequesterParams, andrequester.wait_for_servicecannot be used when seeking interoperability with other DDS vendors./****************************************************************************/ /* (c) Copyright, Real-Time Innovations, All rights reserved. */ /* */ /* Permission to modify and use for internal purposes granted. */ /* This software is provided "as is", without warranty, express or implied. */ /* */ /****************************************************************************/ #include "AddThreeInts.hpp" // generated support for the request and reply types #include <rti/request/rtirequest.hpp> // full request-reply API #include <rti/util/util.hpp> // for sleep() #include <rti/config/Logger.hpp> // Logger to configure logging verbosity class AddThreeIntsRequesterExample { private: dds::domain::DomainParticipant participant; public: explicit AddThreeIntsRequesterExample(int domain_id) : participant( domain_id, dds::core::QosProvider::Default().participant_qos("RequestReplyExampleProfiles::ParticipantExampleProfile")) { } void run_example(int x, int y, int z) { const auto max_wait = dds::core::Duration::from_secs(20); // Create the Requester with the participant, a service name (which has // be the same as the one used in the Replier) and optionally a QoS // profile (defined in USER_QOS_PROFILES.xml). rti::request::RequesterParams requester_params(participant); auto qos_provider = dds::core::QosProvider::Default(); requester_params.service_name("add_three_ints"); requester_params.require_matching_service_on_send_request(false); rti::request::Requester<tutorial_interfaces::srv::AddThreeInts_Request, tutorial_interfaces::srv::AddThreeInts_Response> requester( requester_params); // Wait for a service to be available sleep(2); // Send the request auto request_id = requester.send_request(tutorial_interfaces::srv::AddThreeInts_Request(x, y, z)); // Receive replies bool in_progress = true; while (in_progress) { auto replies = requester.receive_replies(max_wait); // When receive_replies times out, // it returns an empty reply collection if (replies.length() == 0) { throw std::runtime_error("Timed out waiting for replies"); return; } // Print the prime numbers we receive for (const auto& reply : replies) { if (!reply.info().valid()) { continue; } std:: cout << "Received sum " << reply.data().sum() << std::endl; } } } }; int requester_main(int x, int y, int z, int domain_id) { std::cout << "AddThreeIntsRequester: Sending a request to calculate the " << "sum of " << x << ", " << y << ", " << z << std::endl; try { AddThreeIntsRequesterExample(domain_id).run_example(x, y, z); } catch (const std::exception& ex) { std::cout << "Exception: " << ex.what() << std::endl; return -1; } return 0; } #if !(defined(RTI_VXWORKS) && !defined(__RTP__)) && !defined(RTI_PSOS) int main(int argc, char *argv[]) { int domain_id = 0; int x, y, z; if (argc < 4) { std::cout << "AddThreeIntsRequester" << std::endl << "Sends a request to calculate the sum" << std::endl << "Parameters: <x> <y> <z> [<domain_id>=0]" << std::endl; return -1; } x = atoi(argv[1]); y = atoi(argv[2]); z = atoi(argv[3]); if (argc > 4) { domain_id = atoi(argv[4]); } return requester_main(x, y, z, domain_id); } #endif
Open
AddThreeIntsReplier.cxxand make the following changes to use the AddThreeInts type instead of the Prime type:/****************************************************************************/ /* (c) Copyright, Real-Time Innovations, All rights reserved. */ /* */ /* Permission to modify and use for internal purposes granted. */ /* This software is provided "as is", without warranty, express or implied. */ /* */ /****************************************************************************/ #include "AddThreeInts.hpp" // generated support for the request and reply types #include <rti/request/rtirequest.hpp> // full request-reply API #include <rti/config/Logger.hpp> // Logger to configure logging verbosity #include <cmath> class AddThreeIntsReplierExample { private: dds::domain::DomainParticipant participant; public: explicit AddThreeIntsReplierExample(int domain_id) : participant( domain_id, dds::core::QosProvider::Default().participant_qos("RequestReplyExampleProfiles::ParticipantExampleProfile")) { } private: void calculate_and_send_sum( rti::request::Replier<tutorial_interfaces::srv::AddThreeInts_Request, tutorial_interfaces::srv::AddThreeInts_Response> replier, const rti::sub::LoanedSample<tutorial_interfaces::srv::AddThreeInts_Request>& request) { int64_t a = static_cast<int64_t>(request.data().a()); int64_t b = static_cast<int64_t>(request.data().b()); int64_t c = static_cast<int64_t>(request.data().c()); tutorial_interfaces::srv::AddThreeInts_Response reply; reply.sum() = a + b + c;; replier.send_reply(reply, request.info()); } public: void run_example() { // Create the Replier with the participant, a service name and // optionally a QoS profile (defined in USER_QOS_PROFILES.xml). auto qos_provider = dds::core::QosProvider::Default(); rti::request::ReplierParams replier_params(participant); replier_params.service_name("add_three_ints"); rti::request::Replier<tutorial_interfaces::srv::AddThreeInts_Request, tutorial_interfaces::srv::AddThreeInts_Response> replier( replier_params); // Receive requests and process them const auto MAX_WAIT = dds::core::Duration::from_secs(20); auto requests = replier.receive_requests(MAX_WAIT); while (requests.length() > 0) { // end the requester when no requests // received in MAX_WAIT for (const auto& request : requests) { if (!request.info().valid()) { continue; } std::cout << "Calculating sum of " << request.data().a() << ", " << request.data().b() << ", " << request.data().c() << std::endl; calculate_and_send_sum(replier, request); std::cout << "Sent reply" << std::endl; } requests = replier.receive_requests(MAX_WAIT); } } }; int replier_main(int domain_id) { rti::config::Logger::instance().verbosity( rti::config::Verbosity::WARNING); std::cout << "AddThreeIntsReplier running (on domain " << domain_id << ")" << std::endl; try { AddThreeIntsReplierExample(domain_id).run_example(); } catch (const std::exception& ex) { std::cout << "Exception: " << ex.what() << std::endl; return -1; } return 0; } #if !(defined(RTI_VXWORKS) && !defined(__RTP__)) && !defined(RTI_PSOS) int main(int argc, char *argv[]) { int domain_id = 0; if (argc > 1) { domain_id = atoi(argv[1]); } return replier_main(domain_id); } #endif
Modify
USER_QOS_PROFILES.xmlto include a DomainParticipant QoS profile and remove the existing snippets that set the Durability on the reader and writer (otherwise the endpoints may be incompatible with the ROS 2 endpoints):<?xml version="1.0"?> <dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://community.rti.com/schema/7.5.0/rti_dds_qos_profiles.xsd"> <qos_library name="RequestReplyExampleProfiles"> <qos_profile name="RequesterExampleProfile" base_name="BuiltinQosLib::Pattern.RPC"> </qos_profile> <qos_profile name="ReplierExampleProfile" base_name="BuiltinQosLib::Pattern.RPC"> </qos_profile> <qos_profile name="ParticipantExampleProfile" base_name="BuiltinQosLib::Pattern.RPC"> </qos_profile> </qos_library> </dds>
Modify
make/makefile_RequestReplyPrimes_x64Linux4gcc7.3.0to compile AddThreeInts code:###################################################################### # makefile_Primes_x64Linux4gcc7.3.0# # (c) Copyright, Real-Time Innovations, 2012. All rights reserved. # RTI grants Licensee a license to use, modify, compile, and create # derivative works of the software solely for use with RTI Connext DDS. # Licensee may redistribute copies of the software provided that all such # copies are subject to this license. The software is provided "as is", # with no warranty of any type, including any warranty for fitness for # any purpose. RTI is under no obligation to maintain or support the # software. RTI shall not be liable for any incidental or consequential # damages arising out of the use or inability to use the software. # # # This makefile was automatically generated by RTI Code Generator (rtiddsgen) # version 4.5.0. # # # Note: This makefile is only meant to build our example applications and # may require alterations to build on your system. # # This makefile assumes that your build environment is already correctly # configured. (For example, the correct version of your compiler and # linker should be in your PATH.) ###################################################################### SOURCE_DIR = src/ TARGET_ARCH = x64Linux4gcc7.3.0 #TOOLSROOT = # Set the location of the toolchain #COMPILERSYSROOT = $(TOOLSROOT)/build/tmp/sysroots/genericx86-64 #COMPILER_PATH = $(TOOLSROOT)/build/tmp/sysroots/x86_64-linux/usr/bin/x86_64-wrs-linux ifndef COMPILER COMPILER = <span class="math-inline">\(COMPILER\_PATH\)</span>(GCC_PREFIX)g++ endif COMPILER_FLAGS = -m64 -Wall $(ADDITIONAL_COMPILER_FLAGS) -std=c++14 ifndef LINKER LINKER = <span class="math-inline">\(COMPILER\_PATH\)</span>(GCC_PREFIX)g++ endif LINKER_FLAGS = -m64 <span class="math-inline">\(ADDITIONAL\_LINKER\_FLAGS\) SYSLIBS \= \-ldl \-lm \-lpthread \-lrt \-no\-pie \-rdynamic DEFINES \= \-DRTI\_UNIX \-DRTI\_LINUX \-DRTI\_64BIT ifndef DEBUG DEBUG\=0 endif ifeq \(</span>(DEBUG),1) COMPILER_FLAGS += -g -O0 LINKER_FLAGS += -g DEBUG_SFX = d else DEBUG_SFX = endif ifndef SHAREDLIB SHAREDLIB=0 endif ifeq (<span class="math-inline">\(SHAREDLIB\),1\) SHAREDLIB\_SFX \= STATIC\_LIBRARIES \= else SHAREDLIB\_SFX \= z DEFINES \+\= \-DRTI\_STATIC endif INCLUDES \= \-I\. \-I</span>(NDDSHOME)/include -I$(NDDSHOME)/include/ndds -I$(NDDSHOME)/include/ndds/hpp LIBS = -L$(NDDSHOME)/lib/<span class="math-inline">\(TARGET\_ARCH\) LIBS \+\= \-lrticonnextmsgcpp2</span>(SHAREDLIB_SFX)<span class="math-inline">\(DEBUG\_SFX\) LIBS \+\= \-lnddscpp2</span>(SHAREDLIB_SFX)<span class="math-inline">\(DEBUG\_SFX\) \-lnddsc</span>(SHAREDLIB_SFX)<span class="math-inline">\(DEBUG\_SFX\) \-lnddscore</span>(SHAREDLIB_SFX)$(DEBUG_SFX) $(STATIC_LIBRARIES) $(SYSLIBS) CDRSOURCES = AddThreeInts.idl SOURCES = $(SOURCE_DIR)AddThreeIntsPlugin.cxx $(SOURCE_DIR)AddThreeInts.cxx COMMONSOURCES = $(notdir <span class="math-inline">\(SOURCES\)\) EXEC \= AddThreeIntsRequester AddThreeIntsReplier DIRECTORIES \= objs\.dir objs/</span>(TARGET_ARCH).dir COMMONOBJS = <span class="math-inline">\(COMMONSOURCES\:%\.cxx\=objs/</span>(TARGET_ARCH)/%.o) # We actually stick the objects in a sub directory to keep your directory clean. $(TARGET_ARCH) : $(DIRECTORIES) $(COMMONOBJS) <span class="math-inline">\(EXEC\:%\=objs/</span>(TARGET_ARCH)/%.o) <span class="math-inline">\(EXEC\:%\=objs/</span>(TARGET_ARCH)/%) objs/<span class="math-inline">\(TARGET\_ARCH\)/% \: objs/</span>(TARGET_ARCH)/%.o $(LINKER) $(LINKER_FLAGS) -o $@ $@.o $(COMMONOBJS) <span class="math-inline">\(LIBS\) objs/</span>(TARGET_ARCH)/%.o : $(SOURCE_DIR)%.cxx $(SOURCE_DIR)AddThreeInts.hpp $(COMPILER) $(COMPILER_FLAGS) -o $@ $(DEFINES) $(INCLUDES) -c $< # # Uncomment these lines if you want the support files regenerated when idl # file is modified # # # $(SOURCE_DIR)PrimesPlugin.cxx $(SOURCE_DIR)Primes.cxx \ # $(SOURCE_DIR)Primes.hpp $(SOURCE_DIR)PrimesPlugin.hpp $(SOURCE_DIR)application.hpp : \ # $(SOURCE_DIR)Primes.idl # $(NDDSHOME)/bin/rtiddsgen $(SOURCE_DIR)Primes.idl -replace -language C++11 # # Here is how we create those subdirectories automatically. %.dir : @echo "Checking directory $*" @if [ ! -d $* ]; then \ echo "Making directory $*"; \ mkdir -p $* ; \ fi;Compile the AddThreeInts Requester and Replier:
make -f make/makefile_RequestReplyPrimes_x64Linux4gcc7.3.0
Launch the Connext AddThreeInts Replier:
./objs/x64Linux4gcc7.3.0/AddThreeIntsReplier
Launch the ROS 2 client:
ros2 run cpp_srvcli client 1 2 3
Verify the Connext application does not receive the request and the ROS 2 client does not receive a reply.
Connext output:
AddThreeIntsReplier running (on domain 0)
ROS 2 output:
[INFO] [time] [rclcpp]: service not available, waiting again... [INFO] [time] [rclcpp]: service not available, waiting again... [INFO] [time] [rclcpp]: service not available, waiting again... [INFO] [time] [rclcpp]: service not available, waiting again...
Terminate the applications. Launch the Connext AddThreeInts Requester:
./objs/x64Linux4gcc7.3.0/AddThreeIntsRequester 1 2 3
Launch the ROS 2 server:
ros2 run cpp_srvcli server
Verify the ROS 2 server does not receive a request and the Connext application does not receive a reply.
ROS 2 output:
[INFO] [time] [rclcpp]: Ready to add three ints
Connext output:
AddThreeIntsRequester: Sending a request to calculate the sum of 1, 2, 3
Enable ROS 2 interoperability by setting
dds.ros.enable_interoperabilityinUSER_QOS_PROFILES.xml:<?xml version="1.0"?> <dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://community.rti.com/schema/7.5.0/rti_dds_qos_profiles.xsd"> <qos_library name="RequestReplyExampleProfiles"> <!-- Use these profiles to customize the Requester or Replier QoS --> <!-- These profiles inherit from the built-in profile BuiltinQosLib::Pattern.RPC, which defines the default values for the DataWriters and DataReaders created by a Requester or Replier --> <qos_profile name="RequesterExampleProfile" base_name="BuiltinQosLib::Pattern.RPC"> </qos_profile> <qos_profile name="ReplierExampleProfile" base_name="BuiltinQosLib::Pattern.RPC"> </qos_profile> <qos_profile name="ParticipantExampleProfile" base_name="BuiltinQosLib::Pattern.RPC"> <domain_participant_qos> <property> <value> <element> <name>dds.ros.enable_interoperability</name> <value>TRUE</value> </element> </value> </property> </domain_participant_qos> </qos_profile> </qos_library> </dds>
Launch the Connext AddThreeInts Replier:
./objs/x64Linux4gcc7.3.0/AddThreeIntsReplier
Launch the ROS 2 client:
ros2 run cpp_srvcli client 1 2 3
Verify the request is received and processed:
Connext output:
AddThreeIntsReplier running (on domain 0) Calculating sum of 1, 2, 3 Sent reply
ROS 2 output:
[INFO] [time] [rclcpp]: Sum: 43
Terminate the applications. Launch the Connext AddThreeInts Requester:
./objs/x64Linux4gcc7.3.0/AddThreeIntsRequester
Launch the ROS 2 server:
ros2 run cpp_srvcli server
Verify the request is received and processed:
ROS 2 output:
[INFO] [time] [rclcpp]: Ready to add three ints [INFO] [time] [rclcpp]: Incoming request a: 1 b: 2 c: 3 [INFO] [time] [rclcpp]: sending back response: [6]
Connext output:
AddThreeIntsRequester: Sending a request to calculate the sum of 1, 2, 3 Received sum 6