Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions rclcpp/include/rclcpp/exceptions/exceptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,13 @@ class InvalidQosOverridesException : public std::runtime_error
using std::runtime_error::runtime_error;
};

/// Thrown if a QoS compatibility check fails.
class QoSCheckCompatibleException : public std::runtime_error
{
// Inherit constructors from runtime_error.
using std::runtime_error::runtime_error;
};

} // namespace exceptions
} // namespace rclcpp

Expand Down
63 changes: 63 additions & 0 deletions rclcpp/include/rclcpp/qos.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <string>

#include "rclcpp/duration.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rcl/logging_rosout.h"
#include "rmw/incompatible_qos_events_statuses.h"
Expand Down Expand Up @@ -62,6 +63,13 @@ enum class LivelinessPolicy
Unknown = RMW_QOS_POLICY_LIVELINESS_UNKNOWN,
};

enum class QoSCompatibility
{
Ok = RMW_QOS_COMPATIBILITY_OK,
Warning = RMW_QOS_COMPATIBILITY_WARNING,
Error = RMW_QOS_COMPATIBILITY_ERROR,
};

/// QoS initialization values, cannot be created directly, use KeepAll or KeepLast instead.
struct RCLCPP_PUBLIC QoSInitialization
{
Expand Down Expand Up @@ -273,6 +281,61 @@ bool operator==(const QoS & left, const QoS & right);
RCLCPP_PUBLIC
bool operator!=(const QoS & left, const QoS & right);

/// Result type for checking QoS compatibility
/**
* \see rclcpp::qos_check_compatible()
*/
struct QoSCheckCompatibleResult
{
/// Compatibility result.
QoSCompatibility compatibility;

/// Reason for a (possible) incompatibility.
/**
* Set if compatiblity is QoSCompatibility::Warning or QoSCompatiblity::Error.
* Not set if the QoS profiles are compatible.
*/
std::string reason;
};

/// Check if two QoS profiles are compatible.
/**
* Two QoS profiles are compatible if a publisher and subcription
* using the QoS policies can communicate with each other.
*
* If any policies have value "system default" or "unknown" then it is possible that
* compatiblity cannot be determined.
* In this case, the value QoSCompatility::Warning is set as part of
* the returned structure.
*
* Example usage:
*
* ```cpp
* rclcpp::QoSCheckCompatibleResult result = rclcpp::qos_check_compatible(
* publisher_qos, subscription_qos);
* if (rclcpp::QoSCompatibility::Error != result.compatibility) {
* // QoS not compatible ...
* // result.reason contains info about the incompatibility
* } else if (rclcpp::QoSCompatibility::Warning != result.compatibility) {
* // QoS may not be compatible ...
* // result.reason contains info about the possible incompatibility
* }
* ```
*
* \param[in] publisher_qos: The QoS profile for a publisher.
* \param[in] subscription_qos: The QoS profile for a subscription.
* \return Struct with compatiblity set to QoSCompatibility::Ok if the QoS profiles are
* compatible, or
* \return Struct with compatibility set to QoSCompatibility::Warning if there is a chance
* the QoS profiles are not compatible, or
* \return Struct with compatibility set to QoSCompatibility::Error if the QoS profiles are
* not compatible.
* \throws rclcpp::exceptions::QoSCheckCompatibilityException if an unexpected error occurs.
*/
RCLCPP_PUBLIC
QoSCheckCompatibleResult
qos_check_compatible(const QoS & publisher_qos, const QoS & subscription_qos);

/**
* Clock QoS class
* - History: Keep last,
Expand Down
41 changes: 41 additions & 0 deletions rclcpp/src/rclcpp/qos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

#include <string>

#include "rmw/error_handling.h"
#include "rmw/types.h"
#include "rmw/qos_profiles.h"

namespace rclcpp
{
Expand Down Expand Up @@ -309,6 +311,45 @@ bool operator!=(const QoS & left, const QoS & right)
return !(left == right);
}

QoSCheckCompatibleResult
qos_check_compatible(const QoS & publisher_qos, const QoS & subscription_qos)
{
rmw_qos_compatibility_type_t compatible;
const size_t reason_size = 2048u;
char reason_c_str[reason_size] = "";
rmw_ret_t ret = rmw_qos_profile_check_compatible(
publisher_qos.get_rmw_qos_profile(),
subscription_qos.get_rmw_qos_profile(),
&compatible,
reason_c_str,
reason_size);
if (RMW_RET_OK != ret) {
std::string error_str(rmw_get_error_string().str);
rmw_reset_error();
throw rclcpp::exceptions::QoSCheckCompatibleException{error_str};
}

QoSCheckCompatibleResult result;
result.reason = std::string(reason_c_str);

switch (compatible) {
case RMW_QOS_COMPATIBILITY_OK:
result.compatibility = QoSCompatibility::Ok;
break;
case RMW_QOS_COMPATIBILITY_WARNING:
result.compatibility = QoSCompatibility::Warning;
break;
case RMW_QOS_COMPATIBILITY_ERROR:
result.compatibility = QoSCompatibility::Error;
break;
default:
throw rclcpp::exceptions::QoSCheckCompatibleException{
"Unexpected compatibility value returned by rmw '" + std::to_string(compatible) +
"'"};
}
return result;
}

ClockQoS::ClockQoS(const QoSInitialization & qos_initialization)
// Using `rmw_qos_profile_sensor_data` intentionally.
// It's best effort and `qos_initialization` is overriding the depth to 1.
Expand Down
42 changes: 42 additions & 0 deletions rclcpp/test/rclcpp/test_qos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

#include <gtest/gtest.h>

#include <string>

#include "rclcpp/qos.hpp"

#include "rmw/types.h"
Expand Down Expand Up @@ -208,3 +210,43 @@ TEST(TestQoS, policy_name_from_kind) {
"LIFESPAN_QOS_POLICY",
rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_LIFESPAN));
}

TEST(TestQoS, qos_check_compatible)
{
// Compatible
{
rclcpp::QoS qos = rclcpp::QoS(1)
.reliable()
.durability_volatile()
.deadline(rclcpp::Duration(1, 0u))
.lifespan(rclcpp::Duration(1, 0u))
.liveliness(rclcpp::LivelinessPolicy::Automatic)
.liveliness_lease_duration(rclcpp::Duration(1, 0u));
rclcpp::QoSCheckCompatibleResult ret = rclcpp::qos_check_compatible(qos, qos);
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Ok);
EXPECT_EQ(ret.reason, std::string(""));
}

// Note, the following incompatible tests assume we are using a DDS middleware,
// and may not be valid for other RMWs.
// TODO(jacobperron): programmatically check if current RMW is one of the officially
// supported DDS middlewares before running the following tests

// Incompatible
{
rclcpp::QoS pub_qos = rclcpp::QoS(1).best_effort();
rclcpp::QoS sub_qos = rclcpp::QoS(1).reliable();
rclcpp::QoSCheckCompatibleResult ret = rclcpp::qos_check_compatible(pub_qos, sub_qos);
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Error);
EXPECT_FALSE(ret.reason.empty());
}

// Warn of possible incompatibility
{
rclcpp::SystemDefaultsQoS pub_qos;
rclcpp::QoS sub_qos = rclcpp::QoS(1).reliable();
rclcpp::QoSCheckCompatibleResult ret = rclcpp::qos_check_compatible(pub_qos, sub_qos);
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Warning);
EXPECT_FALSE(ret.reason.empty());
}
}