Compare commits

...

33 Commits
21.0.3 ... iron

Author SHA1 Message Date
Yadunund
33a3545821 21.0.8 2024-11-08 22:04:11 +00:00
Yadunund
6a7eaca0e4 Update changelogs
Signed-off-by: Yadunund <yadunund@gmail.com>
2024-11-08 22:03:51 +00:00
mergify[bot]
21adb0ec1e associated clocks should be protected by mutex. (#2255) (#2258)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
(cherry picked from commit 5d6e5fa766)

Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-11-07 13:39:37 -08:00
Cristóbal Arroyo
1f9e5e8992 Skip client_qos test (#2658)
* Skip client_qos test

Signed-off-by: Crola1702 <cristobal.arroyo@ekumenlabs.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-11-05 15:32:54 -05:00
mergify[bot]
34b3c99bd6 add logger level service to lifecycle node. (#2277) (#2288)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
(cherry picked from commit e7f06398db)

Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-11-05 14:49:06 -05:00
Tomoya Fujita
531b2b1a08 Use the same context for the specified node in rclcpp::spin functions… (#2618)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Signed-off-by: Kotaro Yoshimoto <pythagora.yoshimoto@gmail.com>
2024-09-06 11:24:21 +02:00
Yadunund
2d34e13be2 21.0.7 2024-07-10 22:41:12 -07:00
Yadunund
627d91bef4 Update changelogs
Signed-off-by: Yadunund <yadunund@gmail.com>
2024-07-10 22:41:04 -07:00
jmachowinski
d588ccb562 fix: Fixed race condition in action server between is_ready and take"… (#2531)
* fix: Fixed race condition in action server between is_ready and take" (#2495)

Some background information: is_ready, take_data and execute data
may be called from different threads in any order. The code in the old
state expected them to be called in series, without interruption.
This lead to multiple race conditions, as the state of the pimpl objects
was altered by the three functions in a non thread safe way.

Co-authored-by: William Woodall <william@osrfoundation.org>
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>

* fix: added workaround for call to double calls to take_data

This adds a workaround for a known bug in the executor in iron.

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>

---------

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Co-authored-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Co-authored-by: William Woodall <william@osrfoundation.org>
2024-06-27 11:46:10 -07:00
Tomoya Fujita
c1a01fc08d revert call shutdown in LifecycleNode destructor (Iron) (#2559)
* Revert "lifecycle node dtor shutdown should be called only in primary state. (#2543)"

This reverts commit dceb612ef5.

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

* Revert "call shutdown in LifecycleNode dtor to avoid leaving the device in un… (#2450) (#2490)"

This reverts commit f41a353b56.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-06-11 07:41:30 -07:00
mergify[bot]
47adc8f0af Add test creating two content filter topics with the same topic name (#2546) (#2549) (#2550)
Signed-off-by: Mario-DL <mariodominguez@eprosima.com>
Co-authored-by: Mario Domínguez López <116071334+Mario-DL@users.noreply.github.com>
(cherry picked from commit 7c096888ca)

Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
2024-06-06 18:21:25 -07:00
Tomoya Fujita
dceb612ef5 lifecycle node dtor shutdown should be called only in primary state. (#2543)
* lifecycle node dtor shutdown should be called only in primary state.

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

* LifecycleNode shutdown on dtor only with valid context. (#2545)

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-06-06 11:05:00 -07:00
mergify[bot]
753a29b87f rclcpp::shutdown should not be called before LifecycleNode dtor. (#2527) (#2539)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
(cherry picked from commit 22df1d593a)

Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-05-23 22:13:15 -07:00
mergify[bot]
d599f9e63b Do not generate the exception when action service response timeout. (#2464) (#2519)
* Do not generate the exception when action service response timeout.

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

* address review comment.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
(cherry picked from commit 6c7764e968)

Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-05-03 09:32:56 -07:00
mergify[bot]
2e8a23e09e Revise the description of service configure_introspection() (#2511) (#2514)
Signed-off-by: Barry Xu <barry.xu@sony.com>
(cherry picked from commit 55939613a0)

Co-authored-by: Barry Xu <barry.xu@sony.com>
2024-04-23 22:56:37 +02:00
Yadunund
ef85efaca2 21.0.6 2024-04-19 20:19:18 +08:00
Yadunund
a8f047d689 Update changelogs
Signed-off-by: Yadunund <yadunund@openrobotics.org>
2024-04-19 20:19:08 +08:00
mergify[bot]
f41a353b56 call shutdown in LifecycleNode dtor to avoid leaving the device in un… (#2450) (#2490)
* call shutdown in LifecycleNode dtor to avoid leaving the device in unknown state.

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

* add test to verify LifecycleNode::shutdown is called on destructor.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
(cherry picked from commit 04ea0bb002)

Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-04-07 14:58:59 -07:00
mergify[bot]
f80980b431 address ambiguous auto variable. (#2481) (#2486)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Signed-off-by: Steve Nogar <stephen.m.nogar.civ@army.mil>
(cherry picked from commit 3cdb25934e)

Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-04-04 16:59:23 -07:00
Yadunund
7907b2fee0 21.0.5 2024-02-07 17:29:33 +08:00
Yadunund
0dc2756dce Update changelogs
Signed-off-by: Yadunund <yadunund@openrobotics.org>
2024-02-07 17:29:13 +08:00
mergify[bot]
7877358e7b Fix data race in EventHandlerBase (#2349) (#2387)
Both the EventHandler and its associated pubs/subs share
the same underlying rmw event listener.
When a pub/sub is destroyed, the listener is destroyed.
There is a data race when the ~EventHandlerBase wants
to access the listener after it has been destroyed.

The EventHandler stores a shared_ptr of its associated pub/sub.
But since we were clearing the listener event callbacks on the
base class destructor ~EventHandlerBase, the pub/sub was
already destroyed, which means the rmw event listener was also
destroyed, thus causing a segfault when trying to obtain it
to clear the callbacks.

Clearing the callbacks on ~EventHandler instead of
~EventHandlerBase avoids the race, since the pub/sub are still valid.

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
(cherry picked from commit 411dbe8212)

Co-authored-by: mauropasse <mauropasse@hotmail.com>
2023-12-13 10:13:25 -08:00
mergify[bot]
dc6ac4e30f fix(rclcpp_components): increase the service queue sizes in component_container (#2363) (#2381)
* fix(rclcpp_components): increase the service queue sizes in component_container

Signed-off-by: M. Fatih Cırıt <mfc@leodrive.ai>
(cherry picked from commit 9c098e544e)

Co-authored-by: M. Fatih Cırıt <xmfcx@users.noreply.github.com>
2023-12-01 17:04:30 -05:00
Yadunund
da3d2f49b3 21.0.4 2023-11-17 11:27:34 +08:00
Yadunund
a043349ecc Update changelogs
Signed-off-by: Yadunund <yadunund@openrobotics.org>
2023-11-17 11:27:20 +08:00
mergify[bot]
abcdcc4ed7 Disable the loaned messages inside the executor. (#2335) (#2365)
* Disable the loaned messages inside the executor.

They are currently unsafe to use; see the comment in the
commit for more information.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
(cherry picked from commit f294488e17)

Co-authored-by: Chris Lalancette <clalancette@gmail.com>
2023-11-09 14:01:26 -05:00
mergify[bot]
613bcc52ba Add missing 'enable_rosout' comments (#2345) (#2346)
Signed-off-by: Jiaqi Li <ljq0831@qq.com>
(cherry picked from commit fff009a751)

Co-authored-by: Jiaqi Li <ljq0831@qq.com>
2023-10-31 08:17:46 -07:00
mergify[bot]
82a8dba6c3 address rate related flaky tests. (#2329) (#2341)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
(cherry picked from commit fcbe64cff4)

Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-10-26 07:45:40 -07:00
Chris Lalancette
c67720e95c Add missing stdexcept include (#2186) (#2333)
Signed-off-by: Øystein Sture <os@skarvtech.com>
Co-authored-by: Øystein Sture <oysstu@users.noreply.github.com>
2023-10-10 15:26:10 -04:00
mergify[bot]
1ddf865efe Update SignalHandler get_global_signal_handler to avoid complex types in static memory (#2316) (#2322)
* Update SignalHandler get_global_signal_handler to avoid complex types in static memory

This was flagged by msan as a problem.

There's a description of why this is a potential problem here: https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables

Signed-off-by: Tully Foote <tullyfoote@intrinsic.ai>
Co-authored-by: William Woodall <william+github@osrfoundation.org>
(cherry picked from commit 7c1143dc15)
2023-10-04 14:56:06 -07:00
mergify[bot]
6af511f79f Add missing header required by the rclcpp::NodeOptions type (#2324) (#2325)
Signed-off-by: Ignacio Vizzo <ignacio@dexory.com>
(cherry picked from commit d6bd8baac5)

Co-authored-by: Ignacio Vizzo <ignaciovizzo@gmail.com>
2023-10-04 11:15:10 -04:00
mergify[bot]
fbb78ec975 Fix C++20 allocator construct deprecation (#2292) (#2318)
Signed-off-by: Guilherme Rodrigues <guilherme.rodrigues@ait.ac.at>
(cherry picked from commit fa732b9ee8)

Co-authored-by: AiVerisimilitude <133206333+AiVerisimilitude@users.noreply.github.com>
2023-09-27 20:39:53 -04:00
mergify[bot]
b82da1ade4 Topic correct typeadapter deduction (#2294) (#2298)
* fix TypeAdapter deduction

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
(cherry picked from commit 5e152d77d8)

Co-authored-by: Chen Lihui <lihui.chen@sony.com>
2023-09-14 17:19:04 -07:00
35 changed files with 823 additions and 336 deletions

View File

@@ -2,6 +2,40 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21.0.8 (2024-11-08)
-------------------
* associated clocks should be protected by mutex. (`#2258 <https://github.com/ros2/rclcpp/issues/2258>`_)
* Skip client_qos test (`#2658 <https://github.com/ros2/rclcpp/issues/2658>`_)
* Use the same context for the specified node in rclcpp::spin functions. (`#2618 <https://github.com/ros2/rclcpp/issues/2618>`_)
* Contributors: Cristóbal Arroyo, Tomoya Fujita
21.0.7 (2024-07-10)
-------------------
* Add test creating two content filter topics with the same topic name (`#2550 <https://github.com/ros2/rclcpp/issues/2550>`_)
* Revise the description of service configure_introspection() (`#2514 <https://github.com/ros2/rclcpp/issues/2514>`_)
* Contributors: Alejandro Hernández Cordero, Barry Xu
21.0.6 (2024-04-19)
-------------------
* address ambiguous auto variable. (`#2486 <https://github.com/ros2/rclcpp/issues/2486>`_)
* Contributors: Tomoya Fujita
21.0.5 (2024-02-07)
-------------------
* Fix data race in EventHandlerBase (`#2387 <https://github.com/ros2/rclcpp/issues/2387>`_)
* Contributors: mauropasse
21.0.4 (2023-11-17)
-------------------
* Disable the loaned messages inside the executor. (`#2365 <https://github.com/ros2/rclcpp/issues/2365>`_)
* Add missing 'enable_rosout' comments (`#2346 <https://github.com/ros2/rclcpp/issues/2346>`_)
* Address rate related flaky tests. (`#2341 <https://github.com/ros2/rclcpp/issues/2341>`_)
* Add missing stdexcept include (`#2333 <https://github.com/ros2/rclcpp/issues/2333>`_)
* Update SignalHandler get_global_signal_handler to avoid complex types in static memory (`#2322 <https://github.com/ros2/rclcpp/issues/2322>`_)
* Fix C++20 allocator construct deprecation (`#2318 <https://github.com/ros2/rclcpp/issues/2318>`_)
* Topic correct typeadapter deduction (`#2298 <https://github.com/ros2/rclcpp/issues/2298>`_)
* Contributors: AiVerisimilitude, Chen Lihui, Chris Lalancette, Jiaqi Li, Øystein Sture, Tomoya Fujita, William Woodall
21.0.3 (2023-09-08)
-------------------
* Do not crash Executor when send_response fails due to client failure. (`#2279 <https://github.com/ros2/rclcpp/issues/2279>`_)

View File

@@ -845,7 +845,7 @@ protected:
"Received invalid sequence number. Ignoring...");
return std::nullopt;
}
auto value = std::move(it->second.second);
std::optional<CallbackInfoVariant> value = std::move(it->second.second);
this->pending_requests_.erase(request_number);
return value;
}

View File

@@ -26,6 +26,7 @@
#include <unordered_set>
#include <utility>
#include <vector>
#include <stdexcept>
#include "rcl/context.h"
#include "rcl/guard_condition.h"

View File

@@ -260,6 +260,16 @@ public:
}
}
~EventHandler()
{
// Since the rmw event listener holds a reference to the
// "on ready" callback, we need to clear it on destruction of this class.
// This clearing is not needed for other rclcpp entities like pub/subs, since
// they do own the underlying rmw entities, which are destroyed
// on their rclcpp destructors, thus no risk of dangling pointers.
clear_on_ready_callback();
}
/// Take data so that the callback cannot be scheduled again
std::shared_ptr<void>
take_data() override

View File

@@ -108,7 +108,9 @@ spin_until_future_complete(
const FutureT & future,
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
{
rclcpp::executors::SingleThreadedExecutor executor;
rclcpp::ExecutorOptions options;
options.context = node_ptr->get_context();
rclcpp::executors::SingleThreadedExecutor executor(options);
return executors::spin_node_until_future_complete<FutureT>(executor, node_ptr, future, timeout);
}

View File

@@ -481,13 +481,13 @@ private:
"subscription use different allocator types, which is not supported");
}
if constexpr (rclcpp::TypeAdapter<MessageT>::is_specialized::value) {
if constexpr (rclcpp::TypeAdapter<MessageT, ROSMessageType>::is_specialized::value) {
ROSMessageTypeAllocator ros_message_alloc(allocator);
auto ptr = ros_message_alloc.allocate(1);
ros_message_alloc.construct(ptr);
auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_alloc, 1);
ROSMessageTypeAllocatorTraits::construct(ros_message_alloc, ptr);
ROSMessageTypeDeleter deleter;
allocator::set_allocator_for_deleter(&deleter, &allocator);
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*message, *ptr);
rclcpp::TypeAdapter<MessageT, ROSMessageType>::convert_to_ros_message(*message, *ptr);
auto ros_msg = std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, deleter);
ros_message_subscription->provide_intra_process_message(std::move(ros_msg));
} else {

View File

@@ -43,6 +43,7 @@ public:
* - arguments = {}
* - parameter_overrides = {}
* - use_global_arguments = true
* - enable_rosout = true
* - use_intra_process_comms = false
* - enable_topic_statistics = false
* - start_parameter_services = true

View File

@@ -495,7 +495,7 @@ public:
}
}
/// Configure client introspection.
/// Configure service introspection.
/**
* \param[in] clock clock to use to generate introspection timestamps
* \param[in] qos_service_event_pub QoS settings to use when creating the introspection publisher

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>21.0.3</version>
<version>21.0.8</version>
<description>The ROS client library in C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -496,7 +496,7 @@ Context::sleep_for(const std::chrono::nanoseconds & nanoseconds)
std::unique_lock<std::mutex> lock(interrupt_mutex_);
auto start = std::chrono::steady_clock::now();
// this will release the lock while waiting
interrupt_condition_variable_.wait_for(lock, nanoseconds);
interrupt_condition_variable_.wait_for(lock, time_left);
time_left -= std::chrono::steady_clock::now() - start;
}
} while (time_left > std::chrono::nanoseconds::zero() && this->is_valid());

View File

@@ -39,13 +39,6 @@ UnsupportedEventTypeException::UnsupportedEventTypeException(
EventHandlerBase::~EventHandlerBase()
{
// Since the rmw event listener holds a reference to
// this callback, we need to clear it on destruction of this class.
// This clearing is not needed for other rclcpp entities like pub/subs, since
// they do own the underlying rmw entities, which are destroyed
// on their rclcpp destructors, thus no risk of dangling pointers.
clear_on_ready_callback();
if (rcl_event_fini(&event_handle_) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",

View File

@@ -653,6 +653,11 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
subscription->get_topic_name(),
[&]() {return subscription->take_type_erased(message.get(), message_info);},
[&]() {subscription->handle_message(message, message_info);});
// TODO(clalancette): In the case that the user is using the MessageMemoryPool,
// and they take a shared_ptr reference to the message in the callback, this can
// inadvertently return the message to the pool when the user is still using it.
// This is a bug that needs to be fixed in the pool, and we should probably have
// a custom deleter for the message that actually does the return_message().
subscription->return_message(message);
}
break;

View File

@@ -17,7 +17,9 @@
void
rclcpp::spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
{
rclcpp::executors::SingleThreadedExecutor exec;
rclcpp::ExecutorOptions options;
options.context = node_ptr->get_context();
rclcpp::executors::SingleThreadedExecutor exec(options);
exec.spin_node_some(node_ptr);
}
@@ -30,7 +32,9 @@ rclcpp::spin_some(rclcpp::Node::SharedPtr node_ptr)
void
rclcpp::spin(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
{
rclcpp::executors::SingleThreadedExecutor exec;
rclcpp::ExecutorOptions options;
options.context = node_ptr->get_context();
rclcpp::executors::SingleThreadedExecutor exec(options);
exec.add_node(node_ptr);
exec.spin();
exec.remove_node(node_ptr);

View File

@@ -14,6 +14,7 @@
#include <memory>
#include <mutex>
#include <stdexcept>
#include "rcutils/macros.h"

View File

@@ -113,7 +113,7 @@ SignalHandler::get_logger()
SignalHandler &
SignalHandler::get_global_signal_handler()
{
static SignalHandler signal_handler;
static SignalHandler & signal_handler = *new SignalHandler();
return signal_handler;
}

View File

@@ -298,7 +298,20 @@ SubscriptionBase::setup_intra_process(
bool
SubscriptionBase::can_loan_messages() const
{
return rcl_subscription_can_loan_messages(subscription_handle_.get());
bool retval = rcl_subscription_can_loan_messages(subscription_handle_.get());
if (retval) {
// TODO(clalancette): The loaned message interface is currently not safe to use with
// shared_ptr callbacks. If a user takes a copy of the shared_ptr, it can get freed from
// underneath them via rcl_return_loaned_message_from_subscription(). The correct solution is
// to return the loaned message in a custom deleter, but that needs to be carefully handled
// with locking. Warn the user about this until we fix it.
RCLCPP_WARN_ONCE(
this->node_logger_,
"Loaned messages are only safe with const ref subscription callbacks. "
"If you are using any other kind of subscriptions, "
"set the ROS_DISABLE_LOANED_MESSAGES environment variable to 1 (the default).");
}
return retval;
}
rclcpp::Waitable::SharedPtr

View File

@@ -54,9 +54,7 @@ public:
ros_time_active_ = true;
// Update all attached clocks to zero or last recorded time
for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
set_clock(last_time_msg_, true, *it);
}
set_all_clocks(last_time_msg_, true);
}
// An internal method to use in the clock callback that iterates and disables all clocks
@@ -71,11 +69,8 @@ public:
ros_time_active_ = false;
// Update all attached clocks
std::lock_guard<std::mutex> guard(clock_list_lock_);
for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
auto msg = std::make_shared<builtin_interfaces::msg::Time>();
set_clock(msg, false, *it);
}
auto msg = std::make_shared<builtin_interfaces::msg::Time>();
set_all_clocks(msg, false);
}
// Check if ROS time is active

View File

@@ -22,6 +22,7 @@
#include <algorithm>
#include <atomic>
#include <chrono>
#include <future>
#include <limits>
#include <memory>
#include <string>
@@ -939,3 +940,31 @@ TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) {
executor.spin();
EXPECT_EQ(kNumMessages, this->callback_count.load());
}
// Check spin functions with non default context
TEST(TestExecutors, testSpinWithNonDefaultContext)
{
auto non_default_context = std::make_shared<rclcpp::Context>();
non_default_context->init(0, nullptr);
{
auto node =
std::make_unique<rclcpp::Node>("node", rclcpp::NodeOptions().context(non_default_context));
EXPECT_NO_THROW(rclcpp::spin_some(node->get_node_base_interface()));
auto check_spin_until_future_complete = [&]() {
std::promise<bool> promise;
std::future<bool> future = promise.get_future();
promise.set_value(true);
auto shared_future = future.share();
auto ret = rclcpp::spin_until_future_complete(
node->get_node_base_interface(), shared_future, 1s);
EXPECT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret);
};
EXPECT_NO_THROW(check_spin_until_future_complete());
}
rclcpp::shutdown(non_default_context);
}

View File

@@ -521,6 +521,10 @@ TEST_F(TestClient, rcl_client_response_subscription_get_actual_qos_error) {
}
TEST_F(TestClient, client_qos) {
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP() << "Skipping. The tests is known to be flaky in the rmw_connextdds.";
}
rclcpp::ServicesQoS qos_profile;
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
rclcpp::Duration duration(std::chrono::nanoseconds(1));

View File

@@ -152,33 +152,54 @@ TEST_F(TestPublisher, conversion_exception_is_passed_up) {
}
}
using UseTakeSharedMethod = bool;
class TestPublisherFixture
: public TestPublisher,
public ::testing::WithParamInterface<UseTakeSharedMethod>
{
};
/*
* Testing that publisher sends type adapted types and ROS message types with intra proccess communications.
*/
TEST_F(
TestPublisher,
TEST_P(
TestPublisherFixture,
check_type_adapted_message_is_sent_and_received_intra_process) {
using StringTypeAdapter = rclcpp::TypeAdapter<std::string, rclcpp::msg::String>;
const std::string message_data = "Message Data";
const std::string topic_name = "topic_name";
bool is_received;
auto callback =
[message_data, &is_received](
const rclcpp::msg::String::ConstSharedPtr msg,
const rclcpp::MessageInfo & message_info
) -> void
{
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
};
auto node = rclcpp::Node::make_shared(
"test_intra_process",
rclcpp::NodeOptions().use_intra_process_comms(true));
auto pub = node->create_publisher<StringTypeAdapter>(topic_name, 10);
auto sub = node->create_subscription<rclcpp::msg::String>(topic_name, 1, callback);
rclcpp::Subscription<rclcpp::msg::String>::SharedPtr sub;
if (GetParam()) {
auto callback =
[message_data, &is_received](
const rclcpp::msg::String::ConstSharedPtr msg,
const rclcpp::MessageInfo & message_info
) -> void
{
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
};
sub = node->create_subscription<rclcpp::msg::String>(topic_name, 1, callback);
} else {
auto callback_unique =
[message_data, &is_received](
rclcpp::msg::String::UniquePtr msg,
const rclcpp::MessageInfo & message_info
) -> void
{
is_received = true;
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
};
sub = node->create_subscription<rclcpp::msg::String>(topic_name, 1, callback_unique);
}
auto wait_for_message_to_be_received = [&is_received, &node]() {
rclcpp::executors::SingleThreadedExecutor executor;
@@ -239,6 +260,14 @@ TEST_F(
}
}
INSTANTIATE_TEST_SUITE_P(
TestPublisherFixtureWithParam,
TestPublisherFixture,
::testing::Values(
true, // use take shared method
false // not use take shared method
));
/*
* Testing that publisher sends type adapted types and ROS message types with inter proccess communications.
*/

View File

@@ -18,10 +18,24 @@
#include "rclcpp/rate.hpp"
class TestRate : public ::testing::Test
{
public:
void SetUp()
{
rclcpp::init(0, nullptr);
}
void TearDown()
{
rclcpp::shutdown();
}
};
/*
Basic tests for the Rate and WallRate classes.
*/
TEST(TestRate, rate_basics) {
TEST_F(TestRate, rate_basics) {
auto period = std::chrono::milliseconds(1000);
auto offset = std::chrono::milliseconds(500);
auto epsilon = std::chrono::milliseconds(100);
@@ -61,7 +75,7 @@ TEST(TestRate, rate_basics) {
ASSERT_TRUE(epsilon > delta);
}
TEST(TestRate, wall_rate_basics) {
TEST_F(TestRate, wall_rate_basics) {
auto period = std::chrono::milliseconds(100);
auto offset = std::chrono::milliseconds(50);
auto epsilon = std::chrono::milliseconds(1);
@@ -101,7 +115,7 @@ TEST(TestRate, wall_rate_basics) {
EXPECT_GT(epsilon, delta);
}
TEST(TestRate, from_double) {
TEST_F(TestRate, from_double) {
{
rclcpp::WallRate rate(1.0);
EXPECT_EQ(std::chrono::seconds(1), rate.period());

View File

@@ -310,3 +310,25 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_fil
}
}
}
TEST_F(
CLASSNAME(
TestContentFilterSubscription,
RMW_IMPLEMENTATION), create_two_content_filters_with_same_topic_name_and_destroy) {
// Create another content filter
auto options = rclcpp::SubscriptionOptions();
std::string filter_expression = "int32_value > %0";
std::vector<std::string> expression_parameters = {"4"};
options.content_filter_options.filter_expression = filter_expression;
options.content_filter_options.expression_parameters = expression_parameters;
auto callback = [](std::shared_ptr<const test_msgs::msg::BasicTypes>) {};
auto sub_2 = node->create_subscription<test_msgs::msg::BasicTypes>(
"content_filter_topic", qos, callback, options);
EXPECT_NE(nullptr, sub_2);
sub_2.reset();
}

View File

@@ -3,6 +3,24 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21.0.8 (2024-11-08)
-------------------
21.0.7 (2024-07-10)
-------------------
* fix: Fixed race condition in action server between is_ready and take"… (`#2531 <https://github.com/ros2/rclcpp/issues/2531>`_)
* Do not generate the exception when action service response timeout. (`#2519 <https://github.com/ros2/rclcpp/issues/2519>`_)
* Contributors: Janosch Machowinski, Tomoya Fujita, William Woodall
21.0.6 (2024-04-19)
-------------------
21.0.5 (2024-02-07)
-------------------
21.0.4 (2023-11-17)
-------------------
21.0.3 (2023-09-08)
-------------------

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>21.0.3</version>
<version>21.0.8</version>
<description>Adds action APIs for C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -19,6 +19,7 @@
#include <string>
#include <tuple>
#include <utility>
#include <variant>
#include "rcl_action/action_client.h"
#include "rcl_action/wait.h"
@@ -31,6 +32,67 @@
namespace rclcpp_action
{
struct ClientBaseData
{
struct FeedbackReadyData
{
FeedbackReadyData(rcl_ret_t retIn, std::shared_ptr<void> msg)
: ret(retIn), feedback_message(msg) {}
rcl_ret_t ret;
std::shared_ptr<void> feedback_message;
};
struct StatusReadyData
{
StatusReadyData(rcl_ret_t retIn, std::shared_ptr<void> msg)
: ret(retIn), status_message(msg) {}
rcl_ret_t ret;
std::shared_ptr<void> status_message;
};
struct GoalResponseData
{
GoalResponseData(rcl_ret_t retIn, rmw_request_id_t header, std::shared_ptr<void> response)
: ret(retIn), response_header(header), goal_response(response) {}
rcl_ret_t ret;
rmw_request_id_t response_header;
std::shared_ptr<void> goal_response;
};
struct CancelResponseData
{
CancelResponseData(rcl_ret_t retIn, rmw_request_id_t header, std::shared_ptr<void> response)
: ret(retIn), response_header(header), cancel_response(response) {}
rcl_ret_t ret;
rmw_request_id_t response_header;
std::shared_ptr<void> cancel_response;
};
struct ResultResponseData
{
ResultResponseData(rcl_ret_t retIn, rmw_request_id_t header, std::shared_ptr<void> response)
: ret(retIn), response_header(header), result_response(response) {}
rcl_ret_t ret;
rmw_request_id_t response_header;
std::shared_ptr<void> result_response;
};
std::variant<
FeedbackReadyData,
StatusReadyData,
GoalResponseData,
CancelResponseData,
ResultResponseData
> data;
explicit ClientBaseData(FeedbackReadyData && data_in)
: data(std::move(data_in)) {}
explicit ClientBaseData(StatusReadyData && data_in)
: data(std::move(data_in)) {}
explicit ClientBaseData(GoalResponseData && data_in)
: data(std::move(data_in)) {}
explicit ClientBaseData(CancelResponseData && data_in)
: data(std::move(data_in)) {}
explicit ClientBaseData(ResultResponseData && data_in)
: data(std::move(data_in)) {}
};
class ClientBaseImpl
{
public:
@@ -94,11 +156,13 @@ public:
size_t num_clients{0u};
size_t num_services{0u};
bool is_feedback_ready{false};
bool is_status_ready{false};
bool is_goal_response_ready{false};
bool is_cancel_response_ready{false};
bool is_result_response_ready{false};
// Lock for action_client_
std::recursive_mutex action_client_mutex_;
// next ready event for taking, will be set by is_ready and will be processed by take_data
std::atomic<size_t> next_ready_event;
// used to indicate that next_ready_event has no ready event for processing
static constexpr size_t NO_EVENT_READY = std::numeric_limits<size_t>::max();
rclcpp::Context::SharedPtr context_;
rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_;
@@ -142,6 +206,7 @@ bool
ClientBase::action_server_is_ready() const
{
bool is_ready;
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
rcl_ret_t ret = rcl_action_server_is_available(
this->pimpl_->node_handle.get(),
this->pimpl_->client_handle.get(),
@@ -255,6 +320,7 @@ ClientBase::get_number_of_ready_services()
void
ClientBase::add_to_wait_set(rcl_wait_set_t * wait_set)
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
rcl_ret_t ret = rcl_action_wait_set_add_action_client(
wait_set, pimpl_->client_handle.get(), nullptr, nullptr);
if (RCL_RET_OK != ret) {
@@ -265,23 +331,56 @@ ClientBase::add_to_wait_set(rcl_wait_set_t * wait_set)
bool
ClientBase::is_ready(rcl_wait_set_t * wait_set)
{
rcl_ret_t ret = rcl_action_client_wait_set_get_entities_ready(
wait_set, pimpl_->client_handle.get(),
&pimpl_->is_feedback_ready,
&pimpl_->is_status_ready,
&pimpl_->is_goal_response_ready,
&pimpl_->is_cancel_response_ready,
&pimpl_->is_result_response_ready);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(
ret, "failed to check for any ready entities");
bool is_feedback_ready{false};
bool is_status_ready{false};
bool is_goal_response_ready{false};
bool is_cancel_response_ready{false};
bool is_result_response_ready{false};
rcl_ret_t ret;
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
ret = rcl_action_client_wait_set_get_entities_ready(
wait_set, pimpl_->client_handle.get(),
&is_feedback_ready,
&is_status_ready,
&is_goal_response_ready,
&is_cancel_response_ready,
&is_result_response_ready);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(
ret, "failed to check for any ready entities");
}
}
return
pimpl_->is_feedback_ready ||
pimpl_->is_status_ready ||
pimpl_->is_goal_response_ready ||
pimpl_->is_cancel_response_ready ||
pimpl_->is_result_response_ready;
pimpl_->next_ready_event = ClientBaseImpl::NO_EVENT_READY;
if (is_feedback_ready) {
pimpl_->next_ready_event = static_cast<size_t>(EntityType::FeedbackSubscription);
return true;
}
if (is_status_ready) {
pimpl_->next_ready_event = static_cast<size_t>(EntityType::StatusSubscription);
return true;
}
if (is_goal_response_ready) {
pimpl_->next_ready_event = static_cast<size_t>(EntityType::GoalClient);
return true;
}
if (is_result_response_ready) {
pimpl_->next_ready_event = static_cast<size_t>(EntityType::ResultClient);
return true;
}
if (is_cancel_response_ready) {
pimpl_->next_ready_event = static_cast<size_t>(EntityType::CancelClient);
return true;
}
return false;
}
void
@@ -432,7 +531,6 @@ ClientBase::set_callback_to_entity(
}
};
// Set it temporarily to the new callback, while we replace the old one.
// This two-step setting, prevents a gap where the old std::function has
// been replaced but the middleware hasn't been told about the new one yet.
@@ -550,140 +648,159 @@ ClientBase::clear_on_ready_callback()
std::shared_ptr<void>
ClientBase::take_data()
{
if (pimpl_->is_feedback_ready) {
std::shared_ptr<void> feedback_message = this->create_feedback_message();
rcl_ret_t ret = rcl_action_take_feedback(
pimpl_->client_handle.get(), feedback_message.get());
return std::static_pointer_cast<void>(
std::make_shared<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(
ret, feedback_message));
} else if (pimpl_->is_status_ready) {
std::shared_ptr<void> status_message = this->create_status_message();
rcl_ret_t ret = rcl_action_take_status(
pimpl_->client_handle.get(), status_message.get());
return std::static_pointer_cast<void>(
std::make_shared<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(
ret, status_message));
} else if (pimpl_->is_goal_response_ready) {
rmw_request_id_t response_header;
std::shared_ptr<void> goal_response = this->create_goal_response();
rcl_ret_t ret = rcl_action_take_goal_response(
pimpl_->client_handle.get(), &response_header, goal_response.get());
return std::static_pointer_cast<void>(
std::make_shared<std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(
ret, response_header, goal_response));
} else if (pimpl_->is_result_response_ready) {
rmw_request_id_t response_header;
std::shared_ptr<void> result_response = this->create_result_response();
rcl_ret_t ret = rcl_action_take_result_response(
pimpl_->client_handle.get(), &response_header, result_response.get());
return std::static_pointer_cast<void>(
std::make_shared<std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(
ret, response_header, result_response));
} else if (pimpl_->is_cancel_response_ready) {
rmw_request_id_t response_header;
std::shared_ptr<void> cancel_response = this->create_cancel_response();
rcl_ret_t ret = rcl_action_take_cancel_response(
pimpl_->client_handle.get(), &response_header, cancel_response.get());
return std::static_pointer_cast<void>(
std::make_shared<std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(
ret, response_header, cancel_response));
} else {
throw std::runtime_error("Taking data from action client but nothing is ready");
// next_ready_event is an atomic, caching localy
size_t next_ready_event = pimpl_->next_ready_event.exchange(ClientBaseImpl::NO_EVENT_READY);
if (next_ready_event == ClientBaseImpl::NO_EVENT_READY) {
// there is a known bug in iron, that take_data might be called multiple
// times. Therefore instead of throwing, we just return a nullptr as a workaround.
return nullptr;
}
return take_data_by_entity_id(next_ready_event);
}
std::shared_ptr<void>
ClientBase::take_data_by_entity_id(size_t id)
{
std::shared_ptr<ClientBaseData> data_ptr;
rcl_ret_t ret;
// Mark as ready the entity from which we want to take data
switch (static_cast<EntityType>(id)) {
case EntityType::GoalClient:
pimpl_->is_goal_response_ready = true;
{
rmw_request_id_t response_header;
std::shared_ptr<void> goal_response;
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
goal_response = this->create_goal_response();
ret = rcl_action_take_goal_response(
pimpl_->client_handle.get(), &response_header, goal_response.get());
}
data_ptr = std::make_shared<ClientBaseData>(
ClientBaseData::GoalResponseData(
ret, response_header, goal_response));
}
break;
case EntityType::ResultClient:
pimpl_->is_result_response_ready = true;
{
rmw_request_id_t response_header;
std::shared_ptr<void> result_response;
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
result_response = this->create_result_response();
ret = rcl_action_take_result_response(
pimpl_->client_handle.get(), &response_header, result_response.get());
}
data_ptr =
std::make_shared<ClientBaseData>(
ClientBaseData::ResultResponseData(
ret, response_header, result_response));
}
break;
case EntityType::CancelClient:
pimpl_->is_cancel_response_ready = true;
{
rmw_request_id_t response_header;
std::shared_ptr<void> cancel_response;
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
cancel_response = this->create_cancel_response();
ret = rcl_action_take_cancel_response(
pimpl_->client_handle.get(), &response_header, cancel_response.get());
}
data_ptr =
std::make_shared<ClientBaseData>(
ClientBaseData::CancelResponseData(
ret, response_header, cancel_response));
}
break;
case EntityType::FeedbackSubscription:
pimpl_->is_feedback_ready = true;
{
std::shared_ptr<void> feedback_message;
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
feedback_message = this->create_feedback_message();
ret = rcl_action_take_feedback(
pimpl_->client_handle.get(), feedback_message.get());
}
data_ptr =
std::make_shared<ClientBaseData>(
ClientBaseData::FeedbackReadyData(
ret, feedback_message));
}
break;
case EntityType::StatusSubscription:
pimpl_->is_status_ready = true;
{
std::shared_ptr<void> status_message;
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
status_message = this->create_status_message();
ret = rcl_action_take_status(
pimpl_->client_handle.get(), status_message.get());
}
data_ptr =
std::make_shared<ClientBaseData>(
ClientBaseData::StatusReadyData(
ret, status_message));
}
break;
}
return take_data();
return std::static_pointer_cast<void>(data_ptr);
}
void
ClientBase::execute(std::shared_ptr<void> & data)
ClientBase::execute(std::shared_ptr<void> & data_in)
{
if (!data) {
throw std::runtime_error("'data' is empty");
if (!data_in) {
// workaround, if take_data was called multiple timed, it returns a nullptr
// normally we should throw here, but as an API stable bug fix, we just ignore this...
return;
}
if (pimpl_->is_feedback_ready) {
auto shared_ptr = std::static_pointer_cast<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(data);
auto ret = std::get<0>(*shared_ptr);
pimpl_->is_feedback_ready = false;
if (RCL_RET_OK == ret) {
auto feedback_message = std::get<1>(*shared_ptr);
this->handle_feedback_message(feedback_message);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking feedback");
}
} else if (pimpl_->is_status_ready) {
auto shared_ptr = std::static_pointer_cast<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(data);
auto ret = std::get<0>(*shared_ptr);
pimpl_->is_status_ready = false;
if (RCL_RET_OK == ret) {
auto status_message = std::get<1>(*shared_ptr);
this->handle_status_message(status_message);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking status");
}
} else if (pimpl_->is_goal_response_ready) {
auto shared_ptr = std::static_pointer_cast<
std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
auto ret = std::get<0>(*shared_ptr);
pimpl_->is_goal_response_ready = false;
if (RCL_RET_OK == ret) {
auto response_header = std::get<1>(*shared_ptr);
auto goal_response = std::get<2>(*shared_ptr);
this->handle_goal_response(response_header, goal_response);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking goal response");
}
} else if (pimpl_->is_result_response_ready) {
auto shared_ptr = std::static_pointer_cast<
std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
auto ret = std::get<0>(*shared_ptr);
pimpl_->is_result_response_ready = false;
if (RCL_RET_OK == ret) {
auto response_header = std::get<1>(*shared_ptr);
auto result_response = std::get<2>(*shared_ptr);
this->handle_result_response(response_header, result_response);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking result response");
}
} else if (pimpl_->is_cancel_response_ready) {
auto shared_ptr = std::static_pointer_cast<
std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
auto ret = std::get<0>(*shared_ptr);
pimpl_->is_cancel_response_ready = false;
if (RCL_RET_OK == ret) {
auto response_header = std::get<1>(*shared_ptr);
auto cancel_response = std::get<2>(*shared_ptr);
this->handle_cancel_response(response_header, cancel_response);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking cancel response");
}
} else {
throw std::runtime_error("Executing action client but nothing is ready");
}
std::shared_ptr<ClientBaseData> data_ptr = std::static_pointer_cast<ClientBaseData>(data_in);
std::visit(
[&](auto && data) -> void {
using T = std::decay_t<decltype(data)>;
if constexpr (std::is_same_v<T, ClientBaseData::FeedbackReadyData>) {
if (RCL_RET_OK == data.ret) {
this->handle_feedback_message(data.feedback_message);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) {
rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking feedback");
}
}
if constexpr (std::is_same_v<T, ClientBaseData::StatusReadyData>) {
if (RCL_RET_OK == data.ret) {
this->handle_status_message(data.status_message);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) {
rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking status");
}
}
if constexpr (std::is_same_v<T, ClientBaseData::GoalResponseData>) {
if (RCL_RET_OK == data.ret) {
this->handle_goal_response(data.response_header, data.goal_response);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) {
rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking goal response");
}
}
if constexpr (std::is_same_v<T, ClientBaseData::ResultResponseData>) {
if (RCL_RET_OK == data.ret) {
this->handle_result_response(data.response_header, data.result_response);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) {
rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking result response");
}
}
if constexpr (std::is_same_v<T, ClientBaseData::CancelResponseData>) {
if (RCL_RET_OK == data.ret) {
this->handle_cancel_response(data.response_header, data.cancel_response);
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) {
rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking cancel response");
}
}
}, data_ptr->data);
}
} // namespace rclcpp_action

View File

@@ -18,6 +18,7 @@
#include <tuple>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
#include "rcl_action/action_server.h"
@@ -33,8 +34,50 @@
using rclcpp_action::ServerBase;
using rclcpp_action::GoalUUID;
struct ServerBaseData;
namespace rclcpp_action
{
struct ServerBaseData
{
using GoalRequestData = std::tuple<
rcl_ret_t,
const rcl_action_goal_info_t,
rmw_request_id_t,
std::shared_ptr<void>
>;
using CancelRequestData = std::tuple<
rcl_ret_t,
std::shared_ptr<action_msgs::srv::CancelGoal::Request>,
rmw_request_id_t
>;
using ResultRequestData = std::tuple<rcl_ret_t, std::shared_ptr<void>, rmw_request_id_t>;
using GoalExpiredData = struct Empty {};
std::variant<GoalRequestData, CancelRequestData, ResultRequestData, GoalExpiredData> data;
explicit ServerBaseData(GoalRequestData && data_in)
: data(std::move(data_in)) {}
explicit ServerBaseData(CancelRequestData && data_in)
: data(std::move(data_in)) {}
explicit ServerBaseData(ResultRequestData && data_in)
: data(std::move(data_in)) {}
explicit ServerBaseData(GoalExpiredData && data_in)
: data(std::move(data_in)) {}
};
enum class ActionEventType : std::size_t
{
GoalService,
ResultService,
CancelService,
Expired,
};
class ServerBaseImpl
{
public:
@@ -60,11 +103,6 @@ public:
size_t num_services_ = 0;
size_t num_guard_conditions_ = 0;
std::atomic<bool> goal_request_ready_{false};
std::atomic<bool> cancel_request_ready_{false};
std::atomic<bool> result_request_ready_{false};
std::atomic<bool> goal_expired_{false};
// Lock for unordered_maps
std::recursive_mutex unordered_map_mutex_;
@@ -75,8 +113,15 @@ public:
// rcl goal handles are kept so api to send result doesn't try to access freed memory
std::unordered_map<GoalUUID, std::shared_ptr<rcl_action_goal_handle_t>> goal_handles_;
// next ready event for taking, will be set by is_ready and will be processed by take_data
std::atomic<size_t> next_ready_event;
// used to indicate that next_ready_event has no ready event for processing
static constexpr size_t NO_EVENT_READY = std::numeric_limits<size_t>::max();
rclcpp::Logger logger_;
};
} // namespace rclcpp_action
ServerBase::ServerBase(
@@ -194,124 +239,170 @@ ServerBase::is_ready(rcl_wait_set_t * wait_set)
&goal_expired);
}
pimpl_->goal_request_ready_ = goal_request_ready;
pimpl_->cancel_request_ready_ = cancel_request_ready;
pimpl_->result_request_ready_ = result_request_ready;
pimpl_->goal_expired_ = goal_expired;
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
return pimpl_->goal_request_ready_.load() ||
pimpl_->cancel_request_ready_.load() ||
pimpl_->result_request_ready_.load() ||
pimpl_->goal_expired_.load();
pimpl_->next_ready_event = ServerBaseImpl::NO_EVENT_READY;
if (goal_request_ready) {
pimpl_->next_ready_event = static_cast<uint32_t>(ActionEventType::GoalService);
return true;
}
if (cancel_request_ready) {
pimpl_->next_ready_event = static_cast<uint32_t>(ActionEventType::CancelService);
return true;
}
if (result_request_ready) {
pimpl_->next_ready_event = static_cast<uint32_t>(ActionEventType::ResultService);
return true;
}
if (goal_expired) {
pimpl_->next_ready_event = static_cast<uint32_t>(ActionEventType::Expired);
return true;
}
return false;
}
std::shared_ptr<void>
ServerBase::take_data()
{
if (pimpl_->goal_request_ready_.load()) {
rcl_ret_t ret;
rcl_action_goal_info_t goal_info = rcl_action_get_zero_initialized_goal_info();
rmw_request_id_t request_header;
size_t next_ready_event = pimpl_->next_ready_event.exchange(ServerBaseImpl::NO_EVENT_READY);
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
std::shared_ptr<void> message = create_goal_request();
ret = rcl_action_take_goal_request(
pimpl_->action_server_.get(),
&request_header,
message.get());
return std::static_pointer_cast<void>(
std::make_shared
<std::tuple<rcl_ret_t, rcl_action_goal_info_t, rmw_request_id_t, std::shared_ptr<void>>>(
ret,
goal_info,
request_header, message));
} else if (pimpl_->cancel_request_ready_.load()) {
rcl_ret_t ret;
rmw_request_id_t request_header;
// Initialize cancel request
auto request = std::make_shared<action_msgs::srv::CancelGoal::Request>();
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
ret = rcl_action_take_cancel_request(
pimpl_->action_server_.get(),
&request_header,
request.get());
return std::static_pointer_cast<void>(
std::make_shared
<std::tuple<rcl_ret_t, std::shared_ptr<action_msgs::srv::CancelGoal::Request>,
rmw_request_id_t>>(ret, request, request_header));
} else if (pimpl_->result_request_ready_.load()) {
rcl_ret_t ret;
// Get the result request message
rmw_request_id_t request_header;
std::shared_ptr<void> result_request = create_result_request();
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
ret = rcl_action_take_result_request(
pimpl_->action_server_.get(), &request_header, result_request.get());
return std::static_pointer_cast<void>(
std::make_shared<std::tuple<rcl_ret_t, std::shared_ptr<void>, rmw_request_id_t>>(
ret, result_request, request_header));
} else if (pimpl_->goal_expired_.load()) {
if (next_ready_event == ServerBaseImpl::NO_EVENT_READY) {
// there is a known bug in iron, that take_data might be called multiple
// times. Therefore instead of throwing, we just return a nullptr as a workaround.
return nullptr;
} else {
throw std::runtime_error("Taking data from action server but nothing is ready");
}
return take_data_by_entity_id(next_ready_event);
}
std::shared_ptr<void>
ServerBase::take_data_by_entity_id(size_t id)
{
static_assert(
static_cast<size_t>(EntityType::GoalService) ==
static_cast<size_t>(ActionEventType::GoalService));
static_assert(
static_cast<size_t>(EntityType::ResultService) ==
static_cast<size_t>(ActionEventType::ResultService));
static_assert(
static_cast<size_t>(EntityType::CancelService) ==
static_cast<size_t>(ActionEventType::CancelService));
std::shared_ptr<ServerBaseData> data_ptr;
// Mark as ready the entity from which we want to take data
switch (static_cast<EntityType>(id)) {
case EntityType::GoalService:
pimpl_->goal_request_ready_ = true;
switch (static_cast<ActionEventType>(id)) {
case ActionEventType::GoalService:
{
rcl_ret_t ret;
rcl_action_goal_info_t goal_info = rcl_action_get_zero_initialized_goal_info();
rmw_request_id_t request_header;
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
std::shared_ptr<void> message = create_goal_request();
ret = rcl_action_take_goal_request(
pimpl_->action_server_.get(),
&request_header,
message.get());
data_ptr = std::make_shared<ServerBaseData>(
ServerBaseData::GoalRequestData(ret, goal_info, request_header, message));
}
break;
case EntityType::ResultService:
pimpl_->result_request_ready_ = true;
case ActionEventType::ResultService:
{
rcl_ret_t ret;
// Get the result request message
rmw_request_id_t request_header;
std::shared_ptr<void> result_request = create_result_request();
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
ret = rcl_action_take_result_request(
pimpl_->action_server_.get(), &request_header, result_request.get());
data_ptr =
std::make_shared<ServerBaseData>(
ServerBaseData::ResultRequestData(ret, result_request, request_header));
}
break;
case EntityType::CancelService:
pimpl_->cancel_request_ready_ = true;
case ActionEventType::CancelService:
{
rcl_ret_t ret;
rmw_request_id_t request_header;
// Initialize cancel request
auto request = std::make_shared<action_msgs::srv::CancelGoal::Request>();
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
ret = rcl_action_take_cancel_request(
pimpl_->action_server_.get(),
&request_header,
request.get());
data_ptr =
std::make_shared<ServerBaseData>(
ServerBaseData::CancelRequestData(ret, request, request_header));
}
break;
case ActionEventType::Expired:
{
data_ptr =
std::make_shared<ServerBaseData>(ServerBaseData::GoalExpiredData());
}
break;
}
return take_data();
return std::static_pointer_cast<void>(data_ptr);
}
void
ServerBase::execute(std::shared_ptr<void> & data)
ServerBase::execute(std::shared_ptr<void> & data_in)
{
if (!data && !pimpl_->goal_expired_.load()) {
throw std::runtime_error("'data' is empty");
if (!data_in) {
// workaround, if take_data was called multiple timed, it returns a nullptr
// normally we should throw here, but as an API stable bug fix, we just ignore this...
return;
}
if (pimpl_->goal_request_ready_.load()) {
execute_goal_request_received(data);
} else if (pimpl_->cancel_request_ready_.load()) {
execute_cancel_request_received(data);
} else if (pimpl_->result_request_ready_.load()) {
execute_result_request_received(data);
} else if (pimpl_->goal_expired_.load()) {
execute_check_expired_goals();
} else {
throw std::runtime_error("Executing action server but nothing is ready");
}
std::shared_ptr<ServerBaseData> data_ptr = std::static_pointer_cast<ServerBaseData>(data_in);
std::visit(
[&](auto && data) -> void {
using T = std::decay_t<decltype(data)>;
if constexpr (std::is_same_v<T, ServerBaseData::GoalRequestData>) {
execute_goal_request_received(data_in);
}
if constexpr (std::is_same_v<T, ServerBaseData::CancelRequestData>) {
execute_cancel_request_received(data_in);
}
if constexpr (std::is_same_v<T, ServerBaseData::ResultRequestData>) {
execute_result_request_received(data_in);
}
if constexpr (std::is_same_v<T, ServerBaseData::GoalExpiredData>) {
execute_check_expired_goals();
}
},
data_ptr->data);
}
void
ServerBase::execute_goal_request_received(std::shared_ptr<void> & data)
{
auto shared_ptr = std::static_pointer_cast
<std::tuple<rcl_ret_t, rcl_action_goal_info_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
rcl_ret_t ret = std::get<0>(*shared_ptr);
std::shared_ptr<ServerBaseData> data_ptr = std::static_pointer_cast<ServerBaseData>(data);
const ServerBaseData::GoalRequestData & gData(
std::get<ServerBaseData::GoalRequestData>(data_ptr->data));
rcl_ret_t ret = std::get<0>(gData);
rcl_action_goal_info_t goal_info = std::get<1>(gData);
rmw_request_id_t request_header = std::get<2>(gData);
const std::shared_ptr<void> message = std::get<3>(gData);
if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) {
// Ignore take failure because connext fails if it receives a sample without valid data.
// This happens when a client shuts down and connext receives a sample saying the client is
@@ -320,14 +411,6 @@ ServerBase::execute_goal_request_received(std::shared_ptr<void> & data)
} else if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
rcl_action_goal_info_t goal_info = std::get<1>(*shared_ptr);
rmw_request_id_t request_header = std::get<2>(*shared_ptr);
std::shared_ptr<void> message = std::get<3>(*shared_ptr);
bool expected = true;
if (!pimpl_->goal_request_ready_.compare_exchange_strong(expected, false)) {
return;
}
GoalUUID uuid = get_goal_id_from_goal_request(message.get());
convert(uuid, &goal_info);
@@ -344,7 +427,16 @@ ServerBase::execute_goal_request_received(std::shared_ptr<void> & data)
}
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
if (ret == RCL_RET_TIMEOUT) {
RCLCPP_WARN(
pimpl_->logger_,
"Failed to send goal response %s (timeout): %s",
to_string(uuid).c_str(), rcl_get_error_string().str);
rcl_reset_error();
return;
} else {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
}
const auto status = response_pair.first;
@@ -402,10 +494,15 @@ ServerBase::execute_goal_request_received(std::shared_ptr<void> & data)
void
ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
{
auto shared_ptr = std::static_pointer_cast
<std::tuple<rcl_ret_t, std::shared_ptr<action_msgs::srv::CancelGoal::Request>,
rmw_request_id_t>>(data);
auto ret = std::get<0>(*shared_ptr);
std::shared_ptr<ServerBaseData> data_ptr = std::static_pointer_cast<ServerBaseData>(data);
const ServerBaseData::CancelRequestData & gData(
std::get<ServerBaseData::CancelRequestData>(data_ptr->data));
rcl_ret_t ret = std::get<0>(gData);
std::shared_ptr<action_msgs::srv::CancelGoal::Request> request = std::get<1>(gData);
rmw_request_id_t request_header = std::get<2>(gData);
if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) {
// Ignore take failure because connext fails if it receives a sample without valid data.
// This happens when a client shuts down and connext receives a sample saying the client is
@@ -414,9 +511,6 @@ ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
} else if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
auto request = std::get<1>(*shared_ptr);
auto request_header = std::get<2>(*shared_ptr);
pimpl_->cancel_request_ready_ = false;
// Convert c++ message to C message
rcl_action_cancel_request_t cancel_request = rcl_action_get_zero_initialized_cancel_request();
@@ -483,6 +577,15 @@ ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
pimpl_->action_server_.get(), &request_header, response.get());
}
if (ret == RCL_RET_TIMEOUT) {
GoalUUID uuid = request->goal_info.goal_id.uuid;
RCLCPP_WARN(
pimpl_->logger_,
"Failed to send cancel response %s (timeout): %s",
to_string(uuid).c_str(), rcl_get_error_string().str);
rcl_reset_error();
return;
}
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
@@ -492,9 +595,14 @@ ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
void
ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
{
auto shared_ptr = std::static_pointer_cast
<std::tuple<rcl_ret_t, std::shared_ptr<void>, rmw_request_id_t>>(data);
auto ret = std::get<0>(*shared_ptr);
std::shared_ptr<ServerBaseData> data_ptr = std::static_pointer_cast<ServerBaseData>(data);
const ServerBaseData::ResultRequestData & gData(
std::get<ServerBaseData::ResultRequestData>(data_ptr->data));
rcl_ret_t ret = std::get<0>(gData);
std::shared_ptr<void> result_request = std::get<1>(gData);
rmw_request_id_t request_header = std::get<2>(gData);
if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) {
// Ignore take failure because connext fails if it receives a sample without valid data.
// This happens when a client shuts down and connext receives a sample saying the client is
@@ -503,10 +611,7 @@ ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
} else if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
auto result_request = std::get<1>(*shared_ptr);
auto request_header = std::get<2>(*shared_ptr);
pimpl_->result_request_ready_ = false;
std::shared_ptr<void> result_response;
// check if the goal exists
@@ -538,6 +643,14 @@ ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
rcl_ret_t rcl_ret = rcl_action_send_result_response(
pimpl_->action_server_.get(), &request_header, result_response.get());
if (rcl_ret == RCL_RET_TIMEOUT) {
RCLCPP_WARN(
pimpl_->logger_,
"Failed to send result response %s (timeout): %s",
to_string(uuid).c_str(), rcl_get_error_string().str);
rcl_reset_error();
return;
}
if (RCL_RET_OK != rcl_ret) {
rclcpp::exceptions::throw_from_rcl_error(rcl_ret);
}
@@ -671,7 +784,13 @@ ServerBase::publish_result(const GoalUUID & uuid, std::shared_ptr<void> result_m
for (auto & request_header : iter->second) {
rcl_ret_t ret = rcl_action_send_result_response(
pimpl_->action_server_.get(), &request_header, result_msg.get());
if (RCL_RET_OK != ret) {
if (ret == RCL_RET_TIMEOUT) {
RCLCPP_WARN(
pimpl_->logger_,
"Failed to send result response %s (timeout): %s",
to_string(uuid).c_str(), rcl_get_error_string().str);
rcl_reset_error();
} else if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
}

View File

@@ -2,6 +2,25 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21.0.8 (2024-11-08)
-------------------
21.0.7 (2024-07-10)
-------------------
21.0.6 (2024-04-19)
-------------------
21.0.5 (2024-02-07)
-------------------
* Increase the service queue sizes in component_container (`#2381 <https://github.com/ros2/rclcpp/issues/2381>`_)
* Contributors: M. Fatih Cırıt
21.0.4 (2023-11-17)
-------------------
* Add missing header required by the rclcpp::NodeOptions type (`#2325 <https://github.com/ros2/rclcpp/issues/2325>`_)
* Contributors: Ignacio Vizzo
21.0.3 (2023-09-08)
-------------------

View File

@@ -15,6 +15,7 @@
#ifndef RCLCPP_COMPONENTS__NODE_FACTORY_HPP__
#define RCLCPP_COMPONENTS__NODE_FACTORY_HPP__
#include "rclcpp/node_options.hpp"
#include "rclcpp_components/node_instance_wrapper.hpp"
namespace rclcpp_components

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>21.0.3</version>
<version>21.0.8</version>
<description>Package containing tools for dynamically loadable components</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -39,10 +39,12 @@ ComponentManager::ComponentManager(
{
loadNode_srv_ = create_service<LoadNode>(
"~/_container/load_node",
std::bind(&ComponentManager::on_load_node, this, _1, _2, _3));
std::bind(&ComponentManager::on_load_node, this, _1, _2, _3),
rclcpp::ServicesQoS().keep_last(200));
unloadNode_srv_ = create_service<UnloadNode>(
"~/_container/unload_node",
std::bind(&ComponentManager::on_unload_node, this, _1, _2, _3));
std::bind(&ComponentManager::on_unload_node, this, _1, _2, _3),
rclcpp::ServicesQoS().keep_last(200));
listNodes_srv_ = create_service<ListNodes>(
"~/_container/list_nodes",
std::bind(&ComponentManager::on_list_nodes, this, _1, _2, _3));

View File

@@ -3,6 +3,29 @@ Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21.0.8 (2024-11-08)
-------------------
* add logger level service to lifecycle node. (`#2288 <https://github.com/ros2/rclcpp/issues/2288>`_)
* Contributors: Tomoya Fujita
21.0.7 (2024-07-10)
-------------------
* revert call shutdown in LifecycleNode destructor (Iron) (`#2559 <https://github.com/ros2/rclcpp/issues/2559>`_)
* lifecycle node dtor shutdown should be called only in primary state. (`#2543 <https://github.com/ros2/rclcpp/issues/2543>`_)
* rclcpp::shutdown should not be called before LifecycleNode dtor. (`#2539 <https://github.com/ros2/rclcpp/issues/2539>`_)
* Contributors: Tomoya Fujita
21.0.6 (2024-04-19)
-------------------
* call shutdown in LifecycleNode dtor to avoid leaving the device in un… (`#2490 <https://github.com/ros2/rclcpp/issues/2490>`_)
* Contributors: Tomoya Fujita
21.0.5 (2024-02-07)
-------------------
21.0.4 (2023-11-17)
-------------------
21.0.3 (2023-09-08)
-------------------
* Switch lifecycle to use the RCLCPP macros. (`#2244 <https://github.com/ros2/rclcpp/issues/2244>`_)

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>21.0.3</version>
<version>21.0.8</version>
<description>Package containing a prototype for lifecycle implementation</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -142,6 +142,10 @@ LifecycleNode::LifecycleNode(
&LifecycleNodeInterface::on_deactivate, this,
std::placeholders::_1));
register_on_error(std::bind(&LifecycleNodeInterface::on_error, this, std::placeholders::_1));
if (options.enable_logger_service()) {
node_logging_->create_logger_services(node_services_);
}
}
LifecycleNode::~LifecycleNode()

View File

@@ -25,6 +25,8 @@
#include "lifecycle_msgs/msg/transition.hpp"
#include "rcl_lifecycle/rcl_lifecycle.h"
#include "rcl_interfaces/srv/get_logger_levels.hpp"
#include "rcl_interfaces/srv/set_logger_levels.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_lifecycle/lifecycle_node.hpp"
@@ -34,6 +36,8 @@
using lifecycle_msgs::msg::State;
using lifecycle_msgs::msg::Transition;
using namespace std::chrono_literals;
static const std::chrono::nanoseconds DEFAULT_EVENT_TIMEOUT = std::chrono::seconds(3);
static const std::chrono::nanoseconds DEFAULT_EVENT_SLEEP_PERIOD = std::chrono::milliseconds(100);
@@ -249,6 +253,35 @@ TEST_F(TestDefaultStateMachine, empty_initializer_rcl_errors) {
}
}
TEST_F(TestDefaultStateMachine, check_logger_services_exist) {
// Logger level services are disabled
{
rclcpp::NodeOptions options = rclcpp::NodeOptions();
options.enable_logger_service(false);
auto node = std::make_shared<rclcpp_lifecycle::LifecycleNode>(
"test_logger_service", "/test", options);
auto get_client = node->create_client<rcl_interfaces::srv::GetLoggerLevels>(
"/test/test_logger_service/get_logger_levels");
ASSERT_FALSE(get_client->wait_for_service(2s));
auto set_client = node->create_client<rcl_interfaces::srv::SetLoggerLevels>(
"/test/test_logger_service/set_logger_levels");
ASSERT_FALSE(set_client->wait_for_service(2s));
}
// Logger level services are enabled
{
rclcpp::NodeOptions options = rclcpp::NodeOptions();
options.enable_logger_service(true);
auto node = std::make_shared<rclcpp_lifecycle::LifecycleNode>(
"test_logger_service", "/test", options);
auto get_client = node->create_client<rcl_interfaces::srv::GetLoggerLevels>(
"/test/test_logger_service/get_logger_levels");
ASSERT_TRUE(get_client->wait_for_service(2s));
auto set_client = node->create_client<rcl_interfaces::srv::SetLoggerLevels>(
"/test/test_logger_service/set_logger_levels");
ASSERT_TRUE(set_client->wait_for_service(2s));
}
}
TEST_F(TestDefaultStateMachine, trigger_transition) {
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");

View File

@@ -55,12 +55,6 @@ public:
explicit EmptyLifecycleNode(const std::string & node_name, const TimerType & timer_type)
: rclcpp_lifecycle::LifecycleNode(node_name)
{
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options;
publisher_ =
std::make_shared<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>>(
get_node_base_interface().get(), std::string("topic"), rclcpp::QoS(10), options);
add_managed_entity(publisher_);
// For coverage this is being added here
switch (timer_type) {
case TimerType::WALL_TIMER:
@@ -77,14 +71,6 @@ public:
}
}
}
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>> publisher()
{
return publisher_;
}
private:
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>> publisher_;
};
class TestLifecyclePublisher : public ::testing::TestWithParam<TimerType>
@@ -93,95 +79,103 @@ public:
void SetUp()
{
rclcpp::init(0, nullptr);
node_ = std::make_shared<EmptyLifecycleNode>("node", GetParam());
}
void TearDown()
{
rclcpp::shutdown();
}
protected:
std::shared_ptr<EmptyLifecycleNode> node_;
};
TEST_P(TestLifecyclePublisher, publish_managed_by_node) {
auto node = std::make_shared<EmptyLifecycleNode>("node", GetParam());
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options;
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>> publisher =
node->create_publisher<test_msgs::msg::Empty>(std::string("topic"), rclcpp::QoS(10), options);
// transition via LifecycleNode
auto success = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
auto reset_key = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR;
auto ret = reset_key;
EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, node_->get_current_state().id());
node_->trigger_transition(
EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, node->get_current_state().id());
node->trigger_transition(
rclcpp_lifecycle::Transition(Transition::TRANSITION_CONFIGURE), ret);
ASSERT_EQ(success, ret);
ret = reset_key;
node_->trigger_transition(
node->trigger_transition(
rclcpp_lifecycle::Transition(Transition::TRANSITION_ACTIVATE), ret);
ASSERT_EQ(success, ret);
ret = reset_key;
EXPECT_TRUE(node_->publisher()->is_activated());
EXPECT_TRUE(publisher->is_activated());
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr));
EXPECT_NO_THROW(publisher->publish(*msg_ptr));
}
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
auto loaned_msg = publisher->borrow_loaned_message();
EXPECT_NO_THROW(publisher->publish(std::move(loaned_msg)));
}
node_->trigger_transition(
node->trigger_transition(
rclcpp_lifecycle::Transition(Transition::TRANSITION_DEACTIVATE), ret);
ASSERT_EQ(success, ret);
ret = reset_key;
(void)ret; // Just to make clang happy
EXPECT_FALSE(node_->publisher()->is_activated());
EXPECT_FALSE(publisher->is_activated());
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr));
EXPECT_NO_THROW(publisher->publish(*msg_ptr));
}
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
auto loaned_msg = publisher->borrow_loaned_message();
EXPECT_NO_THROW(publisher->publish(std::move(loaned_msg)));
}
}
TEST_P(TestLifecyclePublisher, publish) {
auto node = std::make_shared<EmptyLifecycleNode>("node", GetParam());
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options;
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>> publisher =
node->create_publisher<test_msgs::msg::Empty>(std::string("topic"), rclcpp::QoS(10), options);
// transition via LifecyclePublisher
node_->publisher()->on_deactivate();
EXPECT_FALSE(node_->publisher()->is_activated());
publisher->on_deactivate();
EXPECT_FALSE(publisher->is_activated());
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr));
EXPECT_NO_THROW(publisher->publish(*msg_ptr));
}
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
auto loaned_msg = publisher->borrow_loaned_message();
EXPECT_NO_THROW(publisher->publish(std::move(loaned_msg)));
}
node_->publisher()->on_activate();
EXPECT_TRUE(node_->publisher()->is_activated());
publisher->on_activate();
EXPECT_TRUE(publisher->is_activated());
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr));
EXPECT_NO_THROW(publisher->publish(*msg_ptr));
}
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
auto loaned_msg = publisher->borrow_loaned_message();
EXPECT_NO_THROW(publisher->publish(std::move(loaned_msg)));
}
}