Skip to content

Commit

Permalink
[backport foxy] Add QoS decoding translation for infinite durations t…
Browse files Browse the repository at this point in the history
…o RMW_DURATION_INFINITE (backport #684) (#1088)

* Add QoS decoding translation for infinite durations to RMW_DURATION_INFINITE (#684)

* Add QoS decoding translation for infinite durations to RMW_DURATION_INFINITE

Signed-off-by: Emerson Knapp <eknapp@amazon.com>
(cherry picked from commit de70a81)
Signed-off-by: Emerson Knapp <emerson.b.knapp@gmail.com>

* Backport rmw time utilities into private space in rosbag2_transport library

Signed-off-by: Emerson Knapp <emerson.b.knapp@gmail.com>

* Change bag name for tests

Signed-off-by: Emerson Knapp <emerson.b.knapp@gmail.com>

---------

Signed-off-by: Emerson Knapp <eknapp@amazon.com>
Signed-off-by: Emerson Knapp <emerson.b.knapp@gmail.com>
Co-authored-by: Emerson Knapp <537409+emersonknapp@users.noreply.github.com>
Co-authored-by: Emerson Knapp <emerson.b.knapp@gmail.com>
Co-authored-by: Michael Orlov <michael.orlov@apex.ai>
  • Loading branch information
4 people authored May 26, 2023
1 parent 3405be1 commit b1507b6
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 1 deletion.
2 changes: 1 addition & 1 deletion ros2bag/test/test_record_qos_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def test_incomplete_qos_duration(self):
def test_nonexistent_qos_profile(self):
profile_path = PROFILE_PATH / 'foobar.yaml'
with tempfile.TemporaryDirectory() as tmpdirname:
output_path = Path(tmpdirname) / 'ros2bag_test_incomplete'
output_path = Path(tmpdirname) / 'ros2bag_test_nonexistent_qos'
arguments = ['record', '-a', '--qos-profile-overrides-path', profile_path.as_posix(),
'--output', output_path.as_posix()]
with self.launch_bag_command(arguments=arguments) as bag_command:
Expand Down
4 changes: 4 additions & 0 deletions rosbag2_transport/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ add_library(${PROJECT_NAME} SHARED
src/rosbag2_transport/generic_subscription.cpp
src/rosbag2_transport/qos.cpp
src/rosbag2_transport/recorder.cpp
src/rosbag2_transport/rmw_time.c
src/rosbag2_transport/rosbag2_node.cpp
src/rosbag2_transport/rosbag2_transport.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC
Expand Down Expand Up @@ -115,6 +116,7 @@ function(create_tests_for_rmw_implementation)
src/rosbag2_transport/generic_publisher.cpp
src/rosbag2_transport/generic_subscription.cpp
src/rosbag2_transport/qos.cpp
src/rosbag2_transport/rmw_time.c
src/rosbag2_transport/recorder.cpp
src/rosbag2_transport/rosbag2_node.cpp
test/rosbag2_transport/test_rosbag2_node.cpp
Expand All @@ -138,6 +140,7 @@ function(create_tests_for_rmw_implementation)

rosbag2_transport_add_gmock(test_qos
src/rosbag2_transport/qos.cpp
src/rosbag2_transport/rmw_time.c
test/rosbag2_transport/test_qos.cpp
INCLUDE_DIRS
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
Expand All @@ -163,6 +166,7 @@ function(create_tests_for_rmw_implementation)

rosbag2_transport_add_gmock(test_play
src/rosbag2_transport/qos.cpp
src/rosbag2_transport/rmw_time.c
test/rosbag2_transport/test_play.cpp
INCLUDE_DIRS $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/rosbag2_transport>
LINK_LIBS rosbag2_transport
Expand Down
31 changes: 31 additions & 0 deletions rosbag2_transport/src/rosbag2_transport/qos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,27 @@
#include "rosbag2_transport/logging.hpp"

#include "qos.hpp"
#include "./rmw_time.h"

namespace
{
/**
* The following constants were the "Inifinity" value returned by RMW implementations before
* the introduction of RMW_DURATION_INFINITE and associated RMW fixes
* RMW: https://github.com/ros2/rmw/pull/301
* Fast-DDS: https://github.com/ros2/rmw_fastrtps/pull/515
* CycloneDDS: https://github.com/ros2/rmw_cyclonedds/pull/288
* RTI Connext: https://github.com/ros2/rmw_connext/pull/491
*
* These values exist in bags recorded in Foxy, they need to be translated to RMW_DURATION_INFINITE
* to be consistently understood for playback.
* With those values, if a bag is played back in a different implementation than it was recorded,
* the publishers will fail to be created with an error indicating an invalid QoS value..
*/
static const rmw_time_t RMW_CYCLONEDDS_FOXY_INFINITE = rmw_time_from_nsec(0x7FFFFFFFFFFFFFFFll);
static const rmw_time_t RMW_FASTRTPS_FOXY_INFINITE {0x7FFFFFFFll, 0xFFFFFFFFll};
static const rmw_time_t RMW_CONNEXT_FOXY_INFINITE {0x7FFFFFFFll, 0x7FFFFFFFll};
} // namespace

namespace YAML
{
Expand All @@ -33,6 +54,16 @@ bool convert<rmw_time_t>::decode(const Node & node, rmw_time_t & time)
{
time.sec = node["sec"].as<uint64_t>();
time.nsec = node["nsec"].as<uint64_t>();
if (
rmw_time_equal(time, RMW_CYCLONEDDS_FOXY_INFINITE) ||
rmw_time_equal(time, RMW_FASTRTPS_FOXY_INFINITE) ||
rmw_time_equal(time, RMW_CONNEXT_FOXY_INFINITE))
{
// [foxy compatibility] Though we've backported rmw/time.c into this library
// the Foxy RMW layers don't know what to do with the RMW_DURATION_INFINITE value,
// just leave it unspecified for the same behavior.
time = RMW_DURATION_UNSPECIFIED;
}
return true;
}

Expand Down
62 changes: 62 additions & 0 deletions rosbag2_transport/src/rosbag2_transport/rmw_time.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// 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.

// NOTE: This file is copied from Rolling rmw, as part of backporting bugfix #684 to Foxy

#include "rmw_time.h" // NOLINT

#include "rcutils/time.h"

bool
rmw_time_equal(const rmw_time_t left, const rmw_time_t right)
{
return rmw_time_total_nsec(left) == rmw_time_total_nsec(right);
}

rmw_duration_t
rmw_time_total_nsec(const rmw_time_t time)
{
static const uint64_t max_sec = INT64_MAX / RCUTILS_S_TO_NS(1);
if (time.sec > max_sec) {
// Seconds not representable in nanoseconds
return INT64_MAX;
}

const int64_t sec_as_nsec = RCUTILS_S_TO_NS(time.sec);
if (time.nsec > (uint64_t)(INT64_MAX - sec_as_nsec)) {
// overflow
return INT64_MAX;
}
return sec_as_nsec + time.nsec;
}

rmw_time_t
rmw_time_from_nsec(const rmw_duration_t nanoseconds)
{
if (nanoseconds < 0) {
return (rmw_time_t)RMW_DURATION_INFINITE;
}

// Avoid typing the 1 billion constant
rmw_time_t time;
time.sec = RCUTILS_NS_TO_S(nanoseconds);
time.nsec = nanoseconds % RCUTILS_S_TO_NS(1);
return time;
}

rmw_time_t
rmw_time_normalize(const rmw_time_t time)
{
return rmw_time_from_nsec(rmw_time_total_nsec(time));
}
78 changes: 78 additions & 0 deletions rosbag2_transport/src/rosbag2_transport/rmw_time.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// 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.

// NOTE: This file is copied from Rolling rmw, as part of backporting bugfix #684 to Foxy

#ifndef ROSBAG2_TRANSPORT__RMW_TIME_H_
#define ROSBAG2_TRANSPORT__RMW_TIME_H_

#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus

#include <stdint.h>

#include "rcutils/time.h"

#include "rmw/macros.h"
#include "rmw/visibility_control.h"
#include "rmw/types.h"


typedef rcutils_time_point_value_t rmw_time_point_value_t;
typedef rcutils_duration_value_t rmw_duration_t;

/// Constant representing an infinite duration. Use rmw_time_equal for comparisons.
/**
* Different RMW implementations have different representations for infinite durations.
* This value is reported for QoS policy durations that are left unspecified.
* Do not directly compare `sec == sec && nsec == nsec`, because we don't want to be sensitive
* to non-normalized values (nsec > 1 second) - use rmw_time_equal instead.
* This value is INT64_MAX nanoseconds = 0x7FFF FFFF FFFF FFFF = d 9 223 372 036 854 775 807
*
* Note: these constants cannot be `static const rmw_time_t` because in C that can't be used
* as a compile-time initializer
*/
#define RMW_DURATION_INFINITE {9223372036LL, 854775807LL}
#define RMW_DURATION_UNSPECIFIED {0LL, 0LL}

/// Check whether two rmw_time_t represent the same time.
bool
rmw_time_equal(const rmw_time_t left, const rmw_time_t right);

/// Return the total nanosecond representation of a time.
/**
* \return INT64_MAX if input is too large to store in 64 bits
*/
rmw_duration_t
rmw_time_total_nsec(const rmw_time_t time);

/// Construct rmw_time_t from a total nanoseconds representation.
/**
* rmw_time_t only specifies relative time, so the origin is not relevant for this calculation.
* \return RMW_DURATION_INFINITE if input is negative, which is not representable in rmw_time_t
*/
rmw_time_t
rmw_time_from_nsec(const rmw_duration_t nanoseconds);

/// Ensure that an rmw_time_t does not have nanoseconds > 1 second.
rmw_time_t
rmw_time_normalize(const rmw_time_t time);

#ifdef __cplusplus
}
#endif // __cplusplus

#endif // ROSBAG2_TRANSPORT__RMW_TIME_H_
48 changes: 48 additions & 0 deletions rosbag2_transport/test/rosbag2_transport/test_qos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "rmw/types.h"

#include "qos.hpp"
#include "rmw_time.h" // NOLINT

TEST(TestQoS, serialization)
{
Expand Down Expand Up @@ -96,6 +97,53 @@ TEST(TestQoS, detect_new_qos_fields)
EXPECT_EQ(profile.history, RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT); // fix "unused variable"
}

TEST(TestQoS, translates_bad_infinity_values)
{
// Copied from hidden symbols in qos.cpp
const rmw_time_t bad_infinities[3] {
rmw_time_from_nsec(0x7FFFFFFFFFFFFFFFll), // cyclone
{0x7FFFFFFFll, 0xFFFFFFFFll}, // fastrtps
{0x7FFFFFFFll, 0x7FFFFFFFll} // connext
};
rmw_time_t infinity = RMW_DURATION_UNSPECIFIED;
const auto expected_qos = rosbag2_transport::Rosbag2QoS{}
.default_history()
.reliable()
.durability_volatile()
.deadline(infinity)
.lifespan(infinity)
.liveliness(RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT)
.liveliness_lease_duration(infinity)
.get_rmw_qos_profile();

for (const auto & infinity : bad_infinities) {
std::ostringstream serialized_profile;
serialized_profile <<
"history: 1\n"
"depth: 10\n"
"reliability: 1\n"
"durability: 2\n"
"deadline:\n"
" sec: " << infinity.sec << "\n"
" nsec: " << infinity.nsec << "\n"
"lifespan:\n"
" sec: " << infinity.sec << "\n"
" nsec: " << infinity.nsec << "\n"
"liveliness: 0\n"
"liveliness_lease_duration:\n"
" sec: " << infinity.sec << "\n"
" nsec: " << infinity.nsec << "\n"
"avoid_ros_namespace_conventions: false\n";
const YAML::Node loaded_node = YAML::Load(serialized_profile.str());
const auto deserialized_profile = loaded_node.as<rosbag2_transport::Rosbag2QoS>();
const auto actual_qos = deserialized_profile.get_rmw_qos_profile();
EXPECT_TRUE(rmw_time_equal(actual_qos.lifespan, expected_qos.lifespan));
EXPECT_TRUE(rmw_time_equal(actual_qos.deadline, expected_qos.deadline));
EXPECT_TRUE(
rmw_time_equal(
actual_qos.liveliness_lease_duration, expected_qos.liveliness_lease_duration));
}
}

using rosbag2_transport::Rosbag2QoS; // NOLINT
class AdaptiveQoSTest : public ::testing::Test
Expand Down

0 comments on commit b1507b6

Please sign in to comment.