Compare commits

...

15 Commits

Author SHA1 Message Date
methylDragon
0dddb16bed Add type_description struct conversion utils
Signed-off-by: methylDragon <methylDragon@gmail.com>
2024-12-31 01:33:51 -08:00
Chris Lalancette
a0a2a067d8 29.3.0 2024-12-20 16:16:13 +00:00
Chris Lalancette
094618a044 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-12-20 16:16:06 +00:00
Jeffery Hsu
f5e08c2d0c Fix transient local IPC publish (#2708)
* Fix transient local publish when inter and intra process communications are both present.

* Apply the fix to TypeAdapted signature

* Add an executor to intra_process_inter_process_mix_transient_local test case to enable inter process publishing test

Signed-off-by: Jeffery Hsu <jefferyyjhsu@gmail.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-12-20 09:10:32 -05:00
Tomoya Fujita
016cfeac99 apply actual QoS from rmw to the IPC publisher. (#2707)
* apply actual QoS from rmw to the IPC publisher.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

* address uncrustify warning.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-12-15 21:46:12 -08:00
Steve Macenski
a13e16e2cb Adding in topic name to logging on IPC issues (#2706)
* Adding in topic name to logging on IPC issues

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>

* Update test matching output logging

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>

* adding in single quotes

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>

---------

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>
2024-12-14 09:04:55 -08:00
rcp1
aad8cb53ad Add parsing for rest of obvious boolean extra arguments and throw for unsupported ones (#2685)
Signed-off-by: Robin Petereit <robin.petereit@tum.de>
2024-12-13 08:18:41 -06:00
Tomoya Fujita
8c0161a07f fix TestTimeSource.ROS_time_valid_attach_detach. (#2700)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-12-09 13:15:40 -08:00
Patrick Roncagliolo
a7f05a904a Update docstring for rclcpp::Node::now() (#2696)
Signed-off-by: Patrick Roncagliolo <ronca.pat@gmail.com>
2024-12-04 09:24:50 -08:00
Chris Lalancette
d7245365ed Re-enable executor test on rmw_connextdds. (#2693)
It supports the events executor now, so re-enable the test.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-12-03 00:03:03 -08:00
Chris Lalancette
3310f9eaed Fix warnings on Windows. (#2692)
For reasons I admit I do not understand, the deprecation
warnings for StaticSingleThreadedExecutor on Windows
happen when we construct a shared_ptr for it in the tests.
If we construct a regular object, then it is fine.  Luckily
this test does not require a shared_ptr, so just make it
a regular object here, which rixes the warning.

While we are in here, make all of the tests camel case to
be consistent.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-12-03 00:02:37 -08:00
Nathan Wiebe Neufeldt
785a70d604 Make ament_cmake a buildtool dependency (#2689)
* Make ament_cmake a buildtool dependency

The `ament_cmake` package isn't needed at runtime and so should only be
listed as a `buildtool_dependency`, as is done in most other packages.

* Remove the ament_cmake dependency entirely

Since there's already a buildtool dependency on ament_cmake_ros, having
ament_cmake as well is redundant.

Signed-off-by: Nathan Wiebe Neufeldt <nwiebeneufeldt@clearpath.ai>
2024-12-01 19:49:35 -05:00
Chris Lalancette
9984197c29 Omnibus fixes for running tests with Connext. (#2684)
* Omnibus fixes for running tests with Connext.

When running the tests with RTI Connext as the default
RMW, some of the tests are failing.  There are three
different failures fixed here:

1.  Setting the liveliness duration to a value smaller than
a microsecond causes Connext to throw an error.  Set it to
a millisecond.

2.  Using the SystemDefaultsQoS sets the QoS to KEEP_LAST 1.
Connext is somewhat slow in this regard, so it can be the case
that we are overwriting a previous service introspection event
with the next one.  Switch to the ServicesDefaultQoS in the test,
which ensures we will not lose events.

3.  Connext is slow to match publishers and subscriptions.  Thus,
when creating a subscription "on-the-fly", we should wait for the
publisher to match it before expecting the subscription to actually
receive data from it.

With these fixes in place, the test_client_common, test_generic_service,
test_service_introspection, and test_executors tests all pass for
me with rmw_connextdds.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* Fixes for executors.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* One more fix for services.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* More fixes for service_introspection.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* More fixes for introspection.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

---------

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-11-30 11:25:06 -08:00
jmachowinski
e9b1004247 fix(Executor): Fix segfault if callback group is deleted during rmw_wait (#2683)
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
2024-11-30 11:04:52 -08:00
Chris Lalancette
e64627004f Remove CODEOWNERS and the rolling-to-master job. (#2686)
They are both legacy from times past, and are both
no longer serving their intended purposes.  Just remove them.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-11-26 15:51:01 -05:00
29 changed files with 764 additions and 168 deletions

View File

@@ -1,13 +0,0 @@
name: Mirror rolling to master
on:
push:
branches: [ rolling ]
jobs:
mirror-to-master:
runs-on: ubuntu-latest
steps:
- uses: zofrex/mirror-branch@v1
with:
target-branch: master

View File

@@ -1,2 +0,0 @@
# This file was generated by https://github.com/audrow/update-ros2-repos
* @ivanpauno @hidmic @wjwwood

View File

@@ -2,6 +2,19 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29.3.0 (2024-12-20)
-------------------
* Fix transient local IPC publish (`#2708 <https://github.com/ros2/rclcpp/issues/2708>`_)
* apply actual QoS from rmw to the IPC publisher. (`#2707 <https://github.com/ros2/rclcpp/issues/2707>`_)
* Adding in topic name to logging on IPC issues (`#2706 <https://github.com/ros2/rclcpp/issues/2706>`_)
* fix TestTimeSource.ROS_time_valid_attach_detach. (`#2700 <https://github.com/ros2/rclcpp/issues/2700>`_)
* Update docstring for `rclcpp::Node::now()` (`#2696 <https://github.com/ros2/rclcpp/issues/2696>`_)
* Re-enable executor test on rmw_connextdds. (`#2693 <https://github.com/ros2/rclcpp/issues/2693>`_)
* Fix warnings on Windows. (`#2692 <https://github.com/ros2/rclcpp/issues/2692>`_)
* Omnibus fixes for running tests with Connext. (`#2684 <https://github.com/ros2/rclcpp/issues/2684>`_)
* fix(Executor): Fix segfault if callback group is deleted during rmw_wait (`#2683 <https://github.com/ros2/rclcpp/issues/2683>`_)
* Contributors: Chris Lalancette, Jeffery Hsu, Patrick Roncagliolo, Steve Macenski, Tomoya Fujita, jmachowinski
29.2.0 (2024-11-25)
-------------------
* accept custom allocator for LoanedMessage. (`#2672 <https://github.com/ros2/rclcpp/issues/2672>`_)

View File

@@ -0,0 +1,215 @@
// Copyright 2025 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 RCLCPP__DYNAMIC_TYPESUPPORT__TYPE_DESCRIPTION_CONVERSIONS_HPP_
#define RCLCPP__DYNAMIC_TYPESUPPORT__TYPE_DESCRIPTION_CONVERSIONS_HPP_
#include "rosidl_runtime_cpp/type_description/individual_type_description__struct.hpp"
#include "rosidl_runtime_cpp/type_description/type_description__struct.hpp"
#include "rosidl_runtime_cpp/type_description/type_source__struct.hpp"
#include "type_description_interfaces/msg/individual_type_description.hpp"
#include "type_description_interfaces/msg/type_description.hpp"
#include "type_description_interfaces/msg/type_source.hpp"
namespace rclcpp
{
namespace dynamic_typesupport
{
// IndividualTypeDescription =======================================================================
/// Convert a runtime individual type description struct to its corresponding message.
/**
* This function converts a rosidl_runtime_cpp::type_description::IndividualTypeDescription
* struct to the corresponding type_description_interfaces/msg/IndividualTypeDescription
* msg.
*
* \param[in] runtime_individual_description the runtime struct to convert
* \return the converted individual type description msg
*/
template<
typename ToAllocatorT = std::allocator<void>,
typename FromAllocatorT
>
type_description_interfaces::msg::IndividualTypeDescription_<ToAllocatorT>
convert_individual_type_description_runtime_to_msg(
const rosidl_runtime_cpp::type_description::IndividualTypeDescription_<FromAllocatorT> &
runtime_individual_description, const ToAllocatorT & alloc = ToAllocatorT())
{
type_description_interfaces::msg::IndividualTypeDescription_<ToAllocatorT> out(alloc);
out.type_name = runtime_individual_description.type_name;
for (const auto & field : runtime_individual_description.fields) {
out.fields.emplace_back();
out.fields.back().name = field.name;
out.fields.back().default_value = field.default_value;
out.fields.back().type.type_id = field.type.type_id;
out.fields.back().type.capacity = field.type.capacity;
out.fields.back().type.string_capacity = field.type.string_capacity;
out.fields.back().type.nested_type_name = field.type.nested_type_name;
}
return out;
}
/// Convert a individual type description message to its corresponding runtime struct.
/**
* This function converts a type_description_interfaces/msg/IndividualTypeDescription msg
* to the corresponding rosidl_runtime_cpp::type_description::IndividualTypeDescription
* struct.
*
* \param[in] description_msg the message to convert
* \return the converted runtime struct
*/
template<
typename ToAllocatorT = std::allocator<void>,
typename FromAllocatorT
>
rosidl_runtime_cpp::type_description::IndividualTypeDescription_<ToAllocatorT>
convert_individual_type_description_msg_to_runtime(
const type_description_interfaces::msg::IndividualTypeDescription_<FromAllocatorT> &
individual_description_msg, const ToAllocatorT & alloc = ToAllocatorT())
{
rosidl_runtime_cpp::type_description::IndividualTypeDescription_<ToAllocatorT> out(alloc);
out.type_name = individual_description_msg.type_name;
for (const auto & field : individual_description_msg.fields) {
out.fields.emplace_back();
out.fields.back().name = field.name;
out.fields.back().default_value = field.default_value;
out.fields.back().type.type_id = field.type.type_id;
out.fields.back().type.capacity = field.type.capacity;
out.fields.back().type.string_capacity = field.type.string_capacity;
out.fields.back().type.nested_type_name = field.type.nested_type_name;
}
return out;
}
// TypeDescription =================================================================================
/// Convert a runtime type description struct to its corresponding message.
/**
* This function converts a rosidl_runtime_cpp::type_description::TypeDescription
* struct to the corresponding type_description_interfaces/msg/TypeDescription
* msg.
*
* \param[in] runtime_description the runtime struct to convert
* \return the converted message
*/
template<
typename ToAllocatorT = std::allocator<void>,
typename FromAllocatorT
>
type_description_interfaces::msg::TypeDescription_<ToAllocatorT>
convert_type_description_runtime_to_msg(
const rosidl_runtime_cpp::type_description::TypeDescription_<FromAllocatorT> & runtime_description,
const ToAllocatorT & alloc = ToAllocatorT())
{
type_description_interfaces::msg::TypeDescription_<ToAllocatorT> out(alloc);
out.type_description =
convert_individual_type_description_runtime_to_msg(runtime_description.type_description, alloc);
for (const auto & referenced_type_description :
runtime_description.referenced_type_descriptions)
{
out.referenced_type_descriptions.push_back(convert_individual_type_description_runtime_to_msg(
referenced_type_description, alloc));
}
return out;
}
/// Convert a type description message to its corresponding runtime struct.
/**
* This function converts a type_description_interfaces/msg/TypeDescription msg
* to the corresponding rosidl_runtime_cpp::type_description::TypeDescription
* struct.
*
* \param[in] description_msg the message to convert
* \return the converted runtime struct
*/
template<
typename ToAllocatorT = std::allocator<void>,
typename FromAllocatorT
>
rosidl_runtime_cpp::type_description::TypeDescription_<ToAllocatorT>
convert_type_description_msg_to_runtime(
const type_description_interfaces::msg::TypeDescription_<FromAllocatorT> & description_msg,
const ToAllocatorT & alloc = ToAllocatorT())
{
rosidl_runtime_cpp::type_description::TypeDescription_<ToAllocatorT> out(alloc);
out.type_description =
convert_individual_type_description_msg_to_runtime(description_msg.type_description, alloc);
for (const auto & referenced_type_description : description_msg.referenced_type_descriptions) {
out.referenced_type_descriptions.push_back(convert_individual_type_description_msg_to_runtime(
referenced_type_description, alloc));
}
return out;
}
// TypeSource ======================================================================================
/// Convert a runtime type source struct to its corresponding message.
/**
* This function converts a rosidl_runtime_cpp::type_description::TypeSource
* struct to the corresponding type_description_interfaces/msg/TypeSource msg.
*
* \param[in] runtime_type_source the runtime struct to convert
* \return the converted message
*/
template<
typename ToAllocatorT = std::allocator<void>,
typename FromAllocatorT
>
type_description_interfaces::msg::TypeSource_<ToAllocatorT>
convert_type_source_sequence_runtime_to_msg(
const rosidl_runtime_cpp::type_description::TypeSource_<FromAllocatorT> & runtime_type_source,
const ToAllocatorT & alloc = ToAllocatorT())
{
type_description_interfaces::msg::TypeSource_<ToAllocatorT> out(alloc);
out.type_name = runtime_type_source.type_name;
out.encoding = runtime_type_source.encoding;
out.raw_file_contents = runtime_type_source.raw_file_contents;
return out;
}
/// Convert a type source message to its corresponding runtime struct.
/**
* This function converts a type_description_interfaces/msg/TypeSource msg to
* the corresponding rosidl_runtime_cpp::type_description::TypeSource struct.
*
* \param[in] type_source_msg the message to convert
* \return the converted runtime struct
*/
template<
typename ToAllocatorT = std::allocator<void>,
typename FromAllocatorT
>
rosidl_runtime_cpp::type_description::TypeSource_<ToAllocatorT>
convert_type_source_sequence_msg_to_runtime(
const type_description_interfaces::msg::TypeSource_<FromAllocatorT> & type_source_msg,
const ToAllocatorT & alloc = ToAllocatorT())
{
rosidl_runtime_cpp::type_description::TypeSource_<ToAllocatorT> out(alloc);
out.type_name = type_source_msg.type_name;
out.encoding = type_source_msg.encoding;
out.raw_file_contents = type_source_msg.raw_file_contents;
return out;
}
} // namespace dynamic_typesupport
} // namespace rclcpp
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__TYPE_DESCRIPTION_CONVERSIONS_HPP_

View File

@@ -1430,7 +1430,7 @@ public:
rclcpp::Clock::ConstSharedPtr
get_clock() const;
/// Returns current time from the time source specified by clock_type.
/// Returns current time from the node clock.
/**
* \sa rclcpp::Clock::now
*/

View File

@@ -146,8 +146,7 @@ public:
const rclcpp::QoS & qos,
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
{
// Topic is unused for now.
(void)topic;
(void)qos;
(void)options;
// If needed, setup intra process communication.
@@ -155,22 +154,26 @@ public:
auto context = node_base->get_context();
// Get the intra process manager instance for this context.
auto ipm = context->get_sub_context<rclcpp::experimental::IntraProcessManager>();
// Register the publisher with the intra process manager.
if (qos.history() != rclcpp::HistoryPolicy::KeepLast) {
// Check if the QoS is compatible with intra-process.
auto qos_profile = get_actual_qos();
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
throw std::invalid_argument(
"intraprocess communication allowed only with keep last history qos policy");
"intraprocess communication on topic '" + topic +
"' allowed only with keep last history qos policy");
}
if (qos.depth() == 0) {
if (qos_profile.depth() == 0) {
throw std::invalid_argument(
"intraprocess communication is not allowed with a zero qos history depth value");
"intraprocess communication on topic '" + topic +
"' is not allowed with a zero qos history depth value");
}
if (qos.durability() == rclcpp::DurabilityPolicy::TransientLocal) {
if (qos_profile.durability() == rclcpp::DurabilityPolicy::TransientLocal) {
buffer_ = rclcpp::experimental::create_intra_process_buffer<
ROSMessageType, ROSMessageTypeAllocator, ROSMessageTypeDeleter>(
rclcpp::detail::resolve_intra_process_buffer_type(options_.intra_process_buffer_type),
qos,
qos_profile,
std::make_shared<ROSMessageTypeAllocator>(ros_message_type_allocator_));
}
// Register the publisher with the intra process manager.
uint64_t intra_process_publisher_id = ipm->add_publisher(this->shared_from_this(), buffer_);
this->setup_intra_process(
intra_process_publisher_id,
@@ -232,8 +235,12 @@ public:
// interprocess publish, resulting in lower publish-to-subscribe latency.
// It's not possible to do that with an unique_ptr,
// as do_intra_process_publish takes the ownership of the message.
// When durability is set to TransientLocal (i.e. there is a buffer),
// inter process publish should always take place to ensure
// late joiners receive past data.
bool inter_process_publish_needed =
get_subscription_count() > get_intra_process_subscription_count();
get_subscription_count() > get_intra_process_subscription_count() || buffer_;
if (inter_process_publish_needed) {
auto shared_msg =
@@ -310,8 +317,11 @@ public:
return;
}
// When durability is set to TransientLocal (i.e. there is a buffer),
// inter process publish should always take place to ensure
// late joiners receive past data.
bool inter_process_publish_needed =
get_subscription_count() > get_intra_process_subscription_count();
get_subscription_count() > get_intra_process_subscription_count() || buffer_;
if (inter_process_publish_needed) {
auto ros_msg_ptr = std::make_shared<ROSMessageType>();

View File

@@ -147,11 +147,13 @@ public:
auto qos_profile = get_actual_qos();
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
throw std::invalid_argument(
"intraprocess communication allowed only with keep last history qos policy");
"intraprocess communication on topic '" + topic_name +
"' allowed only with keep last history qos policy");
}
if (qos_profile.depth() == 0) {
throw std::invalid_argument(
"intraprocess communication is not allowed with 0 depth qos policy");
"intraprocess communication on topic '" + topic_name +
"' is not allowed with 0 depth qos policy");
}
using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp</name>
<version>29.2.0</version>
<version>29.3.0</version>
<description>The ROS client library in C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -729,13 +729,33 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
// Clear any previous wait result
this->wait_result_.reset();
// we need to make sure that callback groups don't get out of scope
// during the wait. As in jazzy, they are not covered by the DynamicStorage,
// we explicitly hold them here as a bugfix
std::vector<rclcpp::CallbackGroup::SharedPtr> cbgs;
{
std::lock_guard<std::mutex> guard(mutex_);
if (this->entities_need_rebuild_.exchange(false) || current_collection_.empty()) {
this->collect_entities();
}
auto callback_groups = this->collector_.get_all_callback_groups();
cbgs.resize(callback_groups.size());
for(const auto & w_ptr : callback_groups) {
auto shr_ptr = w_ptr.lock();
if(shr_ptr) {
cbgs.push_back(std::move(shr_ptr));
}
}
}
this->wait_result_.emplace(wait_set_.wait(timeout));
// drop references to the callback groups, before trying to execute anything
cbgs.clear();
if (!this->wait_result_ || this->wait_result_->kind() == WaitResultKind::Empty) {
RCUTILS_LOG_WARN_NAMED(
"rclcpp",

View File

@@ -110,10 +110,29 @@ StaticSingleThreadedExecutor::spin_once_impl(std::chrono::nanoseconds timeout)
std::optional<rclcpp::WaitResult<rclcpp::WaitSet>>
StaticSingleThreadedExecutor::collect_and_wait(std::chrono::nanoseconds timeout)
{
// we need to make sure that callback groups don't get out of scope
// during the wait. As in jazzy, they are not covered by the DynamicStorage,
// we explicitly hold them here as a bugfix
std::vector<rclcpp::CallbackGroup::SharedPtr> cbgs;
if (this->entities_need_rebuild_.exchange(false) || current_collection_.empty()) {
this->collect_entities();
}
auto callback_groups = this->collector_.get_all_callback_groups();
cbgs.resize(callback_groups.size());
for(const auto & w_ptr : callback_groups) {
auto shr_ptr = w_ptr.lock();
if(shr_ptr) {
cbgs.push_back(std::move(shr_ptr));
}
}
auto wait_result = wait_set_.wait(std::chrono::nanoseconds(timeout));
// drop references to the callback groups, before trying to execute anything
cbgs.clear();
if (wait_result.kind() == WaitResultKind::Empty) {
RCUTILS_LOG_WARN_NAMED(
"rclcpp",

View File

@@ -28,6 +28,13 @@ ament_add_gtest(
if(TARGET test_allocator_deleter)
target_link_libraries(test_allocator_deleter ${PROJECT_NAME})
endif()
ament_add_gtest(test_type_description_conversions
dynamic_typesupport/test_type_description_conversions.cpp)
if(TARGET test_type_description_conversions)
target_link_libraries(test_type_description_conversions ${PROJECT_NAME} ${rcl_interfaces_TARGETS})
endif()
ament_add_gtest(
test_exceptions
exceptions/test_exceptions.cpp)

View File

@@ -0,0 +1,118 @@
// Copyright 2025 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 <gtest/gtest.h>
#include "rclcpp/dynamic_typesupport/type_description_conversions.hpp"
#include "rosidl_runtime_cpp/type_description/individual_type_description__struct.hpp"
#include "rosidl_runtime_cpp/type_description/type_description__struct.hpp"
#include "rosidl_runtime_cpp/type_description/type_source__struct.hpp"
#include "type_description_interfaces/msg/individual_type_description.hpp"
#include "type_description_interfaces/msg/type_description.hpp"
#include "type_description_interfaces/msg/type_source.hpp"
using rclcpp::dynamic_typesupport::
convert_individual_type_description_msg_to_runtime;
using rclcpp::dynamic_typesupport::
convert_individual_type_description_runtime_to_msg;
using rclcpp::dynamic_typesupport::convert_type_description_msg_to_runtime;
using rclcpp::dynamic_typesupport::convert_type_description_runtime_to_msg;
using rclcpp::dynamic_typesupport::convert_type_source_sequence_msg_to_runtime;
using rclcpp::dynamic_typesupport::convert_type_source_sequence_runtime_to_msg;
TEST(TestTypeDescriptionConversions, individual_type_description_roundtrip)
{
rosidl_runtime_cpp::type_description::IndividualTypeDescription original;
original.type_name = "test_type_name";
original.fields.emplace_back();
original.fields.back().name = "field1";
original.fields.back().type.type_id = 1;
original.fields.emplace_back();
original.fields.back().name = "field2";
original.fields.back().type.type_id = 2;
type_description_interfaces::msg::IndividualTypeDescription msg =
convert_individual_type_description_runtime_to_msg(original);
rosidl_runtime_cpp::type_description::IndividualTypeDescription converted_back
=
convert_individual_type_description_msg_to_runtime(msg);
EXPECT_EQ(original.type_name, converted_back.type_name);
ASSERT_EQ(original.fields.size(), converted_back.fields.size());
for (size_t i = 0; i < original.fields.size(); ++i) {
EXPECT_EQ(original.fields[i].name, converted_back.fields[i].name);
EXPECT_EQ(original.fields[i].type, converted_back.fields[i].type);
}
}
TEST(TestTypeDescriptionConversions, type_description_roundtrip)
{
rosidl_runtime_cpp::type_description::TypeDescription original;
original.type_description.type_name = "main_type";
original.type_description.fields.emplace_back();
original.type_description.fields.back().name = "field1";
original.type_description.fields.back().type.type_id = 1;
original.type_description.fields.emplace_back();
original.type_description.fields.back().name = "field2";
original.type_description.fields.back().type.type_id = 2;
rosidl_runtime_cpp::type_description::IndividualTypeDescription ref1;
ref1.type_name = "ref_type_1";
ref1.fields.emplace_back();
ref1.fields.back().name = "ref_field1";
ref1.fields.back().type.type_id = 1;
rosidl_runtime_cpp::type_description::IndividualTypeDescription ref2;
ref2.type_name = "ref_type_2";
ref2.fields.emplace_back();
ref2.fields.back().name = "ref_field2";
ref2.fields.back().type.type_id = 2;
original.referenced_type_descriptions.push_back(ref1);
original.referenced_type_descriptions.push_back(ref2);
type_description_interfaces::msg::TypeDescription msg =
convert_type_description_runtime_to_msg(original);
rosidl_runtime_cpp::type_description::TypeDescription converted_back =
convert_type_description_msg_to_runtime(msg);
EXPECT_EQ(original.type_description.type_name,
converted_back.type_description.type_name);
ASSERT_EQ(original.referenced_type_descriptions.size(),
converted_back.referenced_type_descriptions.size());
for (size_t i = 0; i < original.referenced_type_descriptions.size(); ++i) {
EXPECT_EQ(original.referenced_type_descriptions[i].type_name,
converted_back.referenced_type_descriptions[i].type_name);
}
}
TEST(TestTypeSourceConversions, type_source_roundtrip)
{
rosidl_runtime_cpp::type_description::TypeSource original;
original.type_name = "test_type_name";
original.encoding = "test_encoding";
original.raw_file_contents = "test_contents";
type_description_interfaces::msg::TypeSource msg =
convert_type_source_sequence_runtime_to_msg(original);
rosidl_runtime_cpp::type_description::TypeSource converted_back =
convert_type_source_sequence_msg_to_runtime(msg);
EXPECT_EQ(original.type_name, converted_back.type_name);
EXPECT_EQ(original.encoding, converted_back.encoding);
EXPECT_EQ(original.raw_file_contents, converted_back.raw_file_contents);
}

View File

@@ -388,7 +388,7 @@ to_nanoseconds_helper(DurationT duration)
// - works nominally (it can execute entities)
// - it can execute multiple items at once
// - it does not wait for work to be available before returning
TYPED_TEST(TestExecutors, spin_some)
TYPED_TEST(TestExecutors, spinSome)
{
using ExecutorType = TypeParam;
@@ -484,7 +484,7 @@ TYPED_TEST(TestExecutors, spin_some)
// do not properly implement max_duration (it seems), so disable this test
// for them in the meantime.
// see: https://github.com/ros2/rclcpp/issues/2462
TYPED_TEST(TestExecutorsStable, spin_some_max_duration)
TYPED_TEST(TestExecutorsStable, spinSomeMaxDuration)
{
using ExecutorType = TypeParam;
@@ -714,6 +714,13 @@ TYPED_TEST(TestExecutors, notifyTwiceWhileSpinning)
sub1_msg_count++;
});
// Wait for the subscription to be matched
size_t tries = 10000;
while (this->publisher->get_subscription_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(this->publisher->get_subscription_count(), 2);
// Publish a message and verify it's received
this->publisher->publish(test_msgs::msg::Empty());
auto start = std::chrono::steady_clock::now();
@@ -731,6 +738,13 @@ TYPED_TEST(TestExecutors, notifyTwiceWhileSpinning)
sub2_msg_count++;
});
// Wait for the subscription to be matched
tries = 10000;
while (this->publisher->get_subscription_count() < 3 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(this->publisher->get_subscription_count(), 3);
// Publish a message and verify it's received by both subscriptions
this->publisher->publish(test_msgs::msg::Empty());
start = std::chrono::steady_clock::now();
@@ -820,7 +834,7 @@ TEST(TestExecutors, testSpinWithNonDefaultContext)
rclcpp::shutdown(non_default_context);
}
TYPED_TEST(TestExecutors, release_ownership_entity_after_spinning_cancel)
TYPED_TEST(TestExecutors, releaseOwnershipEntityAfterSpinningCancel)
{
using ExecutorType = TypeParam;
ExecutorType executor;
@@ -843,3 +857,36 @@ TYPED_TEST(TestExecutors, release_ownership_entity_after_spinning_cancel)
EXPECT_EQ(server.use_count(), 1);
}
TYPED_TEST(TestExecutors, testRaceDropCallbackGroupFromSecondThread)
{
using ExecutorType = TypeParam;
// Create an executor
ExecutorType executor;
executor.add_node(this->node);
// Start spinning
auto executor_thread = std::thread(
[&executor]() {
executor.spin();
});
// As the problem is a race, we do this multiple times,
// to raise our chances of hitting the problem
for (size_t i = 0; i < 10; i++) {
auto cg = this->node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
auto timer = this->node->create_timer(1s, [] {}, cg);
// sleep a bit, so that the spin thread can pick up the callback group
// and add it to the executor
std::this_thread::sleep_for(5ms);
// At this point the callbackgroup should be used within the waitset of the executor
// as we leave the scope, the reference to cg will be dropped.
// If the executor has a race, we will experience a segfault at this point.
}
executor.cancel();
executor_thread.join();
}

View File

@@ -431,7 +431,7 @@ TYPED_TEST(TestAllClientTypesWithServer, client_qos)
rclcpp::ServicesQoS qos_profile;
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
rclcpp::Duration duration(std::chrono::nanoseconds(1));
rclcpp::Duration duration(std::chrono::milliseconds(1));
qos_profile.deadline(duration);
qos_profile.lifespan(duration);
qos_profile.liveliness_lease_duration(duration);

View File

@@ -92,3 +92,19 @@ TEST_F(TestCreateSubscription, create_with_statistics) {
ASSERT_NE(nullptr, subscription);
EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name());
}
TEST_F(TestCreateSubscription, create_with_intra_process_com) {
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto options = rclcpp::SubscriptionOptions();
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
ASSERT_NO_THROW(
{
subscription = rclcpp::create_subscription<test_msgs::msg::Empty>(
node, "topic_name", rclcpp::SystemDefaultsQoS(), callback, options);
});
ASSERT_NE(nullptr, subscription);
EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name());
}

View File

@@ -320,7 +320,7 @@ TEST_F(TestGenericService, rcl_service_request_subscription_get_actual_qos_error
TEST_F(TestGenericService, generic_service_qos) {
rclcpp::ServicesQoS qos_profile;
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
rclcpp::Duration duration(std::chrono::nanoseconds(1));
rclcpp::Duration duration(std::chrono::milliseconds(1));
qos_profile.deadline(duration);
qos_profile.lifespan(duration);
qos_profile.liveliness_lease_duration(duration);

View File

@@ -14,6 +14,7 @@
#include <gtest/gtest.h>
#include <future>
#include <chrono>
#include <memory>
#include <string>
@@ -187,6 +188,21 @@ TEST_F(TestPublisher, various_creation_signatures) {
}
}
/*
Testing publisher with intraprocess enabled and SystemDefaultQoS
*/
TEST_F(TestPublisher, test_publisher_with_system_default_qos) {
initialize(rclcpp::NodeOptions().use_intra_process_comms(false));
// explicitly enable intra-process comm with publisher option
auto options = rclcpp::PublisherOptions();
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
using test_msgs::msg::Empty;
ASSERT_NO_THROW(
{
auto publisher = node->create_publisher<Empty>("topic", rclcpp::SystemDefaultsQoS());
});
}
/*
Testing publisher with intraprocess enabled and invalid QoS
*/
@@ -432,11 +448,10 @@ TEST_F(TestPublisher, intra_process_publish_failures) {
publisher->get_publisher_handle().get(), msg.get()));
}
}
RCLCPP_EXPECT_THROW_EQ(
node->create_publisher<test_msgs::msg::Empty>(
"topic", rclcpp::QoS(0), options),
std::invalid_argument(
"intraprocess communication is not allowed with a zero qos history depth value"));
// a zero depth with KEEP_LAST doesn't make sense,
// this will be interpreted as SystemDefaultQoS by rclcpp.
EXPECT_NO_THROW(
node->create_publisher<test_msgs::msg::Empty>("topic", rclcpp::QoS(0), options));
}
TEST_F(TestPublisher, inter_process_publish_failures) {
@@ -711,11 +726,7 @@ TEST_F(TestPublisher, intra_process_transient_local) {
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
struct IntraProcessCallback
{
void callback_fun(size_t s)
{
(void) s;
called = true;
}
void callback_fun(size_t) {called = true;}
bool called = false;
};
rclcpp::SubscriptionOptions sub_options_ipm_disabled;
@@ -772,3 +783,44 @@ TEST_F(TestPublisher, intra_process_transient_local) {
EXPECT_FALSE(callback3.called);
EXPECT_FALSE(callback4.called);
}
TEST_F(TestPublisher, intra_process_inter_process_mix_transient_local) {
constexpr auto history_depth = 10u;
initialize(rclcpp::NodeOptions().use_intra_process_comms(true));
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> pub_options_ipm_enabled;
pub_options_ipm_enabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
auto pub_ipm_enabled_transient_local_enabled = node->create_publisher<test_msgs::msg::Empty>(
"topic1",
rclcpp::QoS(rclcpp::KeepLast(history_depth)).transient_local(), pub_options_ipm_enabled);
test_msgs::msg::Empty msg;
pub_ipm_enabled_transient_local_enabled->publish(msg);
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
struct SubscriptionCallback
{
void callback_fun(size_t) {called.set_value();}
std::promise<void> called;
};
rclcpp::SubscriptionOptions sub_options_ipm_disabled;
sub_options_ipm_disabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
SubscriptionCallback intra_callback, inter_callback;
std::future<void> intra_callback_future = intra_callback.called.get_future(),
inter_callback_future = inter_callback.called.get_future();
auto sub_ipm_disabled_transient_local_enabled = node->create_subscription<test_msgs::msg::Empty>(
"topic1",
rclcpp::QoS(rclcpp::KeepLast(history_depth)).transient_local(),
do_nothing, sub_options_ipm_disabled);
sub_ipm_disabled_transient_local_enabled->set_on_new_intra_process_message_callback(
std::bind(&SubscriptionCallback::callback_fun, &intra_callback, std::placeholders::_1));
sub_ipm_disabled_transient_local_enabled->set_on_new_message_callback(
std::bind(&SubscriptionCallback::callback_fun, &inter_callback, std::placeholders::_1));
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
EXPECT_EQ(executor.spin_until_future_complete(inter_callback_future,
std::chrono::milliseconds(100)), rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(executor.spin_until_future_complete(intra_callback_future,
std::chrono::milliseconds(100)), rclcpp::FutureReturnCode::TIMEOUT);
}

View File

@@ -335,7 +335,7 @@ TEST_F(TestService, rcl_service_request_subscription_get_actual_qos_error) {
TEST_F(TestService, server_qos) {
rclcpp::ServicesQoS qos_profile;
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
rclcpp::Duration duration(std::chrono::nanoseconds(1));
rclcpp::Duration duration(std::chrono::milliseconds(1));
qos_profile.deadline(duration);
qos_profile.lifespan(duration);
qos_profile.liveliness_lease_duration(duration);

View File

@@ -92,9 +92,16 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
request->set__int64_value(42);
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
// Wait for the introspection to attach to our subscription
size_t tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
auto future = client->async_send_request(request);
ASSERT_EQ(
@@ -163,9 +170,11 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
{
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
ASSERT_EQ(sub->get_publisher_count(), 0);
auto request = std::make_shared<BasicTypes::Request>();
request->set__bool_value(true);
@@ -183,9 +192,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
// Wait for the introspection to attach to our subscription
size_t tries = 1000;
while (this->sub->get_publisher_count() < 1 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 1u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -200,9 +216,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 1 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 1u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -217,9 +240,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -235,9 +265,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_content)
{
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
size_t tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
auto request = std::make_shared<BasicTypes::Request>();
request->set__bool_value(true);
@@ -259,9 +296,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -292,9 +336,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -325,9 +376,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(

View File

@@ -212,6 +212,9 @@ TEST_F(TestTimeSource, ROS_time_valid_attach_detach) {
ts.attachNode(node);
EXPECT_FALSE(ros_clock->ros_time_is_active());
ts.attachClock(ros_clock);
EXPECT_FALSE(ros_clock->ros_time_is_active());
ts.detachClock(ros_clock);
EXPECT_FALSE(ros_clock->ros_time_is_active());
}

View File

@@ -3,6 +3,11 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29.3.0 (2024-12-20)
-------------------
* Make ament_cmake a buildtool dependency (`#2689 <https://github.com/ros2/rclcpp/issues/2689>`_)
* Contributors: Nathan Wiebe Neufeldt
29.2.0 (2024-11-25)
-------------------

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp_action</name>
<version>29.2.0</version>
<version>29.3.0</version>
<description>Adds action APIs for C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
@@ -26,8 +26,6 @@
<depend>rcl</depend>
<depend>rcpputils</depend>
<depend>ament_cmake</depend>
<test_depend>ament_cmake_gtest</test_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

View File

@@ -2,6 +2,11 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29.3.0 (2024-12-20)
-------------------
* Add parsing for rest of obvious boolean extra arguments and throw for unsupported ones (`#2685 <https://github.com/ros2/rclcpp/issues/2685>`_)
* Contributors: rcp1
29.2.0 (2024-11-25)
-------------------

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp_components</name>
<version>29.2.0</version>
<version>29.3.0</version>
<description>Package containing tools for dynamically loadable components</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -169,24 +169,63 @@ ComponentManager::create_node_options(const std::shared_ptr<LoadNode::Request> r
for (const auto & a : request->extra_arguments) {
const rclcpp::Parameter extra_argument = rclcpp::Parameter::from_parameter_msg(a);
if (extra_argument.get_name() == "use_intra_process_comms") {
if (extra_argument.get_name() == "forward_global_arguments") {
if (extra_argument.get_type() != rclcpp::ParameterType::PARAMETER_BOOL) {
throw ComponentManagerException(
"Extra component argument 'use_intra_process_comms' must be a boolean");
}
options.use_intra_process_comms(extra_argument.get_value<bool>());
} else if (extra_argument.get_name() == "forward_global_arguments") {
if (extra_argument.get_type() != rclcpp::ParameterType::PARAMETER_BOOL) {
throw ComponentManagerException(
"Extra component argument 'forward_global_arguments' must be a boolean");
"Extra component argument 'forward_global_arguments' must be a boolean");
}
options.use_global_arguments(extra_argument.get_value<bool>());
if (extra_argument.get_value<bool>()) {
RCLCPP_WARN(
get_logger(), "forward_global_arguments is true by default in nodes, but is not "
"recommended in a component manager. If true, this will cause this node's behavior "
"to be influenced by global arguments, not only those targeted at this node.");
get_logger(), "forward_global_arguments is true by default in nodes, but is not "
"recommended in a component manager. If true, this will cause this node's behavior "
"to be influenced by global arguments, not only those targeted at this node.");
}
} else if (extra_argument.get_name() == "enable_rosout") {
if (extra_argument.get_type() != rclcpp::ParameterType::PARAMETER_BOOL) {
throw ComponentManagerException(
"Extra component argument 'enable_rosout' must be a boolean");
}
options.enable_rosout(extra_argument.get_value<bool>());
} else if (extra_argument.get_name() == "use_intra_process_comms") {
if (extra_argument.get_type() != rclcpp::ParameterType::PARAMETER_BOOL) {
throw ComponentManagerException(
"Extra component argument 'use_intra_process_comms' must be a boolean");
}
options.use_intra_process_comms(extra_argument.get_value<bool>());
} else if (extra_argument.get_name() == "enable_topic_statistics") {
if (extra_argument.get_type() != rclcpp::ParameterType::PARAMETER_BOOL) {
throw ComponentManagerException(
"Extra component argument 'enable_topic_statistics' must be a boolean");
}
options.enable_topic_statistics(extra_argument.get_value<bool>());
} else if (extra_argument.get_name() == "start_parameter_services") {
if (extra_argument.get_type() != rclcpp::ParameterType::PARAMETER_BOOL) {
throw ComponentManagerException(
"Extra component argument 'start_parameter_services' must be a boolean");
}
options.start_parameter_services(extra_argument.get_value<bool>());
} else if (extra_argument.get_name() == "start_parameter_event_publisher") {
if (extra_argument.get_type() != rclcpp::ParameterType::PARAMETER_BOOL) {
throw ComponentManagerException(
"Extra component argument 'start_parameter_event_publisher' must be a boolean");
}
options.start_parameter_event_publisher(extra_argument.get_value<bool>());
} else if (extra_argument.get_name() == "use_clock_thread") {
if (extra_argument.get_type() != rclcpp::ParameterType::PARAMETER_BOOL) {
throw ComponentManagerException(
"Extra component argument 'use_clock_thread' must be a boolean");
}
options.use_clock_thread(extra_argument.get_value<bool>());
} else if (extra_argument.get_name() == "enable_logger_service") {
if (extra_argument.get_type() != rclcpp::ParameterType::PARAMETER_BOOL) {
throw ComponentManagerException(
"Extra component argument 'enable_logger_service' must be a boolean");
}
options.enable_logger_service(extra_argument.get_value<bool>());
} else {
throw ComponentManagerException("Extra component argument '" + extra_argument.get_name() +
"' is not supported");
}
}

View File

@@ -181,90 +181,74 @@ void test_components_api(bool use_dedicated_executor)
}
{
// use_intra_process_comms
// invalid extra argument
auto request = std::make_shared<composition_interfaces::srv::LoadNode::Request>();
request->package_name = "rclcpp_components";
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
request->node_name = "test_component_intra_process";
rclcpp::Parameter use_intraprocess_comms("use_intra_process_comms",
rclcpp::ParameterValue(true));
request->extra_arguments.push_back(use_intraprocess_comms.to_parameter_msg());
request->node_name = "test_component_allow_undeclared_parameters";
rclcpp::Parameter extra_argument("allow_undeclared_parameters", rclcpp::ParameterValue(true));
request->extra_arguments.push_back(extra_argument.to_parameter_msg());
auto future = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto result = future.get();
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result->success, true);
EXPECT_EQ(result->error_message, "");
std::cout << result->full_node_name << std::endl;
EXPECT_EQ(result->full_node_name, "/test_component_intra_process");
EXPECT_EQ(result->unique_id, 6u);
}
{
// use_intra_process_comms is not a bool type parameter
auto request = std::make_shared<composition_interfaces::srv::LoadNode::Request>();
request->package_name = "rclcpp_components";
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
request->node_name = "test_component_intra_process_str";
rclcpp::Parameter use_intraprocess_comms("use_intra_process_comms",
rclcpp::ParameterValue("hello"));
request->extra_arguments.push_back(use_intraprocess_comms.to_parameter_msg());
auto future = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto result = future.get();
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result->success, false);
EXPECT_EQ(
result->error_message,
"Extra component argument 'use_intra_process_comms' must be a boolean");
EXPECT_EQ(result->error_message,
"Extra component argument 'allow_undeclared_parameters' is not supported");
EXPECT_EQ(result->full_node_name, "");
EXPECT_EQ(result->unique_id, 0u);
}
{
// forward_global_arguments
std::array<std::string, 8u> valid_extra_arguments = {
"forward_global_arguments",
"use_intra_process_comms",
"enable_rosout",
"enable_topic_statistics",
"start_parameter_services",
"start_parameter_event_publisher",
"use_clock_thread",
"enable_logger_service"
};
for (size_t i = 0; i < valid_extra_arguments.size(); ++i) {
const auto & arg = valid_extra_arguments[i];
auto request = std::make_shared<composition_interfaces::srv::LoadNode::Request>();
request->package_name = "rclcpp_components";
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
request->node_name = "test_component_global_arguments";
rclcpp::Parameter forward_global_arguments("forward_global_arguments",
rclcpp::ParameterValue(true));
request->extra_arguments.push_back(forward_global_arguments.to_parameter_msg());
{
// success
request->node_name = "test_component_" + arg;
rclcpp::Parameter extra_argument(arg, rclcpp::ParameterValue(true));
request->extra_arguments.push_back(extra_argument.to_parameter_msg());
auto future = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto result = future.get();
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result->success, true);
EXPECT_EQ(result->error_message, "");
EXPECT_EQ(result->full_node_name, "/test_component_global_arguments");
EXPECT_EQ(result->unique_id, 7u);
}
auto future = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto result = future.get();
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result->success, true);
EXPECT_EQ(result->error_message, "");
EXPECT_EQ(result->full_node_name, "/test_component_" + arg);
EXPECT_EQ(result->unique_id, 6u + i);
}
{
// forward_global_arguments is not a bool type parameter
auto request = std::make_shared<composition_interfaces::srv::LoadNode::Request>();
request->package_name = "rclcpp_components";
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
request->node_name = "test_component_global_arguments_str";
{
// not a bool type parameter
request->node_name = "test_component_" + arg + "_str";
rclcpp::Parameter extra_argument(arg, rclcpp::ParameterValue("hello"));
request->extra_arguments.clear();
request->extra_arguments.push_back(extra_argument.to_parameter_msg());
rclcpp::Parameter forward_global_arguments("forward_global_arguments",
rclcpp::ParameterValue("hello"));
request->extra_arguments.push_back(forward_global_arguments.to_parameter_msg());
auto future = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto result = future.get();
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result->success, false);
EXPECT_EQ(
result->error_message,
"Extra component argument 'forward_global_arguments' must be a boolean");
EXPECT_EQ(result->full_node_name, "");
EXPECT_EQ(result->unique_id, 0u);
auto future = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(future, 5s); // Wait for the result.
auto result = future.get();
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result->success, false);
EXPECT_EQ(result->error_message,
"Extra component argument '" + arg + "' must be a boolean");
EXPECT_EQ(result->full_node_name, "");
EXPECT_EQ(result->unique_id, 0u);
}
}
auto node_names = node->get_node_names();
@@ -295,22 +279,19 @@ void test_components_api(bool use_dedicated_executor)
auto result_node_names = result->full_node_names;
auto result_unique_ids = result->unique_ids;
EXPECT_EQ(result_node_names.size(), 7u);
EXPECT_EQ(result_node_names.size(), 5u + valid_extra_arguments.size());
EXPECT_EQ(result_node_names[0], "/test_component_foo");
EXPECT_EQ(result_node_names[1], "/test_component_bar");
EXPECT_EQ(result_node_names[2], "/test_component_baz");
EXPECT_EQ(result_node_names[3], "/ns/test_component_bing");
EXPECT_EQ(result_node_names[4], "/test_component_remap");
EXPECT_EQ(result_node_names[5], "/test_component_intra_process");
EXPECT_EQ(result_node_names[6], "/test_component_global_arguments");
EXPECT_EQ(result_unique_ids.size(), 7u);
EXPECT_EQ(result_unique_ids[0], 1u);
EXPECT_EQ(result_unique_ids[1], 2u);
EXPECT_EQ(result_unique_ids[2], 3u);
EXPECT_EQ(result_unique_ids[3], 4u);
EXPECT_EQ(result_unique_ids[4], 5u);
EXPECT_EQ(result_unique_ids[5], 6u);
EXPECT_EQ(result_unique_ids[6], 7u);
for (size_t i = 0u; i < valid_extra_arguments.size(); ++i) {
EXPECT_EQ(result_node_names[5u + i], "/test_component_" + valid_extra_arguments[i]);
}
EXPECT_EQ(result_unique_ids.size(), 5u + valid_extra_arguments.size());
for (size_t i = 0u; i < result_unique_ids.size(); ++i) {
EXPECT_EQ(result_unique_ids[i], 1u + i);
}
}
}
@@ -364,20 +345,18 @@ void test_components_api(bool use_dedicated_executor)
auto result_node_names = result->full_node_names;
auto result_unique_ids = result->unique_ids;
EXPECT_EQ(result_node_names.size(), 6u);
EXPECT_EQ(result_node_names.size(), 4u + valid_extra_arguments.size());
EXPECT_EQ(result_node_names[0], "/test_component_bar");
EXPECT_EQ(result_node_names[1], "/test_component_baz");
EXPECT_EQ(result_node_names[2], "/ns/test_component_bing");
EXPECT_EQ(result_node_names[3], "/test_component_remap");
EXPECT_EQ(result_node_names[4], "/test_component_intra_process");
EXPECT_EQ(result_node_names[5], "/test_component_global_arguments");
EXPECT_EQ(result_unique_ids.size(), 6u);
EXPECT_EQ(result_unique_ids[0], 2u);
EXPECT_EQ(result_unique_ids[1], 3u);
EXPECT_EQ(result_unique_ids[2], 4u);
EXPECT_EQ(result_unique_ids[3], 5u);
EXPECT_EQ(result_unique_ids[4], 6u);
EXPECT_EQ(result_unique_ids[5], 7u);
for (size_t i = 0u; i < valid_extra_arguments.size(); ++i) {
EXPECT_EQ(result_node_names[4u + i], "/test_component_" + valid_extra_arguments[i]);
}
EXPECT_EQ(result_unique_ids.size(), 4u + valid_extra_arguments.size());
for (size_t i = 0u; i < result_unique_ids.size(); ++i) {
EXPECT_EQ(result_unique_ids[i], 2u + i);
}
}
}
}

View File

@@ -3,6 +3,11 @@ Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29.3.0 (2024-12-20)
-------------------
* Update docstring for `rclcpp::Node::now()` (`#2696 <https://github.com/ros2/rclcpp/issues/2696>`_)
* Contributors: Patrick Roncagliolo
29.2.0 (2024-11-25)
-------------------

View File

@@ -731,7 +731,7 @@ public:
rclcpp::Clock::ConstSharedPtr
get_clock() const;
/// Returns current time from the time source specified by clock_type.
/// Returns current time from the node clock.
/**
* \sa rclcpp::Clock::now
*/

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp_lifecycle</name>
<version>29.2.0</version>
<version>29.3.0</version>
<description>Package containing a prototype for lifecycle implementation</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>