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.0 in 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.

  1. Outside of your ROS 2 workspace, create a new directory named connext_ws.

  2. Copy the IDL file for your ROS 2 msg type into the new directory:

    cp <ros2_ws>/install/tutorial_interfaces/share/tutorial_interfaces/msg/Num.idl .
    
  3. 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
    
  4. Generate your Connext example application:

    rtiddsgen -example x64Linux4gcc8.5.0 -language c++11 Num.idl
    
  5. 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");
    
  6. Compile your Connext application:

    make -f makefile_x64Linux4gcc8.5.0
    
  7. In one terminal, run the Connext subscriber:

    ./objs/x64Linux4gcc8.5.0/Num_subscriber
    
  8. In another terminal, run the ROS 2 publisher with the Connext RMW:

    RMW_IMPLEMENTATION=rmw_connextdds ros2 run cpp_pubsub talker
    
  9. 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.

  1. Outside of your ROS 2 workspace, create a new directory named connext_ws.

  2. 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 .
    
  3. 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/
    
  4. 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
    
  5. Navigate to hello_world_request_reply/src and generate new type files using AddThreeInts.idl:

    cd hello_world_request_reply/src
    rtiddsgen -create typefiles -platform x64Linux4gcc8.5.0 -language c++11 AddThreeInts.idl
    
  6. Make a copy of PrimeNumberRequester.cxx named AddThreeIntsRequester.cxx and a copy of PrimeNumberReplier.cxx named AddThreeIntsReplier.cxx:

    cp PrimeNumberRequester.cxx AddThreeIntsRequester.cxx
    cp PrimeNumberReplier.cxx AddThreeIntsReplier.cxx
    
  7. Open AddThreeIntsRequester.cxx and make the following changes to use the AddThreeInts type instead of the primes type:

    Note

    require_matching_service_on_send_request must be set to false in the RequesterParams, and requester.wait_for_service cannot be used when seeking interoperability with ROS 2 applications as they may not include support for related_datareader_key and related_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
    
  8. Open AddThreeIntsReplier.cxx and make the following changes to use the AddThreeInts type instead of the primes 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
    
  9. Modify USER_QOS_PROFILES.xml to 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>
    
  10. Modify make/makefile_RequestReplyPrimes_x64Linux4gcc8.5.0 to compile AddThreeInts code:

    ######################################################################
    # 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;
    
  11. Compile the AddThreeInts Requester and Replier:

    make -f make/makefile_x64Linux4gcc8.5.0
    
  12. Launch the Connext AddThreeInts Replier:

    ./objs/x64Linux4gcc8.5.0/AddThreeIntsReplier
    
  13. Launch the ROS 2 client with the Connext RMW:

    RMW_IMPLEMENTATION=rmw_connextdds ros2 run cpp_srvcli client 41 1 1
    
  14. 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
      
  15. Terminate the applications, then launch the Connext AddThreeInts Requester:

    ./objs/x64Linux4gcc8.5.0/AddThreeIntsRequester 41 1 1
    
  16. Launch the ROS 2 server with the Connext RMW:

    RMW_IMPLEMENTATION=rmw_connextdds ros2 run cpp_srvcli server
    
  17. 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]