diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..486959c --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,28 @@ +name: ROS2 CI +on: [push, pull_request] +jobs: + build: + strategy: + fail-fast: false + matrix: + rosdistro: [dashing, eloquent, master] + os: [ubuntu-18.04, macOS-latest, windows-latest] + exclude: + # pending https://github.com/ament/ament_cmake/pull/233 + - rosdistro: eloquent + os: windows-latest + # pending https://github.com/ament/ament_cmake/pull/234 + - rosdistro: dashing + os: windows-latest + runs-on: ${{ matrix.os }} + steps: + - if: runner.os == 'Linux' + # azure ubuntu repo can be flaky so add an alternate source + run: sed -e 's/azure.archive.ubuntu.com/us.archive.ubuntu.com/g' -e t -e d /etc/apt/sources.list | sudo tee /etc/apt/sources.list.d/nonazure.list + - name: Acquire ROS dependencies + uses: ros-tooling/setup-ros@0.0.15 + - name: Build and test ROS + uses: ros-tooling/action-ros-ci@0.0.14 + with: + package-name: rmw_cyclonedds_cpp + vcs-repo-file-url: https://raw.githubusercontent.com/ros2/ros2/${{ matrix.rosdistro }}/ros2.repos diff --git a/README.md b/README.md index 7cef6aa..e586168 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,37 @@ -# A ROS2 RMW implementation for Eclipse Cyclone DDS +# ROS2 RMW for Eclipse Cyclone DDS -With the code in this repository, it is possible to use [*ROS2*](https://index.ros.org/doc/ros2) -with [*Eclipse Cyclone DDS*](https://github.com/eclipse-cyclonedds/cyclonedds) as the underlying DDS -implementation. +**Easy, fast, reliable, small [Eclipse Cyclone DDS](https://github.com/eclipse-cyclonedds/cyclonedds) middleware** for ROS2. Make your **🐢 run like a 🚀** [Eclipse Cyclone DDS has great adopters](https://iot.eclipse.org/adopters/) and contributors in the ROS community and is an [Eclipse Foundation](https://www.eclipse.org) open source project of [Eclipse IoT](https://iot.eclipse.org) and [OpenADx](https://openadx.eclipse.org) (autonomous driving). -## Getting, building and using it +This package lets [*ROS2*](https://index.ros.org/doc/ros2) use [*Eclipse Cyclone DDS*](https://github.com/eclipse-cyclonedds/cyclonedds) as the underlying DDS implementation. +Cyclone DDS is ready to use. It seeks to give the fastest, easiest, and most robust ROS2 experience. Let the Cyclone blow you away! -All it takes to get Cyclone DDS support into ROS2 is to clone this repository into the ROS2 workspace -source directory, and then run colcon build in the usual manner: +1. Install: - cd ros2_ws/src - git clone https://github.com/ros2/rmw_cyclonedds - git clone https://github.com/eclipse-cyclonedds/cyclonedds - cd .. + ``` + apt install ros-eloquent-rmw-cyclonedds-cpp + ``` + or + ``` + apt install ros-dashing-rmw-cyclonedds-cpp + ``` + +2) Set env variable and run ROS2 apps as usual: + + ```export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp``` + +3) Confirm RMW: In Eloquent, to confirm which RMW you're using: + + ```ros2 doctor --report``` + + +## Building from source and contributing + + +Note the `master` branch maintains compatibility with ROS releases Dashing and later, including the not-yet-released [*Foxy*](https://index.ros.org/doc/ros2/Releases/Release-Foxy-Fitzroy/). + +If building ROS2 from source ([ros2.repos](https://github.com/ros2/ros2/blob/master/ros2.repos)), you already have this package and Cyclone DDS: + + cd /opt/ros/master rosdep install --from src -i colcon build export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp - -This seems to work fine on Linux with a binary ROS2 installation as well as when building ROS2 from -source. On macOS it has only been tested in a source build on a machine in an "unsupported" -configuration (macOS 10.14 with SIP enabled, instead of 10.12 with SIP disabled), and apart from a -few details that are caused by the machine configuration, that works fine, too. There is no reason -why it wouldn't work the same on Windows, but I haven't tried. - -If you want to use a pre-existing installation of Cyclone DDS, you don't need to clone it, but you -may have to tell CMake where to look for it using the `CycloneDDS_DIR` variable. That also appears -to be the case if there are other packages in the ROS2 workspace that you would like to use Cyclone -DDS directly instead of via the ROS2 abstraction. - -## Known limitations - -Cyclone DDS doesn't yet fully implement the Lifespan, Deadline and some of the Liveliness QoS modes. -Consequently these features of ROS2 are also not yet supported when using Cyclone DDS. diff --git a/rmw_cyclonedds_cpp/src/TypeSupport2.hpp b/rmw_cyclonedds_cpp/src/TypeSupport2.hpp index 0beadc1..c3e065f 100644 --- a/rmw_cyclonedds_cpp/src/TypeSupport2.hpp +++ b/rmw_cyclonedds_cpp/src/TypeSupport2.hpp @@ -245,6 +245,9 @@ public: } const void * sequence_contents(const void * ptr_to_sequence) const override { + if (sequence_size(ptr_to_sequence) == 0) { + return nullptr; + } return m_get_const_function(ptr_to_sequence, 0); } }; diff --git a/rmw_cyclonedds_cpp/src/rmw_node.cpp b/rmw_cyclonedds_cpp/src/rmw_node.cpp index 8187cf2..81594c7 100644 --- a/rmw_cyclonedds_cpp/src/rmw_node.cpp +++ b/rmw_cyclonedds_cpp/src/rmw_node.cpp @@ -35,14 +35,14 @@ #include "rmw/allocators.h" #include "rmw/convert_rcutils_ret_to_rmw_ret.h" #include "rmw/error_handling.h" -#include "rmw/names_and_types.h" +#include "rmw/event.h" +#include "rmw/get_node_info_and_types.h" #include "rmw/get_service_names_and_types.h" #include "rmw/get_topic_names_and_types.h" -#include "rmw/get_node_info_and_types.h" -#include "rmw/event.h" -#include "rmw/validate_node_name.h" +#include "rmw/names_and_types.h" #include "rmw/rmw.h" #include "rmw/sanity_checks.h" +#include "rmw/validate_node_name.h" #include "Serialization.hpp" #include "rmw/impl/cpp/macros.hpp" @@ -55,6 +55,7 @@ #if RMW_VERSION_GTE(0, 8, 2) #include "rmw/get_topic_endpoint_info.h" +#include "rmw/incompatible_qos_events_statuses.h" #include "rmw/topic_endpoint_info_array.h" #endif @@ -1326,6 +1327,28 @@ static dds_qos_t * create_readwrite_qos( return qos; } +#if RMW_VERSION_GTE(0, 8, 2) +static rmw_qos_policy_kind_t dds_qos_policy_to_rmw_qos_policy(dds_qos_policy_id_t policy_id) +{ + switch (policy_id) { + case DDS_DURABILITY_QOS_POLICY_ID: + return RMW_QOS_POLICY_DURABILITY; + case DDS_DEADLINE_QOS_POLICY_ID: + return RMW_QOS_POLICY_DEADLINE; + case DDS_LIVELINESS_QOS_POLICY_ID: + return RMW_QOS_POLICY_LIVELINESS; + case DDS_RELIABILITY_QOS_POLICY_ID: + return RMW_QOS_POLICY_RELIABILITY; + case DDS_HISTORY_QOS_POLICY_ID: + return RMW_QOS_POLICY_HISTORY; + case DDS_LIFESPAN_QOS_POLICY_ID: + return RMW_QOS_POLICY_LIFESPAN; + default: + return RMW_QOS_POLICY_INVALID; + } +} +#endif + static bool dds_qos_to_rmw_qos(const dds_qos_t * dds_qos, rmw_qos_profile_t * qos_policies) { assert(dds_qos); @@ -2023,6 +2046,10 @@ static const std::unordered_map mask_map{ {RMW_EVENT_REQUESTED_DEADLINE_MISSED, DDS_REQUESTED_DEADLINE_MISSED_STATUS}, {RMW_EVENT_LIVELINESS_LOST, DDS_LIVELINESS_LOST_STATUS}, {RMW_EVENT_OFFERED_DEADLINE_MISSED, DDS_OFFERED_DEADLINE_MISSED_STATUS}, +#if RMW_VERSION_GTE(0, 8, 2) + {RMW_EVENT_REQUESTED_QOS_INCOMPATIBLE, DDS_REQUESTED_INCOMPATIBLE_QOS_STATUS}, + {RMW_EVENT_OFFERED_QOS_INCOMPATIBLE, DDS_OFFERED_INCOMPATIBLE_QOS_STATUS}, +#endif }; static bool is_event_supported(const rmw_event_type_t event_t) @@ -2116,6 +2143,25 @@ extern "C" rmw_ret_t rmw_take_event( } } +#if RMW_VERSION_GTE(0, 8, 2) + case RMW_EVENT_REQUESTED_QOS_INCOMPATIBLE: { + auto ei = static_cast(event_info); + auto sub = static_cast(event_handle->data); + dds_requested_incompatible_qos_status_t st; + if (dds_get_requested_incompatible_qos_status(sub->enth, &st) < 0) { + *taken = false; + return RMW_RET_ERROR; + } else { + ei->total_count = static_cast(st.total_count); + ei->total_count_change = st.total_count_change; + ei->last_policy_kind = dds_qos_policy_to_rmw_qos_policy( + static_cast(st.last_policy_id)); + *taken = true; + return RMW_RET_OK; + } + } +#endif + case RMW_EVENT_LIVELINESS_LOST: { auto ei = static_cast(event_info); auto pub = static_cast(event_handle->data); @@ -2146,6 +2192,25 @@ extern "C" rmw_ret_t rmw_take_event( } } +#if RMW_VERSION_GTE(0, 8, 2) + case RMW_EVENT_OFFERED_QOS_INCOMPATIBLE: { + auto ei = static_cast(event_info); + auto pub = static_cast(event_handle->data); + dds_offered_incompatible_qos_status_t st; + if (dds_get_offered_incompatible_qos_status(pub->enth, &st) < 0) { + *taken = false; + return RMW_RET_ERROR; + } else { + ei->total_count = static_cast(st.total_count); + ei->total_count_change = st.total_count_change; + ei->last_policy_kind = dds_qos_policy_to_rmw_qos_policy( + static_cast(st.last_policy_id)); + *taken = true; + return RMW_RET_OK; + } + } +#endif + case RMW_EVENT_INVALID: { break; }