5.3. Enabling Connext 7.7.0+ with Connext RMW
The Connext RMW, starting with the Lyrical release, has been optimized to allow for seamless integration with standalone Connext applications, with no additional configuration required. This is an ideal solution if you can update all applications in your ecosystem to Connext release 7.7.0+.
This chapter introduces two tutorials designed to demonstrate how standalone Connext applications work with ROS 2 applications using Connext 7.7.0+ with Connext RMW.
5.3.1. Before you begin
Before walking through the Connext RMW tutorials, ensure your system meets the following prerequisites:
The ROS 2 lyrical release is installed and the environment configured. See ROS 2 installation.
The following tutorials reference Ubuntu 26.04 (LTS); we recommend using this Linux distribution and installing via Debian packages. If you are using a different architecture, substitute your platform for
x64Linux4gcc8.5.0in the following examples.An existing ROS 2 application that you can modify is available.
The following exercises are based on the Writing a simple publisher and subscriber (C++) and Creating custom msg and serv files ROS 2 tutorials. You should complete both because the types and applications generated from these ROS 2 guides are used in the following Connect RMW examples. If you have your own existing application, substitute your types and topic names instead.
Connext 7.7.0+ and the Connext RMW are installed.
The Connext installation provides the libraries needed by the Connext RMW. See Installing the Connext RMW for details.
5.3.2. Publisher/subscriber tutorial for Connext 7.7.0+ with Connext RMW
This tutorial demonstrates Connext application interoperability with ROS 2 using simple publisher and subscriber applications. This exercise is based on the Writing a simple publisher and subscriber (C++) ROS 2 tutorial, modified to use the Connext RMW.
Outside of your ROS 2 workspace, create a new directory named
connext_ws.Copy the IDL file for your ROS 2
msgtype into the new directory:cp <ros2_ws>/install/tutorial_interfaces/share/tutorial_interfaces/msg/Num.idl .
Configure your Connext environment (do not use the terminal where you configured your ROS 2 environment):
source <rti_connext_install_directory>/resource/scripts/rtisetenv_x64Linux4gcc8.5.0.bash
Generate your Connext example application:
rtiddsgen -example x64Linux4gcc8.5.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_x64Linux4gcc8.5.0
In one terminal, run the Connext subscriber:
./objs/x64Linux4gcc8.5.0/Num_subscriber
In another terminal, run the ROS 2 publisher with the Connext RMW:
RMW_IMPLEMENTATION=rmw_connextdds ros2 run cpp_pubsub talker
Verify that the Connext subscriber application begins 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]
5.3.3. Service/client tutorial for Connext 7.7.0+ with Connext RMW
This tutorial demonstrates Connext 7.7.0+ application interoperability with ROS 2 using a simple service and client application. This exercise is based on the Creating custom msg and serv files ROS 2 tutorial, but modified to use the Connext RMW.
Outside of your ROS 2 workspace, create a new directory named
connext_ws.Copy the request/reply example from the RTI workspace into your new directory:
cp -r <path to RTI workspace>/7.7.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 (do not use the terminal where you configured your ROS 2 environment):
source <rti_connext_install_directory>/resource/scripts/rtisetenv_x64Linux4gcc8.5.0.bash
Navigate to
hello_world_request_reply/srcand generate new type files usingAddThreeInts.idl:cd hello_world_request_reply/src rtiddsgen -create typefiles -platform x64Linux4gcc8.5.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 theAddThreeIntstype instead of theprimestype:Note
require_matching_service_on_send_requestmust be set to false in theRequesterParams, andrequester.wait_for_servicecannot be used when seeking interoperability with ROS 2 applications as they may not include support forrelated_datareader_keyandrelated_datawriter_key./****************************************************************************/ /* (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 theAddThreeIntstype instead of theprimestype:/****************************************************************************/ /* (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. Also 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.7.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> </domain_participant_qos> </qos_profile> </qos_library> </dds>
Modify
make/makefile_RequestReplyPrimes_x64Linux4gcc8.5.0to compileAddThreeIntscode:###################################################################### # makefile_Primes_x64Linux4gcc8.5.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.4.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 = x64Linux4gcc8.5.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 = $(COMPILER_PATH)$(GCC_PREFIX)g++ endif COMPILER_FLAGS = -m64 -Wall $(ADDITIONAL_COMPILER_FLAGS) -std=c++14 ifndef LINKER LINKER = $(COMPILER_PATH)$(GCC_PREFIX)g++ endif LINKER_FLAGS = -m64 $(ADDITIONAL_LINKER_FLAGS) SYSLIBS = -ldl -lm -lpthread -lrt -no-pie -rdynamic DEFINES = -DRTI_UNIX -DRTI_LINUX -DRTI_64BIT ifndef DEBUG DEBUG=0 endif ifeq ($(DEBUG),1) COMPILER_FLAGS += -g -O0 LINKER_FLAGS += -g DEBUG_SFX = d else DEBUG_SFX = endif ifndef SHAREDLIB SHAREDLIB=0 endif ifeq ($(SHAREDLIB),1) SHAREDLIB_SFX = STATIC_LIBRARIES = else SHAREDLIB_SFX = z DEFINES += -DRTI_STATIC endif INCLUDES = -I. -I$(NDDSHOME)/include -I$(NDDSHOME)/include/ndds -I$(NDDSHOME)/include/ndds/hpp LIBS = -L$(NDDSHOME)/lib/x86_64Linux \ LIBS += -lrticonnextmsgcpp2$(SHAREDLIB_SFX)$(DEBUG_SFX) LIBS += -lnddscpp2$(SHAREDLIB_SFX)$(DEBUG_SFX) -lnddsc$(SHAREDLIB_SFX)$(DEBUG_SFX) -lnddscore$(SHAREDLIB_SFX)$(DEBUG_SFX) \ \ $(STATIC_LIBRARIES) $(SYSLIBS) CDRSOURCES = AddThreeInts.idl SOURCES = $(SOURCE_DIR)AddThreeIntsPlugin.cxx $(SOURCE_DIR)AddThreeInts.cxx COMMONSOURCES = $(notdir $(SOURCES)) EXEC = AddThreeIntsReplier AddThreeIntsRequester DIRECTORIES = objs.dir objs/$(TARGET_ARCH).dir COMMONOBJS = $(COMMONSOURCES:%.cxx=objs/$(TARGET_ARCH)/%.o) # We actually stick the objects in a sub directory to keep your directory clean. $(TARGET_ARCH) : $(DIRECTORIES) $(COMMONOBJS) \ $(EXEC:%=objs/$(TARGET_ARCH)/%.o) \ $(EXEC:%=objs/$(TARGET_ARCH)/%) objs/$(TARGET_ARCH)/% : objs/$(TARGET_ARCH)/%.o $(LINKER) $(LINKER_FLAGS) -o $@ $@.o $(COMMONOBJS) $(LIBS) objs/$(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
AddThreeIntsRequester and Replier:make -f make/makefile_x64Linux4gcc8.5.0
Launch the Connext
AddThreeIntsReplier:./objs/x64Linux4gcc8.5.0/AddThreeIntsReplier
Launch the ROS 2 client with the Connext RMW:
RMW_IMPLEMENTATION=rmw_connextdds ros2 run cpp_srvcli client 41 1 1
Verify the request is received and processed.
Connext output:
AddThreeIntsReplier running (on domain 0) Calculating sum of 41, 1, 1 Sent reply
ROS 2 output:
[INFO] [time] [rclcpp]: Sum: 43
Terminate the applications, then launch the Connext
AddThreeIntsRequester:./objs/x64Linux4gcc8.5.0/AddThreeIntsRequester 41 1 1
Launch the ROS 2 server with the Connext RMW:
RMW_IMPLEMENTATION=rmw_connextdds ros2 run cpp_srvcli server
Verify the request is received and processed.
Connext output:
AddThreeIntsRequester: Sending a request to calculate the sum of 1, 2, 3 Received sum 6
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]