Compare commits

...

26 Commits

Author SHA1 Message Date
Alejandro Hernandez Cordero
f160024ba4 30.1.3 2025-11-18 11:09:53 +01:00
Alejandro Hernandez Cordero
e6ea37ed5a Changelog
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-11-18 11:09:49 +01:00
Yuchen966
354413c060 correct test function descriptions (#2970)
Signed-off-by: Yuchen Liu <lyuchen9696@gmail.com>
2025-11-17 15:51:23 +01:00
Minju, Lee
63bdf2add4 add : get clients, servers info (#2569)
Signed-off-by: Minju, Lee <dlalswn531@naver.com>
2025-11-17 13:43:50 +01:00
Tim Clephas
ad019b9827 Fix REP url locations (#2987)
This popped up in my global replace

Signed-off-by: Tim Clephas <tim.clephas@nobleo.nl>
2025-11-17 10:01:03 +01:00
Zheng Qu
eb49444c32 Add get_parameter_or overload returning value or alternative (#2973)
Signed-off-by: Zheng Qu <quzhengrobot@gmail.com>
2025-11-07 12:24:01 +01:00
Alejandro Hernandez Cordero
4d14414d91 30.1.2 2025-10-21 11:43:39 +02:00
Alejandro Hernandez Cordero
7fcc7e723c Changelog
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-10-21 11:43:35 +02:00
Tomoya Fujita
db4f9c21de clear handles before node destruction in test_memory_strategy. (#2969)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-10-21 08:50:48 +09:00
Ivo Ivanov
8499404709 Added static assert asserting custom types have no overloaded operator new (#2954)
Signed-off-by: Ivo Ivanov <ivo.ivanov@tha.de>
2025-10-20 09:14:36 +02:00
Skyler Medeiros
715a983c9f Store graph listener inside the context instead of the node graph (#2952)
* factor shutdown hook registration into its own method

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* run ament_uncrustify

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* consistent use of is_shutdown in graph-listener

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* this doesn't actually need to be a separate function

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* whitespace

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* fix typo

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* move graph listener into the context

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* make cpplint happy

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* Update rclcpp/src/rclcpp/context.cpp

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

---------

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>
2025-10-09 10:09:39 +09:00
Tomoya Fujita
cd9099d85b Reapply "Catch the exception from rate.sleep() if the context is invalid. (#2956)" (#2963) (#2964)
This reverts commit 6fbf03945f.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-10-06 17:12:43 +09:00
Tomoya Fujita
6fbf03945f Revert "Catch the exception from rate.sleep() if the context is invalid. (#2956)" (#2963)
This reverts commit 10f0186c6d.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-10-06 11:22:29 +09:00
Tomoya Fujita
10f0186c6d Catch the exception from rate.sleep() if the context is invalid. (#2956)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-10-06 11:16:59 +09:00
Tomoya Fujita
aa60fcf22a it misses the iterator second to lock the weakptr. (#2958)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-09-30 07:56:36 +09:00
Tomoya Fujita
b14af74a4c try aborting before canceling 1st on dtor of ServerGoalHandle. (#2953)
* try aborting before canceling 1st on dtor of ServerGoalHandle.

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

* address review comments from Copilot AI.

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

* add missing header file and descriptions in docsection.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-09-16 14:06:05 +09:00
Ilario A. Azzollini
38f4073474 update Time documentation (#2955)
Signed-off-by: ilarioazzollini <ilario.azzollini92@gmail.com>
2025-09-16 06:43:19 +02:00
Alejandro Hernandez Cordero
380866b338 30.1.1 2025-09-11 15:10:23 +02:00
Alejandro Hernandez Cordero
150a0e8ad9 Changelog
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-09-11 15:10:16 +02:00
Alejandro Hernández Cordero
b432df117a Removed warning (#2949)
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-09-11 10:57:56 +09:00
Alberto Soragna
3644195892 add note about problems with spin_until_future_complete (#2849)
* add note indicating that spin_until_future_complete should be used only with futures generated by ROS entities

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* fix indentation and improve explanation of consequences

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

---------

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2025-08-26 14:36:02 +02:00
Alberto Soragna
bdab46d64e deprecate rclcpp::spin_some and rclcpp::spin_all (#2848)
* reorder events executor constructor arguments to have the first args match the other executors

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* deprecate rclcpp::spin_some and rclcpp::spin_all

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* use rcpputils to ignore deprecation and remove deprecated APIs from comments

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* fix rclcpp_lifecycle build failures.

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

* make linters happy

Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>

* fix test_service_introspection

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* fix lifecycle unit-test

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

---------

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-08-26 19:26:34 +09:00
Barry Xu
82696f6e57 Improve the function extract_type_identifier (#2923)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2025-08-18 20:00:45 +02:00
Tim Clephas
e615c7c309 Allow for implicitly convertable loggers as well (#2922)
* Allow for implicitly convertable loggers as well

Signed-off-by: Tim Clephas <tim.clephas@nobleo.nl>

* Test implicitly convertable logger

Signed-off-by: Tim Clephas <tim.clephas@nobleo.nl>

* This can be simplified now

Signed-off-by: Tim Clephas <tim.clephas@nobleo.nl>

* fixup! This can be simplified now

Signed-off-by: Tim Clephas <tim.clephas@nobleo.nl>

---------

Signed-off-by: Tim Clephas <tim.clephas@nobleo.nl>
2025-08-13 10:46:44 +02:00
Peter Mitrano (AR)
37677791ca Clearer warning message, the old one lacked information and was perhaps misleading (#2927)
* change misleading warning message, making it more correct and informative

Signed-off-by: Peter Mitrano (AR) <peter.mitrano@agile-robots.com>

* Fix compile error. Needed to also build rcl from source.

Signed-off-by: Peter Mitrano (AR) <peter.mitrano@agile-robots.com>

* explicitely initialize pointer as null, to adhere to best practice

Signed-off-by: Peter Mitrano (AR) <peter.mitrano@agile-robots.com>

---------

Signed-off-by: Peter Mitrano (AR) <peter.mitrano@agile-robots.com>
2025-08-12 15:01:53 +02:00
Chris Lalancette
a919a6e5ed Cleanup the dependencies in rclcpp_components. (#2918)
The most important change in here is the changes to the package.xml
and the CMakeLists.txt, which now properly export the dependencies
to downstream packages as required.  On those two fronts:

1.  Make sure to add a dependency on rmw, which this package does depend
    on for rmw_request_id_t
2. Make sure to add a dependency on rcl_interfaces, which this package
   also depends on.
3. Export depend class_loader, composition_interfaces, rclcpp,
   rcpputils, and rmw, all of which are exported in the header files.
4. Remove the unnecessary test dependencies on launch_testing and
   std_msgs, neither of which is used.

The rest of the change here is to cleanup the header files to include
what you use everywhere.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2025-08-03 21:22:30 -04:00
71 changed files with 1289 additions and 230 deletions

View File

@@ -2,6 +2,33 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30.1.3 (2025-11-18)
-------------------
* correct test function descriptions (`#2970 <https://github.com/ros2/rclcpp/issues/2970>`_)
* add : get clients, servers info (`#2569 <https://github.com/ros2/rclcpp/issues/2569>`_)
* Fix REP url locations (`#2987 <https://github.com/ros2/rclcpp/issues/2987>`_)
* Contributors: Minju, Lee, Tim Clephas, Yuchen966
30.1.2 (2025-10-21)
-------------------
* clear handles before node destruction in test_memory_strategy. (`#2969 <https://github.com/ros2/rclcpp/issues/2969>`_)
* Added static assert asserting custom types have no overloaded operator new (`#2954 <https://github.com/ros2/rclcpp/issues/2954>`_)
* Store graph listener inside the context instead of the node graph (`#2952 <https://github.com/ros2/rclcpp/issues/2952>`_)
* Reapply "Catch the exception from rate.sleep() if the context is invalid. (`#2956 <https://github.com/ros2/rclcpp/issues/2956>`_)" (`#2963 <https://github.com/ros2/rclcpp/issues/2963>`_) (`#2964 <https://github.com/ros2/rclcpp/issues/2964>`_)
* Revert "Catch the exception from rate.sleep() if the context is invalid. (`#2956 <https://github.com/ros2/rclcpp/issues/2956>`_)" (`#2963 <https://github.com/ros2/rclcpp/issues/2963>`_)
* Catch the exception from rate.sleep() if the context is invalid. (`#2956 <https://github.com/ros2/rclcpp/issues/2956>`_)
* update Time documentation (`#2955 <https://github.com/ros2/rclcpp/issues/2955>`_)
* Contributors: Ilario A. Azzollini, Ivo Ivanov, Skyler Medeiros, Tomoya Fujita
30.1.1 (2025-09-11)
-------------------
* Removed warning (`#2949 <https://github.com/ros2/rclcpp/issues/2949>`_)
* add note about problems with spin_until_future_complete (`#2849 <https://github.com/ros2/rclcpp/issues/2849>`_)
* deprecate rclcpp::spin_some and rclcpp::spin_all (`#2848 <https://github.com/ros2/rclcpp/issues/2848>`_)
* Improve the function extract_type_identifier (`#2923 <https://github.com/ros2/rclcpp/issues/2923>`_)
* Allow for implicitly convertable loggers as well (`#2922 <https://github.com/ros2/rclcpp/issues/2922>`_)
* Contributors: Alberto Soragna, Alejandro Hernández Cordero, Barry Xu, Tim Clephas
30.1.0 (2025-07-29)
-------------------
* Fix: improve exception context for parameter_value_from (`#2917 <https://github.com/ros2/rclcpp/issues/2917>`_)

View File

@@ -1,10 +1,10 @@
This document is a declaration of software quality for the `rclcpp` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html).
This document is a declaration of software quality for the `rclcpp` package, based on the guidelines in [REP-2004](https://reps.openrobotics.org/rep-2004/).
# rclcpp Quality Declaration
The package `rclcpp` claims to be in the **Quality Level 1** category when it is used with a **Quality Level 1** middleware.
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://www.ros.org/reps/rep-2004.html).
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://reps.openrobotics.org/rep-2004/).
## Version Policy [1]
@@ -55,7 +55,7 @@ All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://d
### Continuous Integration [2.iv]
All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers)
All pull requests must pass CI on all [tier 1 platforms](https://reps.openrobotics.org/rep-2000/#support-tiers)
Currently nightly results can be seen here:
@@ -213,7 +213,7 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://github
## Platform Support [6]
`rclcpp` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them.
`rclcpp` supports all of the tier 1 platforms as described in [REP-2000](https://reps.openrobotics.org/rep-2000/#support-tiers), and tests each change against all of them.
Currently nightly build status can be seen here:
* [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/rclcpp/)
@@ -225,4 +225,4 @@ Currently nightly build status can be seen here:
### Vulnerability Disclosure Policy [7.i]
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html).
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://reps.openrobotics.org/rep-2006/).

View File

@@ -37,6 +37,10 @@
namespace rclcpp
{
namespace graph_listener
{
class GraphListener;
} // namespace graph_listener
/// Thrown when init is called on an already initialized context.
class ContextAlreadyInitialized : public std::runtime_error
@@ -309,6 +313,10 @@ public:
std::shared_ptr<rcl_context_t>
get_rcl_context();
RCLCPP_PUBLIC
std::shared_ptr<rclcpp::graph_listener::GraphListener>
get_graph_listener();
/// Sleep for a given period of time or until shutdown() is called.
/**
* This function can be interrupted early if:
@@ -391,6 +399,9 @@ private:
/// Mutex for protecting the global condition variable.
std::mutex interrupt_mutex_;
/// Graph Listener which waits on graph changes for the node and is shared across nodes.
std::shared_ptr<rclcpp::graph_listener::GraphListener> graph_listener_;
/// Keep shared ownership of global vector of weak contexts
std::shared_ptr<WeakContextsWrapper> weak_contexts_;

View File

@@ -371,6 +371,9 @@ public:
* If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return
* code.
* \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
* \note This method will check the future and the timeout only when the executor is woken up.
* If this future is unrelated to an executor's entity, this method will not correctly detect
* when it's completed and therefore may wait forever and never time out.
*/
template<typename FutureT, typename TimeRepT = int64_t, typename TimeT = std::milli>
FutureReturnCode

View File

@@ -28,24 +28,82 @@
namespace rclcpp
{
/// Create a default single-threaded executor and execute all available work exhaustively.
/** \param[in] node_ptr Shared pointer to the node to spin. */
/**
* @brief Create a default single-threaded executor and execute all available work exhaustively.
* @param node_ptr Shared pointer to the base interface of the node to spin.
* @param max_duration max duration to spin
*
* This method is deprecated because it can lead to very bad performance if used in a loop:
* each call will create a new executor and register the node, which is an expensive operation.
* It's recommended to always manually instantiate an executor and call the methods with
* the same name on it.
* For example:
* SingleThreadedExecutor executor;
* executor.add_node(node_ptr);
* executor.spin_all(max_duration);
* If you are using a non-default context, this should be passed to the executor's constructor.
*/
[[deprecated("use SingleThreadedExecutor::spin_all instead")]]
RCLCPP_PUBLIC
void
spin_all(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
std::chrono::nanoseconds max_duration);
/**
* @brief Create a default single-threaded executor and execute all available work exhaustively.
* @param node_ptr Shared pointer to the node to spin.
* @param max_duration max duration to spin
*
* This method is deprecated because it can lead to very bad performance if used in a loop:
* each call will create a new executor and register the node, which is an expensive operation.
* It's recommended to always manually instantiate an executor and call the methods with
* the same name on it.
* For example:
* SingleThreadedExecutor executor;
* executor.add_node(node_ptr);
* executor.spin_all(max_duration);
* If you are using a non-default context, this should be passed to the executor's constructor.
*/
[[deprecated("use SingleThreadedExecutor::spin_all instead")]]
RCLCPP_PUBLIC
void
spin_all(rclcpp::Node::SharedPtr node_ptr, std::chrono::nanoseconds max_duration);
/// Create a default single-threaded executor and execute any immediately available work.
/** \param[in] node_ptr Shared pointer to the node to spin. */
/**
* @brief Create a default single-threaded executor and execute any immediately available work.
* @param node_ptr Shared pointer to the base interface of the node to spin.
*
* This method is deprecated because it can lead to very bad performance if used in a loop:
* each call will create a new executor and register the node, which is an expensive operation.
* It's recommended to always manually instantiate an executor and call the methods with
* the same name on it.
* For example:
* SingleThreadedExecutor executor;
* executor.add_node(node_ptr);
* executor.spin_some();
* If you are using a non-default context, this should be passed to the executor's constructor.
*/
[[deprecated("use SingleThreadedExecutor::spin_some instead")]]
RCLCPP_PUBLIC
void
spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
/**
* @brief Create a default single-threaded executor and execute any immediately available work.
* @param node_ptr Shared pointer to the node to spin.
*
* This method is deprecated because it can lead to very bad performance if used in a loop:
* each call will create a new executor and register the node, which is an expensive operation.
* It's recommended to always manually instantiate an executor and call the methods with
* the same name on it.
* For example:
* SingleThreadedExecutor executor;
* executor.add_node(node_ptr);
* executor.spin_some();
* If you are using a non-default context, this should be passed to the executor's constructor.
*/
[[deprecated("use SingleThreadedExecutor::spin_some instead")]]
RCLCPP_PUBLIC
void
spin_some(rclcpp::Node::SharedPtr node_ptr);

View File

@@ -64,17 +64,17 @@ public:
/// Default constructor. See the default constructor for Executor.
/**
* \param[in] options Options used to configure the executor.
* \param[in] events_queue The queue used to store events.
* \param[in] execute_timers_separate_thread If true, timers are executed in a separate
* thread. If false, timers are executed in the same thread as all other entities.
* \param[in] options Options used to configure the executor.
*/
RCLCPP_PUBLIC
EventsExecutor(
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions(),
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue = std::make_unique<
rclcpp::experimental::executors::SimpleEventsQueue>(),
bool execute_timers_separate_thread = false,
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions());
bool execute_timers_separate_thread = false);
/// Default destructor.
RCLCPP_PUBLIC

View File

@@ -154,6 +154,12 @@ public:
bool
is_shutdown();
/// Return true if the graph listener was started.
RCLCPP_PUBLIC
virtual
bool
is_started();
protected:
/// Main function for the listening thread.
RCLCPP_PUBLIC
@@ -181,7 +187,6 @@ private:
void
__shutdown();
std::weak_ptr<rclcpp::Context> weak_parent_context_;
std::shared_ptr<rcl_context_t> rcl_parent_context_;
std::thread listener_thread_;

View File

@@ -32,9 +32,7 @@
#define RCLCPP_STATIC_ASSERT_LOGGER(logger) \
do { \
static_assert( \
::std::is_same<typename std::remove_cv_t< \
typename std::remove_reference_t<decltype(logger)>>, \
typename ::rclcpp::Logger>::value, \
::std::is_convertible_v<decltype(logger), ::rclcpp::Logger>, \
"First argument to logging macros must be an rclcpp::Logger"); \
} while (0)

View File

@@ -1388,6 +1388,66 @@ public:
std::vector<rclcpp::TopicEndpointInfo>
get_subscriptions_info_by_topic(const std::string & topic_name, bool no_mangle = false) const;
/// Return the service endpoint information about clients on a given service.
/**
* The returned parameter is a list of service endpoint information, where each item will contain
* the node name, node namespace, service type, endpoint type, endpoint count,
* service endpoint's GIDs, and its QoS profiles.
*
* When the `no_mangle` parameter is `true`, the provided `service_name` should be a valid service
* name for the middleware (useful when combining ROS with native middleware apps).
* When the `no_mangle` parameter is `false`, the provided `service_name` should follow
* ROS service name conventions. In DDS-based RMWs, services are implemented as topics with mangled
* names (e.g., `rq/my_serviceRequest` and `rp/my_serviceReply`), so `no_mangle = true` is not
* supported and will result in an error. Use `get_publishers_info_by_topic` or
* `get_subscriptions_info_by_topic` for unmangled topic queries in such cases. Other RMWs
* (e.g., Zenoh) may support `no_mangle = true` if they natively handle services without topic-based
*
* 'service_name` may be a relative, private, or fully qualified service name.
* A relative or private service will be expanded using this node's namespace and name.
* The queried `service_name` is not remapped.
*
* \param[in] service_name the actual service name used; it will not be automatically remapped.
* \param[in] no_mangle if `true`, `service_name` needs to be a valid middleware service name,
* otherwise it should be a valid ROS service name. Defaults to `false`.
* \return a list of SeviceEndpointInfo representing all the clients on this service.
* \throws InvalidServiceNameError if the given service_name is invalid.
* \throws std::runtime_error if internal error happens.
*/
RCLCPP_PUBLIC
std::vector<rclcpp::ServiceEndpointInfo>
get_clients_info_by_service(const std::string & service_name, bool no_mangle = false) const;
/// Return the service endpoint information about servers on a given service.
/**
* The returned parameter is a list of service endpoint information, where each item will contain
* the node name, node namespace, service type, endpoint type, endpoint count,
* service endpoint's GIDs, and its QoS profiles.
*
* When the `no_mangle` parameter is `true`, the provided `service_name` should be a valid service
* name for the middleware (useful when combining ROS with native middleware apps).
* When the `no_mangle` parameter is `false`, the provided `service_name` should follow
* ROS service name conventions. In DDS-based RMWs, services are implemented as topics with mangled
* names (e.g., `rq/my_serviceRequest` and `rp/my_serviceReply`), so `no_mangle = true` is not
* supported and will result in an error. Use `rcl_get_publishers_info_by_topic` or
* `rcl_get_subscriptions_info_by_topic` for unmangled topic queries in such cases. Other RMWs
* (e.g., Zenoh) may support `no_mangle = true` if they natively handle services without topic-based
*
* 'service_name` may be a relative, private, or fully qualified service name.
* A relative or private service will be expanded using this node's namespace and name.
* The queried `service_name` is not remapped.
*
* \param[in] service_name the actual service name used; it will not be automatically remapped.
* \param[in] no_mangle if `true`, `service_name` needs to be a valid middleware service name,
* otherwise it should be a valid ROS service name. Defaults to `false`.
* \return a list of SeviceEndpointInfo representing all the servers on this service.
* \throws InvalidServiceNameError if the given service_name is invalid.
* \throws std::runtime_error if internal error happens.
*/
RCLCPP_PUBLIC
std::vector<rclcpp::ServiceEndpointInfo>
get_servers_info_by_service(const std::string & service_name, bool no_mangle = false) const;
/// Return a graph event, which will be set anytime a graph change occurs.
/* The graph Event object is a loan which must be returned.
* The Event object is scoped and therefore to return the loan just let it go

View File

@@ -33,6 +33,7 @@
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/service_endpoint_info_array.h"
#include "rmw/topic_endpoint_info_array.h"
namespace rclcpp
@@ -159,14 +160,24 @@ public:
const std::string & topic_name,
bool no_mangle = false) const override;
RCLCPP_PUBLIC
std::vector<rclcpp::ServiceEndpointInfo>
get_clients_info_by_service(
const std::string & service_name,
bool no_mangle = false) const override;
RCLCPP_PUBLIC
std::vector<rclcpp::ServiceEndpointInfo>
get_servers_info_by_service(
const std::string & service_name,
bool no_mangle = false) const override;
private:
RCLCPP_DISABLE_COPY(NodeGraph)
/// Handle to the NodeBaseInterface given in the constructor.
rclcpp::node_interfaces::NodeBaseInterface * node_base_;
/// Graph Listener which waits on graph changes for the node and is shared across nodes.
std::shared_ptr<rclcpp::graph_listener::GraphListener> graph_listener_;
/// Whether or not this node needs to be added to the graph listener.
std::atomic_bool should_add_to_graph_listener_;

View File

@@ -18,6 +18,8 @@
#include <algorithm>
#include <array>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <map>
#include <string>
#include <tuple>
@@ -40,7 +42,9 @@ enum class EndpointType
{
Invalid = RMW_ENDPOINT_INVALID,
Publisher = RMW_ENDPOINT_PUBLISHER,
Subscription = RMW_ENDPOINT_SUBSCRIPTION
Subscription = RMW_ENDPOINT_SUBSCRIPTION,
Client = RMW_ENDPOINT_CLIENT,
Server = RMW_ENDPOINT_SERVER
};
/**
@@ -143,6 +147,125 @@ private:
rosidl_type_hash_t topic_type_hash_;
};
/**
* Struct that contains service endpoint information like the associated node name, node namespace,
* service type, endpoint type, endpoint count, endpoint GIDs, and its QoS profiles.
*/
class ServiceEndpointInfo
{
public:
/// Construct a ServiceEndpointInfo from a rcl_service_endpoint_info_t.
RCLCPP_PUBLIC
explicit ServiceEndpointInfo(const rcl_service_endpoint_info_t & info)
: node_name_(info.node_name),
node_namespace_(info.node_namespace),
service_type_(info.service_type),
endpoint_type_(static_cast<rclcpp::EndpointType>(info.endpoint_type)),
service_type_hash_(info.service_type_hash),
endpoint_count_(info.endpoint_count)
{
for(size_t i = 0; i < endpoint_count_; i++) {
std::array<uint8_t, RMW_GID_STORAGE_SIZE> gid;
std::copy(info.endpoint_gids[i], info.endpoint_gids[i] + RMW_GID_STORAGE_SIZE, gid.begin());
endpoint_gids_.push_back(gid);
rclcpp::QoS qos(
{info.qos_profiles[i].history, info.qos_profiles[i].depth}, info.qos_profiles[i]);
qos_profiles_.push_back(qos);
}
}
/// Get a mutable reference to the node name.
RCLCPP_PUBLIC
std::string &
node_name();
/// Get a const reference to the node name.
RCLCPP_PUBLIC
const std::string &
node_name() const;
/// Get a mutable reference to the node namespace.
RCLCPP_PUBLIC
std::string &
node_namespace();
/// Get a const reference to the node namespace.
RCLCPP_PUBLIC
const std::string &
node_namespace() const;
/// Get a mutable reference to the service type string.
RCLCPP_PUBLIC
std::string &
service_type();
/// Get a const reference to the service type string.
RCLCPP_PUBLIC
const std::string &
service_type() const;
/// Get a mutable reference to the service endpoint type.
RCLCPP_PUBLIC
rclcpp::EndpointType &
endpoint_type();
/// Get a const reference to the service endpoint type.
RCLCPP_PUBLIC
const rclcpp::EndpointType &
endpoint_type() const;
/// Get a mutable reference to the endpoint count.
RCLCPP_PUBLIC
size_t &
endpoint_count();
/// Get a const reference to the endpoint count.
RCLCPP_PUBLIC
const size_t &
endpoint_count() const;
/// Get a mutable reference to the GID of the service endpoint.
RCLCPP_PUBLIC
std::vector<std::array<uint8_t, RMW_GID_STORAGE_SIZE>> &
endpoint_gids();
/// Get a const reference to the GID of the service endpoint.
RCLCPP_PUBLIC
const std::vector<std::array<uint8_t, RMW_GID_STORAGE_SIZE>> &
endpoint_gids() const;
/// Get a mutable reference to the QoS profile of the service endpoint.
RCLCPP_PUBLIC
std::vector<rclcpp::QoS> &
qos_profiles();
/// Get a const reference to the QoS profile of the service endpoint.
RCLCPP_PUBLIC
const std::vector<rclcpp::QoS> &
qos_profiles() const;
/// Get a mutable reference to the type hash of the service endpoint.
RCLCPP_PUBLIC
rosidl_type_hash_t &
service_type_hash();
/// Get a const reference to the type hash of the service endpoint.
RCLCPP_PUBLIC
const rosidl_type_hash_t &
service_type_hash() const;
private:
std::string node_name_;
std::string node_namespace_;
std::string service_type_;
rclcpp::EndpointType endpoint_type_;
std::vector<std::array<uint8_t, RMW_GID_STORAGE_SIZE>> endpoint_gids_;
std::vector<rclcpp::QoS> qos_profiles_;
rosidl_type_hash_t service_type_hash_;
size_t endpoint_count_;
};
namespace node_interfaces
{
@@ -408,6 +531,30 @@ public:
virtual
std::vector<rclcpp::TopicEndpointInfo>
get_subscriptions_info_by_topic(const std::string & topic_name, bool no_mangle = false) const = 0;
/// Return the service endpoint information about clients on a given service.
/**
* \param[in] service_name the actual service name used; it will not be automatically remapped.
* \param[in] no_mangle if `true`, `service_name` needs to be a valid middleware service name,
* otherwise it should be a valid ROS service name.
* \sa rclcpp::Node::get_clients_info_by_service
*/
RCLCPP_PUBLIC
virtual
std::vector<rclcpp::ServiceEndpointInfo>
get_clients_info_by_service(const std::string & service_name, bool no_mangle = false) const = 0;
/// Return the service endpoint information about servers on a given service.
/**
* \param[in] service_name the actual service name used; it will not be automatically remapped.
* \param[in] no_mangle if `true`, `service_name` needs to be a valid middleware service name,
* otherwise it should be a valid ROS service name.
* \sa rclcpp::Node::get_servers_info_by_service
*/
RCLCPP_PUBLIC
virtual
std::vector<rclcpp::ServiceEndpointInfo>
get_servers_info_by_service(const std::string & service_name, bool no_mangle = false) const = 0;
};
} // namespace node_interfaces

View File

@@ -574,6 +574,13 @@ protected:
std::unique_ptr<PublishedType, PublishedTypeDeleter>
duplicate_type_adapt_message_as_unique_ptr(const PublishedType & msg)
{
/// Assert that the published type has no overloaded operator new since this leads to
/// new/delete mismatch (see https://github.com/ros2/rclcpp/issues/2951)
static_assert(!detail::has_overloaded_operator_new_v<PublishedType>,
"When publishing by value (i.e. when calling publish(const T& msg)), the published "
"message type must not have an overloaded operator new. In this case, please use the "
"publish(std::unique_ptr<T> msg) method instead.");
auto ptr = PublishedTypeAllocatorTraits::allocate(published_type_allocator_, 1);
PublishedTypeAllocatorTraits::construct(published_type_allocator_, ptr, msg);
return std::unique_ptr<PublishedType, PublishedTypeDeleter>(ptr, published_type_deleter_);

View File

@@ -68,8 +68,6 @@
*
* - Executors (responsible for execution of callbacks through a blocking spin):
* - rclcpp::spin()
* - rclcpp::spin_some()
* - rclcpp::spin_until_future_complete()
* - rclcpp::executors::SingleThreadedExecutor
* - rclcpp::executors::SingleThreadedExecutor::add_node()
* - rclcpp::executors::SingleThreadedExecutor::spin()

View File

@@ -33,12 +33,11 @@ class Time
public:
/// Time constructor
/**
* Initializes the time values for seconds and nanoseconds individually.
* Large values for nanoseconds are wrapped automatically with the remainder added to seconds.
* Both inputs must be integers.
* Indicates a specific point in time, relative to a clock's 0 point (its epoch).
* The total time since the epoch is given by seconds + nanoseconds.
*
* \param seconds part of the time in seconds since time epoch
* \param nanoseconds part of the time in nanoseconds since time epoch
* \param seconds the seconds component, valid only if positive
* \param nanoseconds the nanoseconds component, to be added to the seconds component
* \param clock_type clock type
* \throws std::runtime_error if seconds are negative
*/
@@ -47,7 +46,7 @@ public:
/// Time constructor
/**
* \param nanoseconds since time epoch
* \param nanoseconds the total time since the epoch in nanoseconds
* \param clock_type clock type
* \throws std::runtime_error if nanoseconds are negative
*/
@@ -190,7 +189,7 @@ public:
/// Get the nanoseconds since epoch
/**
* \return the nanoseconds since epoch as a rcl_time_point_value_t structure.
* \return the total time since the epoch in nanoseconds, as a rcl_time_point_value_t structure.
*/
RCLCPP_PUBLIC
rcl_time_point_value_t
@@ -209,7 +208,7 @@ public:
* \warning Depending on sizeof(double) there could be significant precision loss.
* When an exact time is required use nanoseconds() instead.
*
* \return the seconds since epoch as a floating point number.
* \return the total time since the epoch in seconds, as a floating point number.
*/
RCLCPP_PUBLIC
double

View File

@@ -16,6 +16,7 @@
#define RCLCPP__TYPE_ADAPTER_HPP_
#include <type_traits>
#include <new>
namespace rclcpp
{
@@ -128,6 +129,24 @@ struct assert_type_pair_is_specialized_type_adapter
"No type adapter for this custom type/ros message type pair");
};
template<typename, typename = void>
struct has_overloaded_operator_new : std::false_type {};
template<typename T>
struct has_overloaded_operator_new<T, std::void_t<
decltype(T::operator new(std::size_t()))
>>: std::true_type {};
template<typename, typename = void>
struct has_overloaded_aligned_operator_new : std::false_type {};
template<typename T>
struct has_overloaded_aligned_operator_new<T,
std::void_t<decltype( T::operator new(std::size_t(), std::align_val_t()) )>>
: std::true_type {};
template<typename T>
inline constexpr bool has_overloaded_operator_new_v = has_overloaded_operator_new<T>::value ||
has_overloaded_aligned_operator_new<T>::value;
} // namespace detail
/// Template metafunction that can make the type being adapted explicit.

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

View File

@@ -28,7 +28,7 @@
#include "rclcpp/detail/utilities.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/graph_listener.hpp"
#include "rcutils/error_handling.h"
#include "rcutils/macros.h"
@@ -145,7 +145,8 @@ rclcpp_logging_output_handler(
Context::Context()
: rcl_context_(nullptr),
shutdown_reason_(""),
logging_mutex_(nullptr)
logging_mutex_(nullptr),
graph_listener_(nullptr)
{}
Context::~Context()
@@ -243,6 +244,24 @@ Context::init(
weak_contexts_ = get_weak_contexts();
weak_contexts_->add_context(this->shared_from_this());
std::lock_guard<std::recursive_mutex> lock (on_shutdown_callbacks_mutex_);
graph_listener_ = std::make_shared<graph_listener::GraphListener>(shared_from_this());
if (!graph_listener_->is_started()) {
// Register an on_shutdown hook to shutdown the graph listener.
// This is important to ensure that the wait set is finalized before
// destruction of static objects occurs.
std::weak_ptr<rclcpp::graph_listener::GraphListener> weak_graph_listener = graph_listener_;
on_shutdown ([weak_graph_listener]() {
auto shared_graph_listener = weak_graph_listener.lock();
if(shared_graph_listener) {
shared_graph_listener->shutdown(std::nothrow);
}
});
}
} catch (const std::exception & e) {
ret = rcl_shutdown(rcl_context_.get());
rcl_context_.reset();
@@ -500,6 +519,12 @@ Context::get_rcl_context()
return rcl_context_;
}
std::shared_ptr<rclcpp::graph_listener::GraphListener>
Context::get_graph_listener()
{
return graph_listener_;
}
bool
Context::sleep_for(const std::chrono::nanoseconds & nanoseconds)
{

View File

@@ -18,6 +18,7 @@
#include <iterator>
#include <memory>
#include <map>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
@@ -52,6 +53,7 @@ class rclcpp::ExecutorImplementation {};
Executor::Executor(const std::shared_ptr<rclcpp::Context> & context)
: spinning(false),
context_(context),
entities_need_rebuild_(true),
collector_(nullptr),
wait_set_({}, {}, {}, {}, {}, {}, context)
@@ -186,6 +188,11 @@ Executor::add_callback_group(
void
Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
{
if (node_ptr->get_context() != context_) {
throw std::runtime_error(
"add_node() called with a node with a different context from this executor");
}
this->collector_.add_node(node_ptr);
try {

View File

@@ -13,6 +13,7 @@
// limitations under the License.
#include "rclcpp/executors.hpp"
#include "rcpputils/compile_warnings.hpp"
void
rclcpp::spin_all(
@@ -25,12 +26,6 @@ rclcpp::spin_all(
exec.spin_node_all(node_ptr, max_duration);
}
void
rclcpp::spin_all(rclcpp::Node::SharedPtr node_ptr, std::chrono::nanoseconds max_duration)
{
rclcpp::spin_all(node_ptr->get_node_base_interface(), max_duration);
}
void
rclcpp::spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
{
@@ -40,12 +35,6 @@ rclcpp::spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr
exec.spin_node_some(node_ptr);
}
void
rclcpp::spin_some(rclcpp::Node::SharedPtr node_ptr)
{
rclcpp::spin_some(node_ptr->get_node_base_interface());
}
void
rclcpp::spin(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
{
@@ -62,3 +51,19 @@ rclcpp::spin(rclcpp::Node::SharedPtr node_ptr)
{
rclcpp::spin(node_ptr->get_node_base_interface());
}
void
rclcpp::spin_all(rclcpp::Node::SharedPtr node_ptr, std::chrono::nanoseconds max_duration)
{
RCPPUTILS_DEPRECATION_WARNING_OFF_START
rclcpp::spin_all(node_ptr->get_node_base_interface(), max_duration);
RCPPUTILS_DEPRECATION_WARNING_OFF_STOP
}
void
rclcpp::spin_some(rclcpp::Node::SharedPtr node_ptr)
{
RCPPUTILS_DEPRECATION_WARNING_OFF_START
rclcpp::spin_some(node_ptr->get_node_base_interface());
RCPPUTILS_DEPRECATION_WARNING_OFF_STOP
}

View File

@@ -25,9 +25,9 @@ using namespace std::chrono_literals;
using rclcpp::experimental::executors::EventsExecutor;
EventsExecutor::EventsExecutor(
const rclcpp::ExecutorOptions & options,
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue,
bool execute_timers_separate_thread,
const rclcpp::ExecutorOptions & options)
bool execute_timers_separate_thread)
: rclcpp::Executor(options)
{
// Get ownership of the queue used to store events.

View File

@@ -19,7 +19,6 @@
#include <memory>
#include <string>
#include <vector>
#include "rcl/error_handling.h"
#include "rcl/types.h"
#include "rclcpp/detail/add_guard_condition_to_rcl_wait_set.hpp"
@@ -38,8 +37,7 @@ namespace graph_listener
{
GraphListener::GraphListener(const std::shared_ptr<Context> & parent_context)
: weak_parent_context_(parent_context),
rcl_parent_context_(parent_context->get_rcl_context()),
: rcl_parent_context_(parent_context->get_rcl_context()),
is_started_(false),
is_shutdown_(false),
interrupt_guard_condition_(parent_context)
@@ -72,23 +70,11 @@ void
GraphListener::start_if_not_started()
{
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
if (is_shutdown_.load()) {
if (is_shutdown()) {
throw GraphListenerShutdownError();
}
auto parent_context = weak_parent_context_.lock();
if (!is_started_ && parent_context) {
// Register an on_shutdown hook to shtudown the graph listener.
// This is important to ensure that the wait set is finalized before
// destruction of static objects occurs.
std::weak_ptr<GraphListener> weak_this = shared_from_this();
parent_context->on_shutdown(
[weak_this]() {
auto shared_this = weak_this.lock();
if (shared_this) {
// should not throw from on_shutdown if it can be avoided
shared_this->shutdown(std::nothrow);
}
});
if (!is_started()) {
// Initialize the wait set before starting.
init_wait_set();
// Start the listener thread.
@@ -122,7 +108,7 @@ GraphListener::run_loop()
{
while (true) {
// If shutdown() was called, exit.
if (is_shutdown_.load()) {
if (is_shutdown()) {
return;
}
rcl_ret_t ret;
@@ -190,7 +176,7 @@ GraphListener::run_loop()
if (graph_gc == wait_set_.guard_conditions[graph_gc_indexes[i]]) {
node_ptr->notify_graph_change();
}
if (is_shutdown_) {
if (is_shutdown()) {
// If shutdown, then notify the node of this as well.
node_ptr->notify_shutdown();
}
@@ -257,7 +243,7 @@ GraphListener::add_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph
throw std::invalid_argument("node is nullptr");
}
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
if (is_shutdown_.load()) {
if (is_shutdown()) {
throw GraphListenerShutdownError();
}
@@ -332,11 +318,11 @@ GraphListener::__shutdown()
{
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
if (!is_shutdown_.exchange(true)) {
if (is_started_) {
if (is_started()) {
interrupt_(&interrupt_guard_condition_);
listener_thread_.join();
}
if (is_started_) {
if (is_started()) {
cleanup_wait_set();
}
}
@@ -365,6 +351,12 @@ GraphListener::shutdown(const std::nothrow_t &) noexcept
}
}
bool
GraphListener::is_started()
{
return is_started_;
}
bool
GraphListener::is_shutdown()
{

View File

@@ -547,6 +547,18 @@ Node::get_subscriptions_info_by_topic(const std::string & topic_name, bool no_ma
return node_graph_->get_subscriptions_info_by_topic(topic_name, no_mangle);
}
std::vector<rclcpp::ServiceEndpointInfo>
Node::get_clients_info_by_service(const std::string & service_name, bool no_mangle) const
{
return node_graph_->get_clients_info_by_service(service_name, no_mangle);
}
std::vector<rclcpp::ServiceEndpointInfo>
Node::get_servers_info_by_service(const std::string & service_name, bool no_mangle) const
{
return node_graph_->get_servers_info_by_service(service_name, no_mangle);
}
void
Node::for_each_callback_group(
const node_interfaces::NodeBaseInterface::CallbackGroupFunction & func)

View File

@@ -15,6 +15,7 @@
#include "rclcpp/node_interfaces/node_graph.hpp"
#include <algorithm>
#include <cstddef>
#include <map>
#include <string>
#include <tuple>
@@ -36,9 +37,6 @@ using rclcpp::graph_listener::GraphListener;
NodeGraph::NodeGraph(rclcpp::node_interfaces::NodeBaseInterface * node_base)
: node_base_(node_base),
graph_listener_(
node_base->get_context()->get_sub_context<GraphListener>(node_base->get_context())
),
should_add_to_graph_listener_(true),
graph_users_count_(0)
{}
@@ -50,7 +48,7 @@ NodeGraph::~NodeGraph()
// graph listener after checking that it was not here.
if (!should_add_to_graph_listener_.exchange(false)) {
// If it was already false, then it needs to now be removed.
graph_listener_->remove_node(this);
node_base_->get_context()->get_graph_listener()->remove_node(this);
}
}
@@ -419,7 +417,7 @@ NodeGraph::get_node_names_and_namespaces() const
rcl_get_error_string().str;
rcl_reset_error();
}
RCUTILS_LOG_ERROR_NAMED("rclcpp", error_msg.c_str());
RCUTILS_LOG_ERROR_NAMED("rclcpp", "%s", error_msg.c_str());
throw std::runtime_error(error_msg);
}
@@ -442,7 +440,7 @@ NodeGraph::get_node_names_and_namespaces() const
}
if (ret_names != RCUTILS_RET_OK || ret_ns != RCUTILS_RET_OK) {
RCUTILS_LOG_ERROR_NAMED("rclcpp", error.c_str());
RCUTILS_LOG_ERROR_NAMED("rclcpp", "%s", error.c_str());
throw std::runtime_error(error);
}
@@ -598,8 +596,8 @@ NodeGraph::get_graph_event()
}
// on first call, add node to graph_listener_
if (should_add_to_graph_listener_.exchange(false)) {
graph_listener_->add_node(this);
graph_listener_->start_if_not_started();
node_base_->get_context()->get_graph_listener()->add_node(this);
node_base_->get_context()->get_graph_listener()->start_if_not_started();
}
return event;
}
@@ -756,6 +754,122 @@ NodeGraph::get_subscriptions_info_by_topic(
rcl_get_subscriptions_info_by_topic);
}
static
std::vector<rclcpp::ServiceEndpointInfo>
convert_to_service_info_list(const rcl_service_endpoint_info_array_t & info_array)
{
std::vector<rclcpp::ServiceEndpointInfo> service_info_list;
for (size_t i = 0; i < info_array.size; ++i) {
service_info_list.push_back(rclcpp::ServiceEndpointInfo(info_array.info_array[i]));
}
return service_info_list;
}
template<const char * EndpointType, typename FunctionT>
static std::vector<rclcpp::ServiceEndpointInfo>
get_info_by_service(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & service_name,
bool no_mangle,
FunctionT rcl_get_info_by_service)
{
std::string fqdn;
auto rcl_node_handle = node_base->get_rcl_node_handle();
if (no_mangle) {
fqdn = service_name;
} else {
fqdn = rclcpp::expand_topic_or_service_name(
service_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
true);
// Get the node options
const rcl_node_options_t * node_options = rcl_node_get_options(rcl_node_handle);
if (nullptr == node_options) {
throw std::runtime_error("Need valid node options in get_info_by_service()");
}
const rcl_arguments_t * global_args = nullptr;
if (node_options->use_global_arguments) {
global_args = &(rcl_node_handle->context->global_arguments);
}
char * remapped_service_name = nullptr;
rcl_ret_t ret = rcl_remap_service_name(
&(node_options->arguments),
global_args,
fqdn.c_str(),
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
node_options->allocator,
&remapped_service_name);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, std::string("Failed to remap service name ") + fqdn);
} else if (nullptr != remapped_service_name) {
fqdn = remapped_service_name;
node_options->allocator.deallocate(remapped_service_name, node_options->allocator.state);
}
}
rcutils_allocator_t allocator = rcutils_get_default_allocator();
rcl_service_endpoint_info_array_t info_array =
rcl_get_zero_initialized_service_endpoint_info_array();
rcl_ret_t ret =
rcl_get_info_by_service(rcl_node_handle, &allocator, fqdn.c_str(), no_mangle, &info_array);
if (RCL_RET_OK != ret) {
auto error_msg =
std::string("Failed to get information by service for ") + EndpointType + std::string(":");
if (RCL_RET_UNSUPPORTED == ret) {
error_msg += std::string("function not supported by RMW_IMPLEMENTATION");
} else {
error_msg += rcl_get_error_string().str;
}
rcl_reset_error();
if (RCL_RET_OK != rcl_service_endpoint_info_array_fini(&info_array, &allocator)) {
error_msg += std::string(", failed also to cleanup service info array, leaking memory: ") +
rcl_get_error_string().str;
rcl_reset_error();
}
throw_from_rcl_error(ret, error_msg);
}
std::vector<rclcpp::ServiceEndpointInfo> service_info_list =
convert_to_service_info_list(info_array);
ret = rcl_service_endpoint_info_array_fini(&info_array, &allocator);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "rcl_service_info_array_fini failed.");
}
return service_info_list;
}
static constexpr char kClientEndpointTypeName[] = "clients";
std::vector<rclcpp::ServiceEndpointInfo>
NodeGraph::get_clients_info_by_service(
const std::string & service_name,
bool no_mangle) const
{
return get_info_by_service<kClientEndpointTypeName>(
node_base_,
service_name,
no_mangle,
rcl_get_clients_info_by_service);
}
static constexpr char kServerEndpointTypeName[] = "servers";
std::vector<rclcpp::ServiceEndpointInfo>
NodeGraph::get_servers_info_by_service(
const std::string & service_name,
bool no_mangle) const
{
return get_info_by_service<kServerEndpointTypeName>(
node_base_,
service_name,
no_mangle,
rcl_get_servers_info_by_service);
}
std::string &
rclcpp::TopicEndpointInfo::node_name()
{
@@ -839,3 +953,99 @@ rclcpp::TopicEndpointInfo::topic_type_hash() const
{
return topic_type_hash_;
}
std::string &
rclcpp::ServiceEndpointInfo::node_name()
{
return node_name_;
}
const std::string &
rclcpp::ServiceEndpointInfo::node_name() const
{
return node_name_;
}
std::string &
rclcpp::ServiceEndpointInfo::node_namespace()
{
return node_namespace_;
}
const std::string &
rclcpp::ServiceEndpointInfo::node_namespace() const
{
return node_namespace_;
}
std::string &
rclcpp::ServiceEndpointInfo::service_type()
{
return service_type_;
}
const std::string &
rclcpp::ServiceEndpointInfo::service_type() const
{
return service_type_;
}
rclcpp::EndpointType &
rclcpp::ServiceEndpointInfo::endpoint_type()
{
return endpoint_type_;
}
const rclcpp::EndpointType &
rclcpp::ServiceEndpointInfo::endpoint_type() const
{
return endpoint_type_;
}
size_t &
rclcpp::ServiceEndpointInfo::endpoint_count()
{
return endpoint_count_;
}
const size_t &
rclcpp::ServiceEndpointInfo::endpoint_count() const
{
return endpoint_count_;
}
std::vector<std::array<uint8_t, RMW_GID_STORAGE_SIZE>> &
rclcpp::ServiceEndpointInfo::endpoint_gids()
{
return endpoint_gids_;
}
const std::vector<std::array<uint8_t, RMW_GID_STORAGE_SIZE>> &
rclcpp::ServiceEndpointInfo::endpoint_gids() const
{
return endpoint_gids_;
}
std::vector<rclcpp::QoS> &
rclcpp::ServiceEndpointInfo::qos_profiles()
{
return qos_profiles_;
}
const std::vector<rclcpp::QoS> &
rclcpp::ServiceEndpointInfo::qos_profiles() const
{
return qos_profiles_;
}
rosidl_type_hash_t &
rclcpp::ServiceEndpointInfo::service_type_hash()
{
return service_type_hash_;
}
const rosidl_type_hash_t &
rclcpp::ServiceEndpointInfo::service_type_hash() const
{
return service_type_hash_;
}

View File

@@ -67,7 +67,13 @@ Rate::sleep()
// Calculate the time to sleep
auto time_to_sleep = next_interval - now;
// Sleep (will get interrupted by ctrl-c, may not sleep full time)
clock_->sleep_for(time_to_sleep);
try {
// If the context is invalid, an exception will be thrown.
clock_->sleep_for(time_to_sleep);
} catch (const std::runtime_error & e) {
// If it didn't sleep the full time, return false
return false;
}
return true;
}

View File

@@ -15,6 +15,7 @@
#include "rclcpp/typesupport_helpers.hpp"
#include <algorithm>
#include <functional>
#include <memory>
#include <sstream>
@@ -73,6 +74,21 @@ const void * get_typesupport_handle_impl(
}
}
// Trim leading and trailing whitespace from the string.
std::string string_trim(std::string_view str_v)
{
auto begin = std::find_if_not(str_v.begin(), str_v.end(), [](unsigned char ch) {
return std::isspace(ch);
});
auto end = std::find_if_not(str_v.rbegin(), str_v.rend(), [](unsigned char ch) {
return std::isspace(ch);
}).base();
if (begin >= end) {
return {};
}
return std::string(begin, end);
}
} // anonymous namespace
std::tuple<std::string, std::string, std::string>
@@ -82,6 +98,7 @@ extract_type_identifier(const std::string & full_type)
auto sep_position_back = full_type.find_last_of(type_separator);
auto sep_position_front = full_type.find_first_of(type_separator);
if (sep_position_back == std::string::npos ||
sep_position_front == 0 ||
sep_position_back == 0 ||
sep_position_back == full_type.length() - 1)
{
@@ -97,7 +114,8 @@ extract_type_identifier(const std::string & full_type)
}
std::string type_name = full_type.substr(sep_position_back + 1);
return std::make_tuple(package_name, middle_module, type_name);
return std::make_tuple(
string_trim(package_name), string_trim(middle_module), string_trim(type_name));
}
std::string get_typesupport_library_path(

View File

@@ -125,11 +125,13 @@ BENCHMARK_F(ServicePerformanceTest, async_send_response)(benchmark::State & stat
auto service = node->create_service<test_msgs::srv::Empty>(empty_service_name, callback);
reset_heap_counters();
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node->get_node_base_interface());
for (auto _ : state) {
(void)_;
state.PauseTiming();
// Clear executor queue
rclcpp::spin_some(node->get_node_base_interface());
executor.spin_some();
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
auto future = empty_client->async_send_request(request);
@@ -137,7 +139,7 @@ BENCHMARK_F(ServicePerformanceTest, async_send_response)(benchmark::State & stat
benchmark::DoNotOptimize(service);
benchmark::ClobberMemory();
rclcpp::spin_until_future_complete(node->get_node_base_interface(), future);
executor.spin_until_future_complete(future);
}
if (callback_count == 0) {
state.SkipWithError("Service callback was not called");

View File

@@ -804,8 +804,10 @@ TEST(TestExecutors, testSpinUntilFutureCompleteNodePtr)
}
// Check spin functions with non default context
TEST(TestExecutors, testSpinWithNonDefaultContext)
TYPED_TEST(TestExecutors, testSpinWithNonDefaultContext)
{
using ExecutorType = TypeParam;
auto non_default_context = std::make_shared<rclcpp::Context>();
non_default_context->init(0, nullptr);
@@ -813,9 +815,14 @@ TEST(TestExecutors, testSpinWithNonDefaultContext)
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()));
rclcpp::ExecutorOptions options;
options.context = non_default_context;
ExecutorType executor(options);
EXPECT_NO_THROW(executor.add_node(node->get_node_base_interface()));
EXPECT_NO_THROW(rclcpp::spin_all(node->get_node_base_interface(), 1s));
EXPECT_NO_THROW(executor.spin_some());
EXPECT_NO_THROW(executor.spin_all(1s));
auto check_spin_until_future_complete = [&]() {
std::promise<bool> promise;
@@ -823,8 +830,7 @@ TEST(TestExecutors, testSpinWithNonDefaultContext)
promise.set_value(true);
auto shared_future = future.share();
auto ret = rclcpp::spin_until_future_complete(
node->get_node_base_interface(), shared_future, 1s);
auto ret = executor.spin_until_future_complete(shared_future, 1s);
EXPECT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret);
};
EXPECT_NO_THROW(check_spin_until_future_complete());

View File

@@ -112,10 +112,12 @@ protected:
auto req_id = client->async_send_request(request, std::move(callback));
auto start = std::chrono::steady_clock::now();
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
while (!received_response &&
(std::chrono::steady_clock::now() - start) < timeout)
{
rclcpp::spin_some(node);
executor.spin_some();
}
if (!received_response) {
@@ -356,10 +358,12 @@ TYPED_TEST(TestAllClientTypesWithServer, on_new_response_callback)
this->template async_send_request<ClientType, test_msgs::srv::Empty::Request>(client, request);
auto start = std::chrono::steady_clock::now();
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(server_node);
while (server_requests_count == 0 &&
(std::chrono::steady_clock::now() - start) < std::chrono::seconds(10))
{
rclcpp::spin_some(server_node);
executor.spin_some();
}
ASSERT_EQ(server_requests_count, 1u);
@@ -380,7 +384,7 @@ TYPED_TEST(TestAllClientTypesWithServer, on_new_response_callback)
while (server_requests_count == 1 &&
(std::chrono::steady_clock::now() - start) < std::chrono::seconds(10))
{
rclcpp::spin_some(server_node);
executor.spin_some();
}
ASSERT_EQ(server_requests_count, 2u);
@@ -402,7 +406,7 @@ TYPED_TEST(TestAllClientTypesWithServer, on_new_response_callback)
while (server_requests_count < 5 &&
(std::chrono::steady_clock::now() - start) < std::chrono::seconds(10))
{
rclcpp::spin_some(server_node);
executor.spin_some();
}
ASSERT_EQ(server_requests_count, 5u);
@@ -492,10 +496,12 @@ void client_async_send_request_callback_with_request(
auto req_id = client->async_send_request(request, std::move(callback));
auto start = std::chrono::steady_clock::now();
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
while (!received_response &&
(std::chrono::steady_clock::now() - start) < std::chrono::seconds(1))
{
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_TRUE(received_response);
EXPECT_FALSE(client->remove_pending_request(req_id));
@@ -553,25 +559,29 @@ void client_qos_depth(rclcpp::Node::SharedPtr node)
}
auto start = std::chrono::steady_clock::now();
rclcpp::executors::SingleThreadedExecutor server_executor;
server_executor.add_node(server_node);
while ((server_cb_count_ < client_requests) &&
(std::chrono::steady_clock::now() - start) < 2s)
{
rclcpp::spin_some(server_node);
server_executor.spin_some();
std::this_thread::sleep_for(2ms);
}
EXPECT_GT(server_cb_count_, client_qos_profile.depth());
start = std::chrono::steady_clock::now();
rclcpp::executors::SingleThreadedExecutor client_executor;
client_executor.add_node(node);
while ((client_cb_count_ < client_qos_profile.depth()) &&
(std::chrono::steady_clock::now() - start) < 1s)
{
rclcpp::spin_some(node);
client_executor.spin_some();
}
// Spin an extra time to check if client QoS depth has been ignored,
// so more client callbacks might be called than expected.
rclcpp::spin_some(node);
client_executor.spin_some();
EXPECT_EQ(client_cb_count_, client_qos_profile.depth());
}

View File

@@ -42,7 +42,9 @@ TEST(TestCreateTimer, timer_executes)
timer->cancel();
});
rclcpp::spin_some(node);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
executor.spin_some();
ASSERT_TRUE(got_callback);
rclcpp::shutdown();

View File

@@ -78,8 +78,10 @@ public:
counter++;
});
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node_);
while (counter < expected_recv_msg_count) {
rclcpp::spin_some(node_);
executor.spin_some();
}
return messages;
}
@@ -107,11 +109,13 @@ public:
{
using clock = std::chrono::system_clock;
auto start = clock::now();
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node_);
while (!condition()) {
if ((clock::now() - start) > timeout) {
return false;
}
rclcpp::spin_some(node_);
executor.spin_some();
}
return true;
}

View File

@@ -372,16 +372,18 @@ TEST_F(TestGenericService, generic_service_qos_depth) {
}
auto start = std::chrono::steady_clock::now();
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(server_node);
while ((server_cb_count_ < server_qos_profile.depth()) &&
(std::chrono::steady_clock::now() - start) < 1s)
{
rclcpp::spin_some(server_node);
executor.spin_some();
std::this_thread::sleep_for(1ms);
}
// Spin an extra time to check if server QoS depth has been ignored,
// so more server responses might be processed than expected.
rclcpp::spin_some(server_node);
executor.spin_some();
EXPECT_EQ(server_cb_count_, server_qos_profile.depth());
}

View File

@@ -43,9 +43,7 @@ public:
node_graph_ = node_->get_node_graph_interface();
ASSERT_NE(nullptr, node_graph_);
graph_listener_ =
std::make_shared<rclcpp::graph_listener::GraphListener>(
rclcpp::contexts::get_global_default_context());
graph_listener_ = rclcpp::contexts::get_global_default_context()->get_graph_listener();
}
void TearDown()

View File

@@ -535,7 +535,7 @@ TEST(TestIntraProcessManager, add_pub_sub) {
* - Remove the first subscription from ipm and add a new one.
* - Publishes a unique_ptr message with a subscription not requesting ownership.
* - The received message is expected to be the same, the first subscription do not receive it.
* - Publishes a shared_ptr message with a subscription not requesting ownership.
* - Publishes a unique_ptr message with a subscription not requesting ownership.
* - The received message is expected to be the same.
*/
TEST(TestIntraProcessManager, single_subscription) {
@@ -587,9 +587,9 @@ TEST(TestIntraProcessManager, single_subscription) {
* - One is expected to receive the published message, while the other will receive a copy.
* - Publishes a unique_ptr message with 2 subscriptions not requesting ownership.
* - Both received messages are expected to be the same as the published one.
* - Publishes a shared_ptr message with 2 subscriptions requesting ownership.
* - Publishes a unique_ptr message with 2 subscriptions requesting ownership.
* - Both received messages are expected to be a copy of the published one.
* - Publishes a shared_ptr message with 2 subscriptions not requesting ownership.
* - Publishes a unique_ptr message with 2 subscriptions not requesting ownership.
* - Both received messages are expected to be the same as the published one.
*/
TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
@@ -694,9 +694,9 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
* - The 2 subscriptions not requesting ownership are expected to both receive the same copy
* of the message, one of the subscription requesting ownership is expected to receive a
* different copy, while the last is expected to receive the published message.
* - Publishes a shared_ptr message with 1 subscription requesting ownership and 1 not.
* - The subscription requesting ownership is expected to receive a copy of the message, while
* the other is expected to receive the published message
* - Publishes a unique_ptr message with 1 subscription requesting ownership and 1 not.
* - The subscription requesting ownership is expected to receive the published message, while
* the other is expected to receive a copy of the message
*/
TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
using IntraProcessManagerT = rclcpp::experimental::IntraProcessManager;

View File

@@ -253,9 +253,19 @@ bool log_function_const_ref(const rclcpp::Logger & logger)
return true;
}
class DerivedLogger : public rclcpp::Logger
{
public:
explicit DerivedLogger(const rclcpp::Logger & logger)
: rclcpp::Logger(logger) {}
};
TEST_F(TestLoggingMacros, test_log_from_node) {
auto logger = rclcpp::get_logger("test_logging_logger");
EXPECT_TRUE(log_function(logger));
EXPECT_TRUE(log_function_const(logger));
EXPECT_TRUE(log_function_const_ref(logger));
DerivedLogger derived_logger(logger);
RCLCPP_INFO(derived_logger, "successful log from derived logger");
}

View File

@@ -129,6 +129,8 @@ TEST_F(TestMemoryStrategy, get_subscription_by_handle) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_subscription_by_handle(subscription_handle, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
EXPECT_EQ(
nullptr,
@@ -181,6 +183,8 @@ TEST_F(TestMemoryStrategy, get_service_by_handle) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_service_by_handle(service_handle, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
EXPECT_EQ(
nullptr,
@@ -228,6 +232,11 @@ TEST_F(TestMemoryStrategy, get_client_by_handle) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_client_by_handle(client_handle, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
// reset client handle before node destruction, otherwise it tries to access node
// via weak_ptr in its deleter
client_handle.reset();
} // Node goes out of scope
EXPECT_EQ(
nullptr,
@@ -276,6 +285,8 @@ TEST_F(TestMemoryStrategy, get_timer_by_handle) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_timer_by_handle(timer_handle, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
EXPECT_EQ(
nullptr,
@@ -375,6 +386,8 @@ TEST_F(TestMemoryStrategy, get_group_by_subscription) {
EXPECT_EQ(
callback_group,
memory_strategy()->get_group_by_subscription(subscription, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
// NodeBase(SubscriptionBase->rcl_node_t->NodeBase) is still alive.
EXPECT_EQ(
@@ -420,6 +433,8 @@ TEST_F(TestMemoryStrategy, get_group_by_service) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_group_by_service(service, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
EXPECT_EQ(
nullptr,
@@ -459,6 +474,8 @@ TEST_F(TestMemoryStrategy, get_group_by_client) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_group_by_client(client, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
EXPECT_EQ(
nullptr,
@@ -498,6 +515,8 @@ TEST_F(TestMemoryStrategy, get_group_by_timer) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_group_by_timer(timer, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
EXPECT_EQ(
nullptr,
@@ -536,6 +555,8 @@ TEST_F(TestMemoryStrategy, get_group_by_waitable) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_group_by_waitable(waitable, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
EXPECT_EQ(
nullptr,

View File

@@ -3689,6 +3689,115 @@ TEST_F(TestNode, get_publishers_subscriptions_info_by_topic) {
}, rclcpp::exceptions::InvalidTopicNameError);
}
// test that calling get_clients_info_by_service and get_servers_info_by_service
TEST_F(TestNode, get_clients_servers_info_by_service) {
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
std::string service_name = "test_service_info";
std::string fq_service_name = rclcpp::expand_topic_or_service_name(
service_name, node->get_name(), node->get_namespace(), true);
// Lists should be empty
EXPECT_TRUE(node->get_clients_info_by_service(fq_service_name).empty());
EXPECT_TRUE(node->get_servers_info_by_service(fq_service_name).empty());
// Add a client
rclcpp::QoSInitialization qos_initialization =
{
RMW_QOS_POLICY_HISTORY_KEEP_ALL,
10
};
rmw_qos_profile_t rmw_qos_profile_default =
{
RMW_QOS_POLICY_HISTORY_KEEP_ALL,
10,
RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT,
RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL,
{1, 12345},
{20, 9887665},
RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC,
{5, 23456},
false
};
rclcpp::QoS qos = rclcpp::QoS(qos_initialization, rmw_qos_profile_default);
auto client = node->create_client<test_msgs::srv::Empty>(service_name, qos);
// Wait for the underlying RMW implementation to catch up with graph changes
auto client_is_generated =
[&]() {return node->get_clients_info_by_service(fq_service_name).size() == 1u;};
ASSERT_TRUE(wait_for_event(node, client_is_generated));
// List should have one item
auto clients_info = node->get_clients_info_by_service(fq_service_name);
ASSERT_EQ(clients_info.size(), (size_t) 1);
// Server list should be empty
EXPECT_TRUE(node->get_servers_info_by_service(fq_service_name).empty());
// Verify client info has the right data.
EXPECT_EQ(node->get_name(), clients_info[0].node_name());
EXPECT_EQ(node->get_namespace(), clients_info[0].node_namespace());
EXPECT_EQ("test_msgs/srv/Empty", clients_info[0].service_type());
EXPECT_EQ(rclcpp::EndpointType::Client, clients_info[0].endpoint_type());
EXPECT_TRUE(clients_info[0].endpoint_count() == 1 || clients_info[0].endpoint_count() == 2);
for(size_t i = 0; i < clients_info[0].endpoint_count(); i++) {
auto qos_profile = clients_info[0].qos_profiles()[i].get_rmw_qos_profile();
expect_qos_profile_eq(qos.get_rmw_qos_profile(), qos_profile, false);
}
// Add a service server
rclcpp::QoSInitialization qos_initialization2 =
{
RMW_QOS_POLICY_HISTORY_KEEP_LAST,
0
};
rmw_qos_profile_t rmw_qos_profile_default2 =
{
RMW_QOS_POLICY_HISTORY_KEEP_LAST,
0,
RMW_QOS_POLICY_RELIABILITY_RELIABLE,
RMW_QOS_POLICY_DURABILITY_VOLATILE,
{15, 1678},
{29, 2345},
RMW_QOS_POLICY_LIVELINESS_AUTOMATIC,
{5, 23456},
false
};
rclcpp::QoS qos2 = rclcpp::QoS(qos_initialization2, rmw_qos_profile_default2);
auto callback = [](test_msgs::srv::Empty_Request::ConstSharedPtr req,
test_msgs::srv::Empty_Response::ConstSharedPtr resp) {
(void)req;
(void)resp;
};
auto server = node->create_service<test_msgs::srv::Empty>(service_name, callback, qos2);
// Wait for the underlying RMW implementation to catch up with graph changes
auto server_is_generated =
[&]() {return node->get_servers_info_by_service(fq_service_name).size() == 1u;};
ASSERT_TRUE(wait_for_event(node, server_is_generated));
// List should have one item
auto servers_info = node->get_servers_info_by_service(fq_service_name);
ASSERT_EQ(servers_info.size(), (size_t) 1);
// Verify server info has the right data.
EXPECT_EQ(node->get_name(), servers_info[0].node_name());
EXPECT_EQ(node->get_namespace(), servers_info[0].node_namespace());
EXPECT_EQ("test_msgs/srv/Empty", servers_info[0].service_type());
EXPECT_EQ(rclcpp::EndpointType::Server, servers_info[0].endpoint_type());
EXPECT_TRUE(servers_info[0].endpoint_count() == 1 || servers_info[0].endpoint_count() == 2);
for(size_t i = 0; i < servers_info[0].endpoint_count(); i++) {
auto qos_profile = servers_info[0].qos_profiles()[i].get_rmw_qos_profile();
expect_qos_profile_eq(qos2.get_rmw_qos_profile(), qos_profile, false);
}
// Error cases
EXPECT_THROW(
{
clients_info = node->get_clients_info_by_service("13");
}, rclcpp::exceptions::InvalidServiceNameError);
EXPECT_THROW(
{
servers_info = node->get_servers_info_by_service("13");
}, rclcpp::exceptions::InvalidServiceNameError);
}
TEST_F(TestNode, callback_groups) {
auto node = std::make_shared<rclcpp::Node>("node", "ns");
size_t num_callback_groups_in_basic_node = 0;

View File

@@ -173,3 +173,12 @@ TEST_F(TestRate, incorrect_constuctor) {
rclcpp::Rate rate(rclcpp::Duration(-1, 0)),
std::invalid_argument("period must be greater than 0"));
}
TEST(TestRateBasic, invalid_context) {
rclcpp::init(0, nullptr);
rclcpp::Rate rate(1.0);
ASSERT_TRUE(rate.sleep());
rclcpp::shutdown();
EXPECT_NO_THROW(rate.sleep());
ASSERT_FALSE(rate.sleep());
}

View File

@@ -386,17 +386,20 @@ TEST_F(TestService, server_qos_depth) {
std::this_thread::sleep_for(10ms);
}
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(server_node);
auto start = std::chrono::steady_clock::now();
while ((server_cb_count_ < server_qos_profile.depth()) &&
(std::chrono::steady_clock::now() - start) < 1s)
{
rclcpp::spin_some(server_node);
executor.spin_some();
std::this_thread::sleep_for(1ms);
}
// Spin an extra time to check if server QoS depth has been ignored,
// so more server responses might be processed than expected.
rclcpp::spin_some(server_node);
executor.spin_some();
EXPECT_EQ(server_cb_count_, server_qos_profile.depth());
}

View File

@@ -87,6 +87,9 @@ protected:
TEST_F(TestServiceIntrospection, service_introspection_nominal)
{
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
auto request = std::make_shared<BasicTypes::Request>();
request->set__bool_value(true);
request->set__int64_value(42);
@@ -106,7 +109,7 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
auto future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
BasicTypes::Response::SharedPtr response = future.get();
ASSERT_EQ(response->bool_value, false);
@@ -115,7 +118,7 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
// wrap up work to get all the service_event messages
auto start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
std::map<uint8_t, std::shared_ptr<const BasicTypes::Event>> event_map;
@@ -174,6 +177,9 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
service->configure_introspection(
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
ASSERT_EQ(sub->get_publisher_count(), 0);
auto request = std::make_shared<BasicTypes::Request>();
@@ -182,10 +188,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
auto future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
auto start = std::chrono::steady_clock::now();
while ((std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 0U);
@@ -206,10 +212,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 2 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 2U);
@@ -230,10 +236,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 2 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 2U);
@@ -254,10 +260,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 4U);
}
@@ -269,6 +275,9 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
service->configure_introspection(
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
// Wait for the introspection to attach to our subscription
size_t tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
@@ -282,10 +291,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
auto future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
auto start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 4U);
for (const auto & event : events) {
@@ -310,10 +319,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 4U);
for (const auto & event : events) {
@@ -350,10 +359,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 4U);
for (const auto & event : events) {
@@ -390,10 +399,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 4U);
for (const auto & event : events) {

View File

@@ -69,11 +69,13 @@ public:
{
using clock = std::chrono::system_clock;
auto start = clock::now();
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
while (!condition()) {
if ((clock::now() - start) > timeout) {
return false;
}
rclcpp::spin_some(node);
executor.spin_some();
}
return true;
}

View File

@@ -158,3 +158,15 @@ TEST(TypesupportHelpersTest, test_throw_exception_with_invalid_type) {
rclcpp::get_action_typesupport_handle(invalid_type, "rosidl_typesupport_cpp", *library),
std::runtime_error);
}
TEST(TypesupportHelpersTest, throws_exception_if_filetype_has_multiple_slashes_at_start) {
EXPECT_ANY_THROW(rclcpp::extract_type_identifier("//name_with_slashes_at_start"));
}
TEST(TypesupportHelpersTestV3, ProcessesValidTypeWithWhitespace) {
std::string package, middle, name;
std::tie(package, middle, name) = rclcpp::extract_type_identifier(" package/ name ");
EXPECT_EQ(package, "package");
EXPECT_TRUE(middle.empty());
EXPECT_EQ(name, "name");
}

View File

@@ -3,6 +3,22 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30.1.3 (2025-11-18)
-------------------
* Fix REP url locations (`#2987 <https://github.com/ros2/rclcpp/issues/2987>`_)
* Contributors: Tim Clephas
30.1.2 (2025-10-21)
-------------------
* it misses the iterator second to lock the weakptr. (`#2958 <https://github.com/ros2/rclcpp/issues/2958>`_)
* try aborting before canceling 1st on dtor of ServerGoalHandle. (`#2953 <https://github.com/ros2/rclcpp/issues/2953>`_)
* Contributors: Tomoya Fujita
30.1.1 (2025-09-11)
-------------------
* deprecate rclcpp::spin_some and rclcpp::spin_all (`#2848 <https://github.com/ros2/rclcpp/issues/2848>`_)
* Contributors: Alberto Soragna
30.1.0 (2025-07-29)
-------------------
* fix cmake deprecation (`#2914 <https://github.com/ros2/rclcpp/issues/2914>`_)

View File

@@ -1,10 +1,10 @@
This document is a declaration of software quality for the `rclcpp_action` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html).
This document is a declaration of software quality for the `rclcpp_action` package, based on the guidelines in [REP-2004](https://reps.openrobotics.org/rep-2004/).
# rclcpp_action Quality Declaration
The package `rclcpp_action` claims to be in the **Quality Level 1** category when it is used with a **Quality Level 1** middleware.
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://www.ros.org/reps/rep-2004.html).
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://reps.openrobotics.org/rep-2004/).
## Version Policy [1]
@@ -53,7 +53,7 @@ All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://d
### Continuous Integration [2.iv]
All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers)
All pull requests must pass CI on all [tier 1 platforms](https://reps.openrobotics.org/rep-2000/#support-tiers)
Currently nightly results can be seen here:
@@ -179,7 +179,7 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://github
## Platform Support [6]
`rclcpp_action` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them.
`rclcpp_action` supports all of the tier 1 platforms as described in [REP-2000](https://reps.openrobotics.org/rep-2000/#support-tiers), and tests each change against all of them.
Currently nightly build status can be seen here:
* [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/rclcpp_action/)
@@ -191,4 +191,4 @@ Currently nightly build status can be seen here:
### Vulnerability Disclosure Policy [7.i]
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html).
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://reps.openrobotics.org/rep-2006/).

View File

@@ -350,7 +350,7 @@ public:
return;
}
goal_handle = it->lock();
goal_handle = it->second.lock();
}
if (goal_handle) {

View File

@@ -24,6 +24,8 @@
#include "action_msgs/msg/goal_status.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp_action/visibility_control.hpp"
#include "rclcpp_action/types.hpp"
@@ -103,11 +105,19 @@ protected:
_execute();
/// Transition the goal to canceled state if it never reached a terminal state.
/// Returns true if transitioned to canceled, else false.
/// \internal
RCLCPP_ACTION_PUBLIC
bool
try_canceling() noexcept;
/// Transition the goal to aborted state if it never reached a terminal state.
/// Returns true if transitioned to aborted, else false.
/// \internal
RCLCPP_ACTION_PUBLIC
bool
try_aborting() noexcept;
// End API for communication between ServerGoalHandleBase and ServerGoalHandle<>
// -----------------------------------------------------------------------------
@@ -243,11 +253,22 @@ public:
virtual ~ServerGoalHandle()
{
// Cancel goal if handle was allowed to destruct without reaching a terminal state
if (try_canceling()) {
auto null_result = std::make_shared<typename ActionT::Impl::GetResultService::Response>();
null_result->status = action_msgs::msg::GoalStatus::STATUS_CANCELED;
on_terminal_state_(uuid_, null_result);
try {
// Abort goal if handle was allowed to destruct without reaching a terminal state
if (try_aborting()) {
auto null_result = std::make_shared<typename ActionT::Impl::GetResultService::Response>();
null_result->status = action_msgs::msg::GoalStatus::STATUS_ABORTED;
on_terminal_state_(uuid_, null_result);
} else if (try_canceling()) {
// Cancel goal if handle was allowed to destruct without reaching a terminal state
auto null_result = std::make_shared<typename ActionT::Impl::GetResultService::Response>();
null_result->status = action_msgs::msg::GoalStatus::STATUS_CANCELED;
on_terminal_state_(uuid_, null_result);
}
} catch (const std::exception & ex) {
RCLCPP_DEBUG(
rclcpp::get_logger("rclcpp_action"),
"Failed to abort/cancel goal handler in destructor: %s", ex.what());
}
}

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

View File

@@ -13,6 +13,7 @@
// limitations under the License.
#include <memory>
#include <mutex>
#include "rcl_action/action_server.h"
#include "rcl_action/goal_handle.h"
@@ -135,6 +136,24 @@ ServerGoalHandleBase::try_canceling() noexcept
return RCL_RET_OK == ret;
}
return false;
return is_cancelable;
}
bool
ServerGoalHandleBase::try_aborting() noexcept
{
std::lock_guard<std::mutex> lock(rcl_handle_mutex_);
rcl_ret_t ret;
// Check if the goal is abortable
const bool is_abortable = rcl_action_goal_handle_is_abortable(rcl_handle_.get());
if (is_abortable) {
// Transition to ABORTED
ret = rcl_action_update_goal_state(rcl_handle_.get(), GOAL_EVENT_ABORT);
if (RCL_RET_OK != ret) {
return false;
}
}
return is_abortable;
}
} // namespace rclcpp_action

View File

@@ -518,9 +518,11 @@ TEST_F(TestServer, publish_status_accepted)
// 10 seconds
const size_t max_tries = 10 * 1000 / 100;
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
for (size_t retry = 0; retry < max_tries && received_msgs.size() != 1u; ++retry) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
rclcpp::spin_some(node);
executor.spin_some();
}
ASSERT_LT(0u, received_msgs.size());
@@ -582,9 +584,11 @@ TEST_F(TestServer, publish_status_canceling)
// 10 seconds
const size_t max_tries = 10 * 1000 / 100;
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
for (size_t retry = 0; retry < max_tries && received_msgs.size() < 2u; ++retry) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
rclcpp::spin_some(node);
executor.spin_some();
}
ASSERT_LT(0u, received_msgs.size());
@@ -640,10 +644,12 @@ TEST_F(TestServer, publish_status_canceled)
received_handle->canceled(std::make_shared<Fibonacci::Result>());
// 10 seconds
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
const size_t max_tries = 10 * 1000 / 100;
for (size_t retry = 0; retry < max_tries && received_msgs.size() < 3u; ++retry) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
rclcpp::spin_some(node);
executor.spin_some();
}
ASSERT_LT(0u, received_msgs.size());
@@ -697,10 +703,12 @@ TEST_F(TestServer, publish_status_succeeded)
received_handle->succeed(std::make_shared<Fibonacci::Result>());
// 10 seconds
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
const size_t max_tries = 10 * 1000 / 100;
for (size_t retry = 0; retry < max_tries && received_msgs.size() < 2u; ++retry) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
rclcpp::spin_some(node);
executor.spin_some();
}
ASSERT_LT(0u, received_msgs.size());
@@ -754,10 +762,12 @@ TEST_F(TestServer, publish_status_aborted)
received_handle->abort(std::make_shared<Fibonacci::Result>());
// 10 seconds
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
const size_t max_tries = 10 * 1000 / 100;
for (size_t retry = 0; retry < max_tries && received_msgs.size() < 2u; ++retry) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
rclcpp::spin_some(node);
executor.spin_some();
}
ASSERT_LT(0u, received_msgs.size());
@@ -814,10 +824,12 @@ TEST_F(TestServer, publish_feedback)
received_handle->publish_feedback(sent_message);
// 10 seconds
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
const size_t max_tries = 10 * 1000 / 100;
for (size_t retry = 0; retry < max_tries && received_msgs.size() < 1u; ++retry) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
rclcpp::spin_some(node);
executor.spin_some();
}
ASSERT_EQ(1u, received_msgs.size());
@@ -878,10 +890,13 @@ TEST_F(TestServer, get_result)
result->sequence = {5, 8, 13, 21};
received_handle->succeed(result);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
// Wait for the result request to be received
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future));
executor.spin_until_future_complete(future));
auto response = future.get();
EXPECT_EQ(action_msgs::msg::GoalStatus::STATUS_SUCCEEDED, response->status);
@@ -891,13 +906,13 @@ TEST_F(TestServer, get_result)
rclcpp::sleep_for(2 * result_timeout);
// Allow for expiration to take place
rclcpp::spin_some(node);
executor.spin_some();
// Send and wait for another result request
future = result_client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future));
executor.spin_until_future_complete(future));
response = future.get();
EXPECT_EQ(action_msgs::msg::GoalStatus::STATUS_UNKNOWN, response->status);
@@ -946,9 +961,12 @@ TEST_F(TestServer, get_result_deferred)
request->goal_id.uuid = uuid;
auto future = result_client->async_send_request(request);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
// Process request first
rclcpp::sleep_for(std::chrono::milliseconds(10)); // Give a chance for the request to be served
rclcpp::spin_some(node);
executor.spin_some();
// Send a result
auto result = std::make_shared<Fibonacci::Result>();
@@ -958,7 +976,7 @@ TEST_F(TestServer, get_result_deferred)
// Wait for the result request to be received
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future));
executor.spin_until_future_complete(future));
auto response = future.get();
EXPECT_EQ(action_msgs::msg::GoalStatus::STATUS_SUCCEEDED, response->status);
@@ -1042,10 +1060,13 @@ public:
result->sequence = {5, 8, 13, 21};
goal_handle_->succeed(result);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node_);
// Wait for the result request to be received
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node_, future));
executor.spin_until_future_complete(future));
auto response = future.get();
EXPECT_EQ(action_msgs::msg::GoalStatus::STATUS_SUCCEEDED, response->status);
@@ -1055,13 +1076,13 @@ public:
rclcpp::sleep_for(std::chrono::milliseconds(100));
// Allow for expiration to take place
rclcpp::spin_some(node_);
executor.spin_some();
// Send and wait for another result request
future = result_client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node_, future));
executor.spin_until_future_complete(future));
}
protected:

View File

@@ -44,6 +44,8 @@ public:
bool try_cancel() {return try_canceling();}
bool try_abort() {return try_aborting();}
void cancel_goal() {_cancel_goal();}
};
@@ -132,6 +134,20 @@ TEST_F(TestServerGoalHandle, abort) {
EXPECT_THROW(handle_->abort(result), rclcpp::exceptions::RCLError);
}
TEST_F(TestServerGoalHandle, try_abort) {
handle_->execute();
test_msgs::action::Fibonacci::Result::SharedPtr result =
std::make_shared<test_msgs::action::Fibonacci::Result>();
EXPECT_TRUE(handle_->try_abort());
EXPECT_FALSE(handle_->is_canceling());
EXPECT_FALSE(handle_->is_active());
EXPECT_FALSE(handle_->is_executing());
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp_action", rcl_action_update_goal_state, RCL_RET_ERROR);
EXPECT_THROW(handle_->abort(result), rclcpp::exceptions::RCLError);
}
TEST_F(TestServerGoalHandle, succeed) {
handle_->execute();
test_msgs::action::Fibonacci::Result::SharedPtr result =

View File

@@ -2,6 +2,19 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30.1.3 (2025-11-18)
-------------------
* Fix REP url locations (`#2987 <https://github.com/ros2/rclcpp/issues/2987>`_)
* Contributors: Tim Clephas
30.1.2 (2025-10-21)
-------------------
30.1.1 (2025-09-11)
-------------------
* Cleanup the dependencies in rclcpp_components. (`#2918 <https://github.com/ros2/rclcpp/issues/2918>`_)
* Contributors: Chris Lalancette
30.1.0 (2025-07-29)
-------------------
* fix cmake deprecation (`#2914 <https://github.com/ros2/rclcpp/issues/2914>`_)

View File

@@ -19,7 +19,9 @@ find_package(ament_index_cpp REQUIRED)
find_package(class_loader REQUIRED)
find_package(composition_interfaces REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rcl_interfaces REQUIRED)
find_package(rcpputils REQUIRED)
find_package(rmw REQUIRED)
# Add an interface library that can be depended upon by libraries who register components
add_library(component INTERFACE)
@@ -39,13 +41,15 @@ target_include_directories(component_manager PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>")
target_link_libraries(component_manager PUBLIC
class_loader::class_loader
${composition_interfaces_TARGETS}
rclcpp::rclcpp
rmw::rmw
)
target_link_libraries(component_manager PRIVATE
ament_index_cpp::ament_index_cpp
class_loader::class_loader
rcpputils::rcpputils
${rcl_interfaces_TARGETS}
)
target_compile_definitions(component_manager
PRIVATE "RCLCPP_COMPONENTS_BUILDING_LIBRARY")
@@ -54,13 +58,13 @@ add_executable(
component_container
src/component_container.cpp
)
target_link_libraries(component_container component_manager rclcpp::rclcpp)
target_link_libraries(component_container PRIVATE component_manager rclcpp::rclcpp)
add_executable(
component_container_event
src/component_container_event.cpp
)
target_link_libraries(component_container_event component_manager rclcpp::rclcpp)
target_link_libraries(component_container_event PRIVATE component_manager rclcpp::rclcpp)
set(node_main_template_install_dir "share/${PROJECT_NAME}")
install(FILES
@@ -71,19 +75,13 @@ add_executable(
component_container_mt
src/component_container_mt.cpp
)
target_link_libraries(component_container_mt component_manager rclcpp::rclcpp)
target_link_libraries(component_container_mt PRIVATE component_manager rclcpp::rclcpp)
add_executable(
component_container_isolated
src/component_container_isolated.cpp
)
target_link_libraries(component_container_isolated component_manager rclcpp::rclcpp)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_link_libraries(component_container "stdc++fs")
target_link_libraries(component_container_mt "stdc++fs")
target_link_libraries(component_container_isolated "stdc++fs")
endif()
target_link_libraries(component_container_isolated PRIVATE component_manager rclcpp::rclcpp)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
@@ -174,9 +172,5 @@ ament_export_libraries(component_manager)
# Export modern CMake targets
ament_export_targets(export_${PROJECT_NAME})
# specific order: dependents before dependencies
ament_export_dependencies(ament_index_cpp)
ament_export_dependencies(class_loader)
ament_export_dependencies(composition_interfaces)
ament_export_dependencies(rclcpp)
ament_export_dependencies(class_loader composition_interfaces rclcpp rcpputils rmw)
ament_package(CONFIG_EXTRAS rclcpp_components-extras.cmake.in)

View File

@@ -1,10 +1,10 @@
This document is a declaration of software quality for the `rclcpp_components` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html).
This document is a declaration of software quality for the `rclcpp_components` package, based on the guidelines in [REP-2004](https://reps.openrobotics.org/rep-2004/).
# rclcpp_components Quality Declaration
The package `rclcpp_components` claims to be in the **Quality Level 1** category.
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://www.ros.org/reps/rep-2004.html).
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://reps.openrobotics.org/rep-2004/).
## Version Policy [1]
@@ -53,7 +53,7 @@ All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://d
### Continuous Integration [2.iv]
All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers)
All pull requests must pass CI on all [tier 1 platforms](https://reps.openrobotics.org/rep-2000/#support-tiers)
Currently nightly results can be seen here:
@@ -191,7 +191,7 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://github
## Platform Support [6]
`rclcpp_components` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them.
`rclcpp_components` supports all of the tier 1 platforms as described in [REP-2000](https://reps.openrobotics.org/rep-2000/#support-tiers), and tests each change against all of them.
Currently nightly build status can be seen here:
* [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/rclcpp_components/)
@@ -203,4 +203,4 @@ Currently nightly build status can be seen here:
### Vulnerability Disclosure Policy [7.i]
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html).
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://reps.openrobotics.org/rep-2006/).

View File

@@ -43,10 +43,13 @@
#include <map>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include "class_loader/class_loader.hpp"
#include "composition_interfaces/srv/load_node.hpp"
#include "composition_interfaces/srv/unload_node.hpp"
#include "composition_interfaces/srv/list_nodes.hpp"
@@ -56,12 +59,10 @@
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_components/node_factory.hpp"
#include "rclcpp_components/node_instance_wrapper.hpp"
#include "rclcpp_components/visibility_control.hpp"
namespace class_loader
{
class ClassLoader;
} // namespace class_loader
#include "rmw/types.h"
namespace rclcpp_components
{

View File

@@ -16,14 +16,20 @@
#ifndef RCLCPP_COMPONENTS__COMPONENT_MANAGER_ISOLATED_HPP__
#define RCLCPP_COMPONENTS__COMPONENT_MANAGER_ISOLATED_HPP__
#include <atomic>
#include <chrono>
#include <map>
#include <memory>
#include <string>
#include <thread>
#include <unordered_map>
#include <utility>
#include <vector>
#include <unordered_map>
#include <system_error>
#include "rclcpp/executor.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp_components/component_manager.hpp"
#include "rcpputils/thread_name.hpp"

View File

@@ -18,7 +18,10 @@
#include <functional>
#include <memory>
#include "rclcpp/node_options.hpp"
#include "rclcpp_components/node_factory.hpp"
#include "rclcpp_components/node_instance_wrapper.hpp"
namespace rclcpp_components
{

View File

@@ -16,6 +16,7 @@
#define RCLCPP_COMPONENTS__REGISTER_NODE_MACRO_HPP__
#include "class_loader/class_loader.hpp"
#include "rclcpp_components/node_factory.hpp"
#include "rclcpp_components/node_factory_template.hpp"
/// Register a component that can be dynamically loaded at runtime.

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>30.1.0</version>
<version>30.1.3</version>
<description>Package containing tools for dynamically loadable components</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
@@ -16,23 +16,32 @@
<buildtool_depend>ament_cmake_ros</buildtool_depend>
<build_export_depend>class_loader</build_export_depend>
<build_export_depend>composition_interfaces</build_export_depend>
<build_export_depend>rclcpp</build_export_depend>
<build_export_depend>rcpputils</build_export_depend>
<build_export_depend>rmw</build_export_depend>
<build_depend>ament_index_cpp</build_depend>
<build_depend>class_loader</build_depend>
<build_depend>composition_interfaces</build_depend>
<build_depend>rclcpp</build_depend>
<build_depend>rcl_interfaces</build_depend>
<build_depend>rcpputils</build_depend>
<build_depend>rmw</build_depend>
<exec_depend>ament_index_cpp</exec_depend>
<exec_depend>class_loader</exec_depend>
<exec_depend>composition_interfaces</exec_depend>
<exec_depend>rclcpp</exec_depend>
<exec_depend>rcl_interfaces</exec_depend>
<exec_depend>rcpputils</exec_depend>
<exec_depend>rmw</exec_depend>
<test_depend>ament_cmake_google_benchmark</test_depend>
<test_depend>ament_cmake_gtest</test_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<test_depend>launch_testing</test_depend>
<test_depend>std_msgs</test_depend>
<export>
<build_type>ament_cmake</build_type>

View File

@@ -14,7 +14,8 @@
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp_components/component_manager.hpp"
@@ -26,4 +27,6 @@ int main(int argc, char * argv[])
auto node = std::make_shared<rclcpp_components::ComponentManager>(exec);
exec->add_node(node);
exec->spin();
return 0;
}

View File

@@ -14,7 +14,7 @@
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/experimental/executors/events_executor/events_executor.hpp"
#include "rclcpp_components/component_manager.hpp"
@@ -27,4 +27,6 @@ int main(int argc, char * argv[])
auto node = std::make_shared<rclcpp_components::ComponentManager>(exec);
exec->add_node(node);
exec->spin();
return 0;
}

View File

@@ -16,8 +16,10 @@
#include <vector>
#include <string>
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/executors/multi_threaded_executor.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp_components/component_manager_isolated.hpp"
int main(int argc, char * argv[])
@@ -46,4 +48,6 @@ int main(int argc, char * argv[])
}
exec->add_node(node);
exec->spin();
return 0;
}

View File

@@ -14,7 +14,8 @@
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/executors/multi_threaded_executor.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp_components/component_manager.hpp"
@@ -35,4 +36,6 @@ int main(int argc, char * argv[])
}
exec->add_node(node);
exec->spin();
return 0;
}

View File

@@ -17,15 +17,29 @@
#include <filesystem>
#include <functional>
#include <memory>
#include <stdexcept>
#include <sstream>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include "ament_index_cpp/get_resource.hpp"
#include "class_loader/class_loader.hpp"
#include "rcpputils/filesystem_helper.hpp"
#include "rclcpp/node_options.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp_components/node_factory.hpp"
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
#include "rcl_interfaces/msg/integer_range.hpp"
#include "rcpputils/split.hpp"
#include "rmw/types.h"
using namespace std::placeholders;
namespace rclcpp_components

View File

@@ -3,6 +3,22 @@ Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30.1.3 (2025-11-18)
-------------------
* add : get clients, servers info (`#2569 <https://github.com/ros2/rclcpp/issues/2569>`_)
* Fix REP url locations (`#2987 <https://github.com/ros2/rclcpp/issues/2987>`_)
* Add get_parameter_or overload returning value or alternative (`#2973 <https://github.com/ros2/rclcpp/issues/2973>`_)
* Contributors: Minju, Lee, Tim Clephas, Zheng Qu
30.1.2 (2025-10-21)
-------------------
30.1.1 (2025-09-11)
-------------------
* deprecate rclcpp::spin_some and rclcpp::spin_all (`#2848 <https://github.com/ros2/rclcpp/issues/2848>`_)
* Clearer warning message, the old one lacked information and was perhaps misleading (`#2927 <https://github.com/ros2/rclcpp/issues/2927>`_)
* Contributors: Alberto Soragna, Peter Mitrano (AR)
30.1.0 (2025-07-29)
-------------------
* fix cmake deprecation (`#2914 <https://github.com/ros2/rclcpp/issues/2914>`_)

View File

@@ -1,10 +1,10 @@
This document is a declaration of software quality for the `rclcpp_lifecycle` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html).
This document is a declaration of software quality for the `rclcpp_lifecycle` package, based on the guidelines in [REP-2004](https://reps.openrobotics.org/rep-2004/).
# rclcpp_lifecycle Quality Declaration
The package `rclcpp_lifecycle` claims to be in the **Quality Level 1** category when used with a **Quality Level 1** middleware.
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://www.ros.org/reps/rep-2004.html).
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://reps.openrobotics.org/rep-2004/).
## Version Policy [1]
@@ -53,7 +53,7 @@ All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://d
### Continuous Integration [2.iv]
All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers)
All pull requests must pass CI on all [tier 1 platforms](https://reps.openrobotics.org/rep-2000/#support-tiers)
Currently nightly results can be seen here:
@@ -191,7 +191,7 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://github
## Platform Support [6]
`rclcpp_lifecycle` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them.
`rclcpp_lifecycle` supports all of the tier 1 platforms as described in [REP-2000](https://reps.openrobotics.org/rep-2000/#support-tiers), and tests each change against all of them.
Currently nightly build status can be seen here:
* [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/rclcpp_lifecycle/)
@@ -203,4 +203,4 @@ Currently nightly build status can be seen here:
### Vulnerability Disclosure Policy [7.i]
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html).
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://reps.openrobotics.org/rep-2006/).

View File

@@ -486,6 +486,16 @@ public:
ParameterT & value,
const ParameterT & alternative_value) const;
/// Return the parameter value, or the "alternative_value" if not set.
/**
* \sa rclcpp::Node::get_parameter_or
*/
template<typename ParameterT>
ParameterT
get_parameter_or(
const std::string & name,
const ParameterT & alternative_value) const;
/// Return the parameters by the given parameter names.
/**
* \sa rclcpp::Node::get_parameters
@@ -693,6 +703,22 @@ public:
std::vector<rclcpp::TopicEndpointInfo>
get_subscriptions_info_by_topic(const std::string & topic_name, bool no_mangle = false) const;
/// Return the service endpoint information about clients on a given service.
/**
* \sa rclcpp::Node::get_clients_info_by_service
*/
RCLCPP_LIFECYCLE_PUBLIC
std::vector<rclcpp::ServiceEndpointInfo>
get_clients_info_by_service(const std::string & service_name, bool no_mangle = false) const;
/// Return the service endpoint information about server on a given service.
/**
* \sa rclcpp::Node::get_servers_info_by_service
*/
RCLCPP_LIFECYCLE_PUBLIC
std::vector<rclcpp::ServiceEndpointInfo>
get_servers_info_by_service(const std::string & service_name, bool no_mangle = false) const;
/// Return a graph event, which will be set anytime a graph change occurs.
/* The graph Event object is a loan which must be returned.
* The Event object is scoped and therefore to return the load just let it go

View File

@@ -306,5 +306,16 @@ LifecycleNode::get_parameter_or(
return got_parameter;
}
template<typename ParameterT>
ParameterT
LifecycleNode::get_parameter_or(
const std::string & name,
const ParameterT & alternative_value) const
{
ParameterT parameter;
get_parameter_or(name, parameter, alternative_value);
return parameter;
}
} // namespace rclcpp_lifecycle
#endif // RCLCPP_LIFECYCLE__LIFECYCLE_NODE_IMPL_HPP_

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

View File

@@ -422,6 +422,18 @@ LifecycleNode::get_subscriptions_info_by_topic(const std::string & topic_name, b
return node_graph_->get_subscriptions_info_by_topic(topic_name, no_mangle);
}
std::vector<rclcpp::ServiceEndpointInfo>
LifecycleNode::get_clients_info_by_service(const std::string & service_name, bool no_mangle) const
{
return node_graph_->get_clients_info_by_service(service_name, no_mangle);
}
std::vector<rclcpp::ServiceEndpointInfo>
LifecycleNode::get_servers_info_by_service(const std::string & service_name, bool no_mangle) const
{
return node_graph_->get_servers_info_by_service(service_name, no_mangle);
}
void
LifecycleNode::for_each_callback_group(
const rclcpp::node_interfaces::NodeBaseInterface::CallbackGroupFunction & func)

View File

@@ -390,6 +390,7 @@ LifecycleNode::LifecycleNodeInterfaceImpl::change_state(
constexpr bool publish_update = true;
State initial_state;
unsigned int current_state_id;
const rcl_lifecycle_transition_t * original_transition{nullptr};
{
std::lock_guard<std::recursive_mutex> lock(state_machine_mutex_);
@@ -405,6 +406,10 @@ LifecycleNode::LifecycleNodeInterfaceImpl::change_state(
// keep the initial state to pass to a transition callback
initial_state = State(state_machine_.current_state);
original_transition =
rcl_lifecycle_get_transition_by_id(state_machine_.current_state, transition_id);
if (
rcl_lifecycle_trigger_transition_by_id(
&state_machine_, transition_id, publish_update) != RCL_RET_OK)
@@ -459,12 +464,15 @@ LifecycleNode::LifecycleNodeInterfaceImpl::change_state(
// Update the internal current_state_
current_state_ = State(state_machine_.current_state);
// error handling ?!
// error handling
// TODO(karsten1987): iterate over possible ret value
if (cb_return_code == node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR) {
RCLCPP_WARN(
node_logging_interface_->get_logger(),
"Error occurred while doing error handling.");
if (original_transition) {
RCLCPP_WARN(
node_logging_interface_->get_logger(),
"Callback returned ERROR during the transition: %s", original_transition->label);
}
auto error_cb_code = execute_callback(current_state_id, initial_state);
auto error_cb_label = get_label_for_return_code(error_cb_code);

View File

@@ -840,6 +840,26 @@ TEST_F(TestDefaultStateMachine, check_parameters) {
EXPECT_TRUE(parameter.as_bool());
}
TEST_F(TestDefaultStateMachine, test_get_parameter_or) {
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
const std::string param_name = "test_param";
int param_int = -999;
// Parameter does not exist, should return "or" value
EXPECT_FALSE(test_node->get_parameter_or(param_name, param_int, 123));
EXPECT_EQ(param_int, 123);
EXPECT_EQ(test_node->get_parameter_or(param_name, 456), 456);
// Declare param_int
test_node->declare_parameter(param_name, rclcpp::ParameterValue(789));
// Parameter exists, should return existing value
EXPECT_TRUE(test_node->get_parameter_or(param_name, param_int, 123));
EXPECT_EQ(param_int, 789);
EXPECT_EQ(test_node->get_parameter_or(param_name, 456), 789);
}
TEST_F(TestDefaultStateMachine, test_getters) {
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
auto options = test_node->get_node_options();
@@ -922,6 +942,12 @@ TEST_F(TestDefaultStateMachine, test_graph_services) {
EXPECT_EQ(1u, test_node->count_services("/testnode/get_available_transitions"));
EXPECT_EQ(1u, test_node->count_services("/testnode/get_state"));
EXPECT_EQ(1u, test_node->count_services("/testnode/get_transition_graph"));
auto clients_info =
test_node->get_clients_info_by_service("/testnode/change_state");
EXPECT_EQ(0u, clients_info.size());
auto servers_info = test_node->get_servers_info_by_service("/testnode/change_state");
EXPECT_EQ(1u, servers_info.size());
}
TEST_F(TestDefaultStateMachine, test_graph_services_by_node) {

View File

@@ -226,36 +226,28 @@ private:
rclcpp::init(0, nullptr);
lifecycle_node_ = std::make_shared<EmptyLifecycleNode>();
lifecycle_client_ = std::make_shared<LifecycleServiceClient>("client");
spinner_ = std::thread(&TestLifecycleServiceClient::spin, this);
executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
spinner_ = std::thread([this]() {
executor_->add_node(lifecycle_node_->get_node_base_interface());
executor_->add_node(lifecycle_client_);
executor_->spin();
});
}
void TearDown() override
{
{
std::lock_guard<std::mutex> guard(shutdown_mutex_);
rclcpp::shutdown();
while(!executor_->is_spinning()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
executor_->cancel();
spinner_.join();
}
void spin()
{
while (true) {
{
std::lock_guard<std::mutex> guard(shutdown_mutex_);
if (!rclcpp::ok()) {
break;
}
rclcpp::spin_some(lifecycle_node_->get_node_base_interface());
rclcpp::spin_some(lifecycle_client_);
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
rclcpp::shutdown();
}
std::shared_ptr<EmptyLifecycleNode> lifecycle_node_;
std::shared_ptr<LifecycleServiceClient> lifecycle_client_;
std::mutex shutdown_mutex_;
std::shared_ptr<rclcpp::executors::SingleThreadedExecutor> executor_;
std::thread spinner_;
};
@@ -455,34 +447,39 @@ TEST_F(TestLifecycleServiceClientRCLErrors, call_services_rcl_errors) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp_lifecycle", rcl_lifecycle_state_machine_is_initialized, RCL_RET_ERROR);
rclcpp::executors::SingleThreadedExecutor lifecycle_node_executor;
lifecycle_node_executor.add_node(lifecycle_node->get_node_base_interface());
rclcpp::executors::SingleThreadedExecutor client_executor;
client_executor.add_node(lifecycle_client);
// on_change_state
lifecycle_client->change_state(
lifecycle_msgs::msg::Transition::TRANSITION_CONFIGURE);
rclcpp::spin_some(lifecycle_client);
client_executor.spin_some();
EXPECT_THROW(
rclcpp::spin_some(lifecycle_node->get_node_base_interface()), std::runtime_error);
lifecycle_node_executor.spin_some(), std::runtime_error);
// on_get_state
lifecycle_client->get_state();
rclcpp::spin_some(lifecycle_client);
client_executor.spin_some();
EXPECT_THROW(
rclcpp::spin_some(lifecycle_node->get_node_base_interface()), std::runtime_error);
lifecycle_node_executor.spin_some(), std::runtime_error);
// on_get_avilable_states
lifecycle_client->get_available_states();
rclcpp::spin_some(lifecycle_client);
client_executor.spin_some();
EXPECT_THROW(
rclcpp::spin_some(lifecycle_node->get_node_base_interface()), std::runtime_error);
lifecycle_node_executor.spin_some(), std::runtime_error);
// on_get_available_transitions
lifecycle_client->get_available_transitions();
rclcpp::spin_some(lifecycle_client);
client_executor.spin_some();
EXPECT_THROW(
rclcpp::spin_some(lifecycle_node->get_node_base_interface()), std::runtime_error);
lifecycle_node_executor.spin_some(), std::runtime_error);
// on_get_transition_graph
lifecycle_client->get_transition_graph();
rclcpp::spin_some(lifecycle_client);
client_executor.spin_some();
EXPECT_THROW(
rclcpp::spin_some(lifecycle_node->get_node_base_interface()), std::runtime_error);
lifecycle_node_executor.spin_some(), std::runtime_error);
}