Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add function for checking QoS profile compatibility #45

Merged
merged 15 commits into from
Feb 28, 2021
Merged
6 changes: 6 additions & 0 deletions rmw_dds_common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ rosidl_generate_interfaces(
add_library(${PROJECT_NAME}_library SHARED
src/gid_utils.cpp
src/graph_cache.cpp
src/qos.cpp
src/time_utils.cpp)

set_target_properties(${PROJECT_NAME}_library
Expand Down Expand Up @@ -92,6 +93,11 @@ if(BUILD_TESTING)
target_link_libraries(test_gid_utils ${PROJECT_NAME}_library)
endif()

ament_add_gmock(test_qos test/test_qos.cpp)
if(TARGET test_qos)
target_link_libraries(test_qos ${PROJECT_NAME}_library)
endif()

ament_add_gmock(test_time_utils test/test_time_utils.cpp)
if(TARGET test_time_utils)
target_link_libraries(test_time_utils ${PROJECT_NAME}_library)
Expand Down
1 change: 1 addition & 0 deletions rmw_dds_common/docs/FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ This package includes:
- A generic [`Context`](rmw_dds_common/include/rmw_dds_common/context.hpp) type to withhold most state needed to implement [ROS nodes discovery](https://github.com/ros2/design/pull/250)
- [Comparison utilities and some C++ operator overloads](rmw_dds_common/include/rmw_dds_common/gid_utils.hpp) for `rmw_gid_t` instances
- [Conversion utilities](rmw_dds_common/include/rmw_dds_common/gid_utils.hpp) between `rmw_dds_common/msg/Gid` messages and `rmw_gid_t` instances
- A function for checking the compatibility of two QoS profiles, [`qos_profile_check_compatible`](rmw_dds_common/include/rmw_dds_common/qos.hpp)
58 changes: 58 additions & 0 deletions rmw_dds_common/include/rmw_dds_common/qos.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RMW_DDS_COMMON__QOS_HPP_
#define RMW_DDS_COMMON__QOS_HPP_

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

#include "rmw_dds_common/visibility_control.h"

namespace rmw_dds_common
{

/// Check if two QoS profiles are compatible
/**
* Two QoS profiles are compatible if a publisher and subcription

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for my edification: I've always wondered about the inconsistent terminology, IMO: "publisher" and subscription" versus, "publisher" and "subscriber" or "publication" and "subscription." Any quick background info on that?

Copy link
Member Author

@jacobperron jacobperron Feb 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH, I can't recall why that terminology. I'm just following suit with the terminology used throughout the rest of the ROS 2 stack, though I think we're also inconsistent about what terminology we're using 🙃 The tutorials seem to use "subscriber" instead of "subscription".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wjwwood Probably has some insight.

Copy link
Member

@wjwwood wjwwood Feb 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A publisher publishes, a subscription is a receipt of a subscription, it isn't a subscriber. So subscriber is the wrong term. A node is kind of a subscriber as it is the thing that subscribes. A publication could be a thing, but because our object publishes, it is a publisher.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wjwwood Interesting. I guess I just think of it more like this (probably attracted to the symmetry/uniformity):

Anyways, thanks for the info. It helps to fill in some of the blanks.

* using the QoS policies can communicate with each other.
*
* This implements the rmw API \ref rmw_qos_profile_check_compatible().
* See \ref rmw_qos_profile_check_compatible() for more information.
*
* \param[in] publisher_profile: The QoS profile used for a publisher.
* \param[in] subscription_profile: The QoS profile used for a subscription.
* \param[out] compatibility: `RMW_QOS_COMPATIBILITY_OK` if the QoS profiles are compatible, or
* `RMW_QOS_COMPATIBILITY_WARNING` if the QoS profiles might be compatible, or
* `RMW_QOS_COMPATIBILITY_ERROR` if the QoS profiles are not compatible.
* \param[out] reason: A detailed reason for a QoS incompatibility.
* Must be pre-allocated by the caller. This parameter is optional and may be set to `NULL`.
* \param[in] reason_size: Size of the string buffer `reason`, if one is provided.
* \return `RMW_RET_OK` if the check was successful, or
* \return `RMW_RET_INVALID_ARGUMENT` if `compatiblity` is NULL, or
* \return `RMW_RET_INVALID_ARGUMENT` if any of the policies have value "unknown".
* \return `RMW_RET_ERROR` if there is an unexpected error.
*/
RMW_DDS_COMMON_PUBLIC
rmw_ret_t
qos_profile_check_compatible(
const rmw_qos_profile_t publisher_qos,
const rmw_qos_profile_t subscription_qos,
rmw_qos_compatibility_type_t * compatibility,
char * reason,
size_t reason_size);

} // namespace rmw_dds_common

#endif // RMW_DDS_COMMON__QOS_HPP_
308 changes: 308 additions & 0 deletions rmw_dds_common/src/qos.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "rmw_dds_common/qos.hpp"

#include <cstring>

#include "rcutils/snprintf.h"
#include "rmw/error_handling.h"
#include "rmw/qos_profiles.h"

namespace rmw_dds_common
{

static bool
operator==(rmw_time_t t1, rmw_time_t t2)
{
return t1.sec == t2.sec && t1.nsec == t2.nsec;
}

static bool
operator!=(rmw_time_t t1, rmw_time_t t2)
{
return !(t1 == t2);
}

static bool
operator<(rmw_time_t t1, rmw_time_t t2)
{
if (t1.sec < t2.sec) {
return true;
} else if (t1.sec == t2.sec && t1.nsec < t2.nsec) {
return true;
}
return false;
}

// Returns RMW_RET_OK if successful or no buffer was provided
// Returns RMW_RET_ERROR if there as an error copying the message to the buffer
static rmw_ret_t
_append_to_buffer(const char * message, char * buffer, size_t buffer_size)
{
// Only write if a buffer is provided
if (!buffer || buffer_size == 0u) {
return RMW_RET_OK;
}
// Determine available space left in buffer
size_t offset = strnlen(buffer, buffer_size);
size_t write_size = buffer_size - offset;
int snprintf_ret = rcutils_snprintf(buffer + offset, write_size, "%s", message);
if (snprintf_ret < 0) {
RMW_SET_ERROR_MSG("failed to append to character buffer");
return RMW_RET_ERROR;
}
return RMW_RET_OK;
}

rmw_ret_t
qos_profile_check_compatible(
const rmw_qos_profile_t publisher_qos,
const rmw_qos_profile_t subscription_qos,
rmw_qos_compatibility_type_t * compatibility,
char * reason,
size_t reason_size)
{
if (!compatibility) {
RMW_SET_ERROR_MSG("compatibility parameter is null");
return RMW_RET_INVALID_ARGUMENT;
}

if (!reason && reason_size != 0u) {
RMW_SET_ERROR_MSG("reason parameter is null, but reason_size parameter is not zero");
return RMW_RET_INVALID_ARGUMENT;
}

// If there are any "unknown" values, then there is an error
if (RMW_QOS_POLICY_RELIABILITY_UNKNOWN == publisher_qos.reliability) {
RMW_SET_ERROR_MSG("Publisher reliability is unknown");
return RMW_RET_INVALID_ARGUMENT;
}
if (RMW_QOS_POLICY_RELIABILITY_UNKNOWN == subscription_qos.reliability) {
RMW_SET_ERROR_MSG("Subscription reliability is unknown");
return RMW_RET_INVALID_ARGUMENT;
}
if (RMW_QOS_POLICY_DURABILITY_UNKNOWN == publisher_qos.durability) {
RMW_SET_ERROR_MSG("Publisher durability is unknown");
return RMW_RET_INVALID_ARGUMENT;
}
if (RMW_QOS_POLICY_DURABILITY_UNKNOWN == subscription_qos.durability) {
RMW_SET_ERROR_MSG("Subscription durability is unknown");
return RMW_RET_INVALID_ARGUMENT;
}
if (RMW_QOS_POLICY_LIVELINESS_UNKNOWN == publisher_qos.liveliness) {
RMW_SET_ERROR_MSG("Publisher liveliness is unknown");
return RMW_RET_INVALID_ARGUMENT;
}
if (RMW_QOS_POLICY_LIVELINESS_UNKNOWN == subscription_qos.liveliness) {
RMW_SET_ERROR_MSG("Subscription liveliness is unknown");
return RMW_RET_INVALID_ARGUMENT;
}

// Presume profiles are compatible until proven otherwise
*compatibility = RMW_QOS_COMPATIBILITY_OK;

// Best effort publisher and reliable subscription
if (publisher_qos.reliability == RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT &&
subscription_qos.reliability == RMW_QOS_POLICY_RELIABILITY_RELIABLE)
{
*compatibility = RMW_QOS_COMPATIBILITY_ERROR;
rmw_ret_t append_ret = _append_to_buffer(
"ERROR: Best effort publisher and reliable subscription;",
reason,
reason_size);
if (RMW_RET_OK != append_ret) {
return append_ret;
}
}

// Volatile publisher and transient local subscription
if (publisher_qos.durability == RMW_QOS_POLICY_DURABILITY_VOLATILE &&
subscription_qos.durability == RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL)
{
*compatibility = RMW_QOS_COMPATIBILITY_ERROR;
rmw_ret_t append_ret = _append_to_buffer(
"ERROR: Volatile publisher and transient local subscription;",
reason,
reason_size);
if (RMW_RET_OK != append_ret) {
return append_ret;
}
}

const rmw_time_t & pub_deadline = publisher_qos.deadline;
const rmw_time_t & sub_deadline = subscription_qos.deadline;
const rmw_time_t deadline_default = RMW_QOS_DEADLINE_DEFAULT;

// No deadline for publisher and deadline for subscription
if (pub_deadline == deadline_default && sub_deadline != deadline_default) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The policies in ROS have this annoying SYSTEM_DEFAULT thing which I have always read as the application explicitly "don't care, do what you think is best", which doesn't make much sense to me. Secondly, I've always interpreted that setting as valid only when creating an endpoint, but never as a value returned by the underlying middleware.

If that interpretation is correct (which it may not be, given that the SYSTEM_DEFAULT is handled later in this function), then it seems illogical that a default deadline setting would be treated differently here. Doesn't it make far more sense to simply say the default deadline is ∞ (however represented)? That way, there always is a deadline.

If you then define the comparison functions such that ∞ is handled correctly, this all becomes much simpler, IMHO.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Secondly, I've always interpreted that setting as valid only when creating an endpoint, but never as a value returned by the underlying middleware.

Yes, that should be the case.

If that interpretation is correct (which it may not be, given that the SYSTEM_DEFAULT is handled later in this function), then it seems illogical that a default deadline setting would be treated differently here. Doesn't it make far more sense to simply say the default deadline is ∞ (however represented)? That way, there always is a deadline.

But the "system default" could be different from infinite, e.g. if you specify in a qos profile file a default profile with a different deadline value (which would be weird, but possible).
I would only use this function with "actual" qos profiles , and not with one where an item can be "SYSTEM_DEFAULT", so we can also consider failing if an item is "SYSTEM_DEFAULT".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this new function is failing when a policy has value "UNKNOWN", which is used when an entity has a qos value valid for the underlying middleware that hasn't an equivalent in ROS 2.

I'm not sure why this function should return a compatibility warning when "SYSTEM_DEFAULT" and an error when "UNKNOWN".
I actually think that the opposite makes more sense, or either returning a compatibility warning or error in both.

@jacobperron what do you think?

Copy link
Member Author

@jacobperron jacobperron Feb 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the "system default" could be different from infinite, e.g. if you specify in a qos profile file a default profile with a different deadline value (which would be weird, but possible).

I wasn't sure if RMW_QOS_DEADLINE_DEFAULT is equivalent to "system default". If it is, then I agree we should treat it the same as other system default values (e.g. warn).

I would only use this function with "actual" qos profiles , and not with one where an item can be "SYSTEM_DEFAULT", so we can also consider failing if an item is "SYSTEM_DEFAULT".

Unfortunately, all of the widely used profiles defined in rmw use "SYSTEM_DEFAULT" values. I think it would be unfortunate if this function failed doing something like this:

rmw_qos_profile_check_compatible( rmw_qos_profile_sensor_data, rmw_qos_profile_sensor_data, ...);

Although, producing a warning isn't much better...

Maybe we should change the default values for those profiles 😅

Actually, this new function is failing when a policy has value "UNKNOWN", which is used when an entity has a qos value valid for the underlying middleware that hasn't an equivalent in ROS 2.

I don't know if this applies here. This is a common implementation for DDS that presumes the RMW supports all the ROS 2 QoS settings (we could document it here to be explicit). If an RMW doesn't support all of the QoS policies, then they should probably provide their own implementation of this function, especially if the RMW is not a DDS implementation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this function for?

I imagine it being used in two ways:

  • Check if the qos profile of two existing endpoints is compatible (for example, using the qos profiles got from the rmw_get_publishers_info_by_topic/rmw_get_subscription_info_by_topic).
    This use case doesn't seem super useful though, better to use the "Qos incompatible event".
  • Check if an user provided profile is compatible with the profile of an existing(s) endpoint (one profile got from the functions above, the other is user provided).
    Useful if you want to "make up" a profile compatible with existing entities.

In case 1, "SYSTEM_DEFAULT" is impossible, and "UNKNOWN" is a possible value.
In case 2, the user provided profile can have a "SYSTEM_DEFAULT" value, but I'm not sure why someone would pass that.

I don't know if this applies here. This is a common implementation for DDS that presumes the RMW supports all the ROS 2 QoS settings (we could document it here to be explicit). If an RMW doesn't support all of the QoS policies

Yes, and in that case you can still get "UNKNOWN".
e.g.:

  • create a publisher with durability=SYSTEM_DEFAULT
  • pass a qos profile file that specifies "DDS_TRANSIENT_DURABILITY_QOS" (using topic filters if desired) (transient is not the same as transient local)
  • call rmw_get_publishers_info_by_topic(), the qos profile returned will have durability=UNKNOWN.

Same applies to any application mixing native DDS with ROS 2.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ivanpauno Yeah, those are the two uses of this function that I had in mind. And the first one is the primary motivation for this API. AFAIK, tools that want to introspect the ROS graph (e.g. ros2doctor and rqt_graph) cannot get "QoS incompatible" events for all nodes in the graph. This API let's them warn about (possible) compatibility issues.

I guess it is technically possible to get a "SYSTEM_DEFAULT" back from rmw_get_publishers_info_by_topic. Though it's not very useful, there's nothing that says it can't return such a value. My thought is that this function will report possible compatibility issues to the best of it's ability. If it gets a policy value of "SYSTEM_DEFAULT" or "UNKNOWN" then the best we can do is warn about possible incompatibilities. Originally, I didn't think that it was possible to get "UNKNOWN" values from rmw_get_publishers_info_by_topic / rmw_get_subscription_info_by_topic, but if that is the case then I agree we should change the behavior of this function to produce a warning instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, tools that want to introspect the ROS graph (e.g. ros2doctor and rqt_graph) cannot get "QoS incompatible" events for all nodes in the graph

Ah yes, that's true.

I guess it is technically possible to get a "SYSTEM_DEFAULT" back from rmw_get_publishers_info_by_topic

rmw_get_publishers_info_by_topic should not return "SYSTEM_DEFAULT", but a more specific value.

but if that is the case then I agree we should change the behavior of this function to produce a warning instead.

Using a warning for both system default and unknown sounds good.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning on "unknown" values: 6c789b4

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it is technically possible to get a "SYSTEM_DEFAULT" back from rmw_get_publishers_info_by_topic

rmw_get_publishers_info_by_topic should not return "SYSTEM_DEFAULT", but a more specific value.

I think that rmw_get_publishers_info_by_topic should try and avoid returning system default, or shouldn't ever. The idea is that system default is a stand-in for "some externally specified setting", so when querying it for a real entity, it should just return the ROS equivalent or unknown if there isn't an equivalent.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To circle back to the original comment, I think I'll leave the current implementation as-is since it seems to agree with the RMW API docs and https://index.ros.org/doc/ros2/Concepts/About-Quality-of-Service-Settings/#qos-compatibilities.
However, I do think that replacing the default duration value with an explicit infinity value would be better (related to ros2/rmw#301). Pending further changes to the RMW API, I'm happy to come back and simplify the logic in this implementation.

While endpoints may not return "SYSTEM_DEFAULT" values, it's possible that other use-cases not involving endpoint queries will call this function. So I think it's nice to warn about "SYSTEM_DEFAULT" values, instead of returning an error with no info about other possible incompatibilities.

*compatibility = RMW_QOS_COMPATIBILITY_ERROR;
rmw_ret_t ret = _append_to_buffer(
"ERROR: Subscription has a deadline, but publisher does not;",
reason,
reason_size);
if (RMW_RET_OK != ret) {
return ret;
}
}

// Subscription deadline is less than publisher deadline
if (pub_deadline != deadline_default && sub_deadline != deadline_default) {
if (sub_deadline < pub_deadline) {
*compatibility = RMW_QOS_COMPATIBILITY_ERROR;
rmw_ret_t append_ret = _append_to_buffer(
"ERROR: Subscription deadline is less than publisher deadline;",
reason,
reason_size);
if (RMW_RET_OK != append_ret) {
return append_ret;
}
}
}

// Automatic liveliness for publisher and manual by topic for subscription
if (publisher_qos.liveliness == RMW_QOS_POLICY_LIVELINESS_AUTOMATIC &&
subscription_qos.liveliness == RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC)
{
*compatibility = RMW_QOS_COMPATIBILITY_ERROR;
rmw_ret_t append_ret = _append_to_buffer(
"ERROR: Publisher's liveliness is automatic and subscription's is manual by topic;",
reason,
reason_size);
if (RMW_RET_OK != append_ret) {
return append_ret;
}
}

const rmw_time_t & pub_lease = publisher_qos.liveliness_lease_duration;
const rmw_time_t & sub_lease = subscription_qos.liveliness_lease_duration;
const rmw_time_t lease_default = RMW_QOS_LIVELINESS_LEASE_DURATION_DEFAULT;

// No lease duration for publisher and lease duration for subscription
if (pub_lease == lease_default && sub_lease != lease_default) {
*compatibility = RMW_QOS_COMPATIBILITY_ERROR;
rmw_ret_t append_ret = _append_to_buffer(
"ERROR: Subscription has a liveliness lease duration, but publisher does not;",
reason,
reason_size);
if (RMW_RET_OK != append_ret) {
return append_ret;
}
}

// Subscription lease duration is less than publisher lease duration
if (pub_lease != lease_default && sub_lease != lease_default) {
if (sub_lease < pub_lease) {
*compatibility = RMW_QOS_COMPATIBILITY_ERROR;
rmw_ret_t append_ret = _append_to_buffer(
"ERROR: Subscription liveliness lease duration is less than publisher;",
reason,
reason_size);
if (RMW_RET_OK != append_ret) {
return append_ret;
}
}
}

// Only check for warnings if there are no errors
if (RMW_QOS_COMPATIBILITY_OK == *compatibility) {
// Reliability for publisher is "system default" and subscription is reliable
if (publisher_qos.reliability == RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT &&
subscription_qos.reliability == RMW_QOS_POLICY_RELIABILITY_RELIABLE)
{
*compatibility = RMW_QOS_COMPATIBILITY_WARNING;
rmw_ret_t append_ret = _append_to_buffer(
"WARNING: Reliable subscription, but publisher is system default;",
reason,
reason_size);
if (RMW_RET_OK != append_ret) {
return append_ret;
}
} else {
// Reliability for publisher is best effort and subscription is "system default"
if (publisher_qos.reliability == RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT &&
subscription_qos.reliability == RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT)
{
*compatibility = RMW_QOS_COMPATIBILITY_WARNING;
rmw_ret_t append_ret = _append_to_buffer(
"WARNING: Best effort publisher, but subscription is system default;",
reason,
reason_size);
if (RMW_RET_OK != append_ret) {
return append_ret;
}
}
}

// Durability for publisher is "system default" and subscription is transient local
if (publisher_qos.durability == RMW_QOS_POLICY_DURABILITY_SYSTEM_DEFAULT &&
subscription_qos.durability == RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL)
{
*compatibility = RMW_QOS_COMPATIBILITY_WARNING;
rmw_ret_t ret = _append_to_buffer(
"WARNING: Transient local subscription, but publisher is system default;",
reason,
reason_size);
if (RMW_RET_OK != ret) {
return ret;
}
} else {
// Durability for publisher is volatile and subscription is "system default"
if (publisher_qos.durability == RMW_QOS_POLICY_DURABILITY_VOLATILE &&
subscription_qos.durability == RMW_QOS_POLICY_DURABILITY_SYSTEM_DEFAULT)
{
*compatibility = RMW_QOS_COMPATIBILITY_WARNING;
rmw_ret_t ret = _append_to_buffer(
"WARNING: Volatile publisher, but subscription is system default;",
reason,
reason_size);
if (RMW_RET_OK != ret) {
return ret;
}
}
}

// Automatic liveliness for publisher and "system default" for subscription
if (publisher_qos.liveliness == RMW_QOS_POLICY_LIVELINESS_AUTOMATIC &&
subscription_qos.liveliness == RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT)
{
*compatibility = RMW_QOS_COMPATIBILITY_WARNING;
rmw_ret_t ret = _append_to_buffer(
"WARNING: Publisher's liveliness is automatic, but subscription's is system default;",
reason,
reason_size);
if (RMW_RET_OK != ret) {
return ret;
}
} else {
if (publisher_qos.liveliness == RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT &&
subscription_qos.liveliness == RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC)
{
*compatibility = RMW_QOS_COMPATIBILITY_WARNING;
rmw_ret_t ret = _append_to_buffer(
"WARNING: Subscription's liveliness is manual by topic, but publisher's is system "
"default;",
reason,
reason_size);
if (RMW_RET_OK != ret) {
return ret;
}
}
}
}

return RMW_RET_OK;
}

} // namespace rmw_dds_common
Loading