Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f160024ba4 | ||
|
|
e6ea37ed5a | ||
|
|
354413c060 | ||
|
|
63bdf2add4 | ||
|
|
ad019b9827 | ||
|
|
eb49444c32 | ||
|
|
4d14414d91 | ||
|
|
7fcc7e723c | ||
|
|
db4f9c21de | ||
|
|
8499404709 | ||
|
|
715a983c9f | ||
|
|
cd9099d85b | ||
|
|
6fbf03945f | ||
|
|
10f0186c6d | ||
|
|
aa60fcf22a | ||
|
|
b14af74a4c | ||
|
|
38f4073474 | ||
|
|
380866b338 | ||
|
|
150a0e8ad9 | ||
|
|
b432df117a | ||
|
|
3644195892 | ||
|
|
bdab46d64e | ||
|
|
82696f6e57 | ||
|
|
e615c7c309 | ||
|
|
37677791ca | ||
|
|
a919a6e5ed |
@@ -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>`_)
|
||||
|
||||
@@ -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/).
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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>`_)
|
||||
|
||||
@@ -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/).
|
||||
|
||||
@@ -350,7 +350,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
goal_handle = it->lock();
|
||||
goal_handle = it->second.lock();
|
||||
}
|
||||
|
||||
if (goal_handle) {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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>`_)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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/).
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>`_)
|
||||
|
||||
@@ -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/).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user