Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13abc1beed | ||
|
|
e77c1fe550 | ||
|
|
00b9d0a018 | ||
|
|
77c7aaf917 | ||
|
|
9019a8d9b7 | ||
|
|
0644f220a2 | ||
|
|
32438a6a67 | ||
|
|
d6bd8baac5 | ||
|
|
623c3eb874 | ||
|
|
7c1143dc15 | ||
|
|
9ff864c6ae | ||
|
|
13182b5aad | ||
|
|
9284d7cefa | ||
|
|
c42745c5ba | ||
|
|
ea31f94eb4 | ||
|
|
f496291e81 | ||
|
|
dd6fad6d42 | ||
|
|
38734d769a | ||
|
|
e103b8d37e | ||
|
|
253d395d4e | ||
|
|
d5e5141b3d | ||
|
|
a0148dfd5d | ||
|
|
5e152d77d8 | ||
|
|
fa732b9ee8 | ||
|
|
bc435776a2 | ||
|
|
43cf0be15c | ||
|
|
fd58bddb05 | ||
|
|
e7f06398db | ||
|
|
ba175922d3 | ||
|
|
77db1ed25b | ||
|
|
403f305b15 | ||
|
|
fd229d72ff | ||
|
|
89f0afe9bc | ||
|
|
a4db4c57a6 | ||
|
|
fbe8f28cd1 | ||
|
|
65f0b70d4a | ||
|
|
9b4b3da3d4 | ||
|
|
cd0440f1a5 | ||
|
|
a17d26b20a | ||
|
|
e2965831d5 | ||
|
|
ea29c142af | ||
|
|
5d6e5fa766 | ||
|
|
22a954e1b0 | ||
|
|
c0d72c3ee0 |
@@ -2,6 +2,54 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
23.2.0 (2023-10-09)
|
||||
-------------------
|
||||
* add clients & services count (`#2072 <https://github.com/ros2/rclcpp/issues/2072>`_)
|
||||
* remove invalid sized allocation test for SerializedMessage. (`#2330 <https://github.com/ros2/rclcpp/issues/2330>`_)
|
||||
* Adding API to copy all parameters from one node to another (`#2304 <https://github.com/ros2/rclcpp/issues/2304>`_)
|
||||
* Contributors: Minju, Lee, Steve Macenski, Tomoya Fujita
|
||||
|
||||
23.1.0 (2023-10-04)
|
||||
-------------------
|
||||
* Add locking to protect the TimeSource::NodeState::node_base\_ (`#2320 <https://github.com/ros2/rclcpp/issues/2320>`_)
|
||||
* Update SignalHandler get_global_signal_handler to avoid complex types in static memory (`#2316 <https://github.com/ros2/rclcpp/issues/2316>`_)
|
||||
* Removing Old Connext Tests (`#2313 <https://github.com/ros2/rclcpp/issues/2313>`_)
|
||||
* Documentation for list_parameters (`#2315 <https://github.com/ros2/rclcpp/issues/2315>`_)
|
||||
* Decouple rosout publisher init from node init. (`#2174 <https://github.com/ros2/rclcpp/issues/2174>`_)
|
||||
* fix the depth to relative in list_parameters (`#2300 <https://github.com/ros2/rclcpp/issues/2300>`_)
|
||||
* Contributors: Chris Lalancette, Lucas Wendland, Minju, Lee, Tomoya Fujita, Tully Foote
|
||||
|
||||
23.0.0 (2023-09-08)
|
||||
-------------------
|
||||
* Fix the return type of Rate::period. (`#2301 <https://github.com/ros2/rclcpp/issues/2301>`_)
|
||||
* Update API docs links in package READMEs (`#2302 <https://github.com/ros2/rclcpp/issues/2302>`_)
|
||||
* Cleanup flaky timers_manager tests. (`#2299 <https://github.com/ros2/rclcpp/issues/2299>`_)
|
||||
* Contributors: Chris Lalancette, Christophe Bedard
|
||||
|
||||
22.2.0 (2023-09-07)
|
||||
-------------------
|
||||
* Topic correct typeadapter deduction (`#2294 <https://github.com/ros2/rclcpp/issues/2294>`_)
|
||||
* Fix C++20 allocator construct deprecation (`#2292 <https://github.com/ros2/rclcpp/issues/2292>`_)
|
||||
* Make Rate to select the clock to work with (`#2123 <https://github.com/ros2/rclcpp/issues/2123>`_)
|
||||
* Correct the position of a comment. (`#2290 <https://github.com/ros2/rclcpp/issues/2290>`_)
|
||||
* Remove unnecessary lambda captures in the tests. (`#2289 <https://github.com/ros2/rclcpp/issues/2289>`_)
|
||||
* Add rcl_logging_interface as an explicit dependency. (`#2284 <https://github.com/ros2/rclcpp/issues/2284>`_)
|
||||
* Revamp list_parameters to be more efficient and easier to read. (`#2282 <https://github.com/ros2/rclcpp/issues/2282>`_)
|
||||
* Contributors: AiVerisimilitude, Alexey Merzlyakov, Chen Lihui, Chris Lalancette, Jiaqi Li
|
||||
|
||||
22.1.0 (2023-08-21)
|
||||
-------------------
|
||||
* Do not crash Executor when send_response fails due to client failure. (`#2276 <https://github.com/ros2/rclcpp/issues/2276>`_)
|
||||
* Adding Custom Unknown Type Error (`#2272 <https://github.com/ros2/rclcpp/issues/2272>`_)
|
||||
* Add a pimpl inside rclcpp::Node for future distro backports (`#2228 <https://github.com/ros2/rclcpp/issues/2228>`_)
|
||||
* Remove an unused variable from the events executor tests. (`#2270 <https://github.com/ros2/rclcpp/issues/2270>`_)
|
||||
* Add spin_all shortcut (`#2246 <https://github.com/ros2/rclcpp/issues/2246>`_)
|
||||
* Adding Missing Group Exceptions (`#2256 <https://github.com/ros2/rclcpp/issues/2256>`_)
|
||||
* Change associated clocks storage to unordered_set (`#2257 <https://github.com/ros2/rclcpp/issues/2257>`_)
|
||||
* associated clocks should be protected by mutex. (`#2255 <https://github.com/ros2/rclcpp/issues/2255>`_)
|
||||
* Instrument loaned message publication code path (`#2240 <https://github.com/ros2/rclcpp/issues/2240>`_)
|
||||
* Contributors: Chris Lalancette, Christophe Bedard, Emerson Knapp, Luca Della Vedova, Lucas Wendland, Tomoya Fujita, Tony Najjar
|
||||
|
||||
22.0.0 (2023-07-11)
|
||||
-------------------
|
||||
* Implement get_node_type_descriptions_interface for lifecyclenode and add smoke test for it (`#2237 <https://github.com/ros2/rclcpp/issues/2237>`_)
|
||||
|
||||
@@ -10,6 +10,7 @@ find_package(builtin_interfaces REQUIRED)
|
||||
find_package(libstatistics_collector REQUIRED)
|
||||
find_package(rcl REQUIRED)
|
||||
find_package(rcl_interfaces REQUIRED)
|
||||
find_package(rcl_logging_interface REQUIRED)
|
||||
find_package(rcl_yaml_param_parser REQUIRED)
|
||||
find_package(rcpputils REQUIRED)
|
||||
find_package(rcutils REQUIRED)
|
||||
@@ -106,6 +107,7 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/qos.cpp
|
||||
src/rclcpp/event_handler.cpp
|
||||
src/rclcpp/qos_overriding_options.cpp
|
||||
src/rclcpp/rate.cpp
|
||||
src/rclcpp/serialization.cpp
|
||||
src/rclcpp/serialized_message.cpp
|
||||
src/rclcpp/service.cpp
|
||||
@@ -207,6 +209,7 @@ ament_target_dependencies(${PROJECT_NAME}
|
||||
"libstatistics_collector"
|
||||
"rcl"
|
||||
"rcl_interfaces"
|
||||
"rcl_logging_interface"
|
||||
"rcl_yaml_param_parser"
|
||||
"rcpputils"
|
||||
"rcutils"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
The ROS client library in C++.
|
||||
|
||||
Visit the [rclcpp API documentation](http://docs.ros2.org/latest/api/rclcpp/) for a complete list of its main components and features.
|
||||
The link to the latest rclcpp API documentation, which includes a complete list of its main components and features, can be found on the rclcpp package info page, at the [ROS Index](https://index.ros.org/p/rclcpp/).
|
||||
|
||||
## Quality Declaration
|
||||
|
||||
|
||||
82
rclcpp/include/rclcpp/copy_all_parameter_values.hpp
Normal file
82
rclcpp/include/rclcpp/copy_all_parameter_values.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright 2023 Open Navigation LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__COPY_ALL_PARAMETER_VALUES_HPP_
|
||||
#define RCLCPP__COPY_ALL_PARAMETER_VALUES_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl_interfaces/srv/list_parameters.hpp"
|
||||
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
|
||||
#include "rcl_interfaces/msg/set_parameters_result.hpp"
|
||||
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/logger.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/**
|
||||
* Copy all parameters from one source node to another destination node.
|
||||
* May throw exceptions if parameters from source are uninitialized or undeclared.
|
||||
* \param source Node to copy parameters from
|
||||
* \param destination Node to copy parameters to
|
||||
* \param override_existing_params Default false. Whether to override existing destination params
|
||||
* if both the source and destination contain the same parameter.
|
||||
*/
|
||||
template<typename NodeT1, typename NodeT2>
|
||||
void
|
||||
copy_all_parameter_values(
|
||||
const NodeT1 & source, const NodeT2 & destination, const bool override_existing_params = false)
|
||||
{
|
||||
using Parameters = std::vector<rclcpp::Parameter>;
|
||||
using Descriptions = std::vector<rcl_interfaces::msg::ParameterDescriptor>;
|
||||
auto source_params = source->get_node_parameters_interface();
|
||||
auto dest_params = destination->get_node_parameters_interface();
|
||||
rclcpp::Logger logger = destination->get_node_logging_interface()->get_logger();
|
||||
|
||||
std::vector<std::string> param_names = source_params->list_parameters({}, 0).names;
|
||||
Parameters params = source_params->get_parameters(param_names);
|
||||
Descriptions descriptions = source_params->describe_parameters(param_names);
|
||||
|
||||
for (unsigned int idx = 0; idx != params.size(); idx++) {
|
||||
if (!dest_params->has_parameter(params[idx].get_name())) {
|
||||
dest_params->declare_parameter(
|
||||
params[idx].get_name(), params[idx].get_parameter_value(), descriptions[idx]);
|
||||
} else if (override_existing_params) {
|
||||
try {
|
||||
rcl_interfaces::msg::SetParametersResult result =
|
||||
dest_params->set_parameters_atomically({params[idx]});
|
||||
if (!result.successful) {
|
||||
// Parameter update rejected or read-only
|
||||
RCLCPP_WARN(
|
||||
logger,
|
||||
"Unable to set parameter (%s): %s!",
|
||||
params[idx].get_name().c_str(), result.reason.c_str());
|
||||
}
|
||||
} catch (const rclcpp::exceptions::InvalidParameterTypeException & e) {
|
||||
RCLCPP_WARN(
|
||||
logger,
|
||||
"Unable to set parameter (%s): incompatable parameter type (%s)!",
|
||||
params[idx].get_name().c_str(), e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__COPY_ALL_PARAMETER_VALUES_HPP_
|
||||
@@ -206,6 +206,14 @@ public:
|
||||
const std::vector<std::string> unknown_ros_args;
|
||||
};
|
||||
|
||||
/// Thrown when an unknown type is passed
|
||||
class UnknownTypeError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit UnknownTypeError(const std::string & type)
|
||||
: std::runtime_error("Unknown type: " + type) {}
|
||||
};
|
||||
|
||||
/// Thrown when an invalid rclcpp::Event object or SharedPtr is encountered.
|
||||
class InvalidEventError : public std::runtime_error
|
||||
{
|
||||
@@ -222,6 +230,14 @@ public:
|
||||
: std::runtime_error("event already registered") {}
|
||||
};
|
||||
|
||||
/// Thrown when a callback group is missing from the node, when it wants to utilize the group.
|
||||
class MissingGroupNodeException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit MissingGroupNodeException(const std::string & obj_type)
|
||||
: std::runtime_error("cannot create: " + obj_type + " , callback group not in node") {}
|
||||
};
|
||||
|
||||
/// Thrown if passed parameters are inconsistent or invalid
|
||||
class InvalidParametersException : public std::runtime_error
|
||||
{
|
||||
|
||||
@@ -297,6 +297,21 @@ public:
|
||||
virtual void
|
||||
spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0));
|
||||
|
||||
/// Add a node, complete all immediately available work exhaustively, and remove the node.
|
||||
/**
|
||||
* \param[in] node Shared pointer to the node to add.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_node_all(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
|
||||
std::chrono::nanoseconds max_duration);
|
||||
|
||||
/// Convenience function which takes Node and forwards NodeBaseInterface.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_node_all(std::shared_ptr<rclcpp::Node> node, std::chrono::nanoseconds max_duration);
|
||||
|
||||
/// Collect and execute work repeatedly within a duration or until no more work is available.
|
||||
/**
|
||||
* This function can be overridden. The default implementation is suitable for a
|
||||
|
||||
@@ -29,6 +29,18 @@
|
||||
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. */
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_all(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
std::chrono::nanoseconds max_duration);
|
||||
|
||||
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. */
|
||||
RCLCPP_PUBLIC
|
||||
|
||||
@@ -486,13 +486,13 @@ private:
|
||||
"subscription use different allocator types, which is not supported");
|
||||
}
|
||||
|
||||
if constexpr (rclcpp::TypeAdapter<MessageT>::is_specialized::value) {
|
||||
if constexpr (rclcpp::TypeAdapter<MessageT, ROSMessageType>::is_specialized::value) {
|
||||
ROSMessageTypeAllocator ros_message_alloc(allocator);
|
||||
auto ptr = ros_message_alloc.allocate(1);
|
||||
ros_message_alloc.construct(ptr);
|
||||
auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_alloc, 1);
|
||||
ROSMessageTypeAllocatorTraits::construct(ros_message_alloc, ptr);
|
||||
ROSMessageTypeDeleter deleter;
|
||||
allocator::set_allocator_for_deleter(&deleter, &allocator);
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*message, *ptr);
|
||||
rclcpp::TypeAdapter<MessageT, ROSMessageType>::convert_to_ros_message(*message, *ptr);
|
||||
auto ros_msg = std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, deleter);
|
||||
ros_message_subscription->provide_intra_process_message(std::move(ros_msg));
|
||||
} else {
|
||||
|
||||
@@ -527,7 +527,7 @@ private:
|
||||
void execute_ready_timers_unsafe();
|
||||
|
||||
// Callback to be called when timer is ready
|
||||
std::function<void(const rclcpp::TimerBase *)> on_ready_callback_ = nullptr;
|
||||
std::function<void(const rclcpp::TimerBase *)> on_ready_callback_;
|
||||
|
||||
// Thread used to run the timers execution task
|
||||
std::thread timers_thread_;
|
||||
|
||||
@@ -972,7 +972,16 @@ public:
|
||||
|
||||
/// Return a list of parameters with any of the given prefixes, up to the given depth.
|
||||
/**
|
||||
* \todo: properly document and test this method.
|
||||
* Parameters are separated into a hierarchy using the "." (dot) character.
|
||||
* The "prefixes" argument is a way to select only particular parts of the hierarchy.
|
||||
*
|
||||
* \param[in] prefixes The list of prefixes that should be searched for within the
|
||||
* current parameters. If this vector of prefixes is empty, then list_parameters
|
||||
* will return all parameters.
|
||||
* \param[in] depth An unsigned integer that represents the recursive depth to search.
|
||||
* If this depth = 0, then all parameters that fit the prefixes will be returned.
|
||||
* \returns A ListParametersResult message which contains both an array of unique prefixes
|
||||
* and an array of names that were matched to the prefixes given.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rcl_interfaces::msg::ListParametersResult
|
||||
@@ -1305,6 +1314,26 @@ public:
|
||||
size_t
|
||||
count_subscribers(const std::string & topic_name) const;
|
||||
|
||||
/// Return the number of clients created for a given service.
|
||||
/**
|
||||
* \param[in] service_name the actual service name used; it will not be automatically remapped.
|
||||
* \return number of clients that have been created for the given service.
|
||||
* \throws std::runtime_error if clients could not be counted
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
count_clients(const std::string & service_name) const;
|
||||
|
||||
/// Return the number of services created for a given service.
|
||||
/**
|
||||
* \param[in] service_name the actual service name used; it will not be automatically remapped.
|
||||
* \return number of services that have been created for the given service.
|
||||
* \throws std::runtime_error if services could not be counted
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
count_services(const std::string & service_name) const;
|
||||
|
||||
/// Return the topic endpoint information about publishers on a given topic.
|
||||
/**
|
||||
* The returned parameter is a list of topic endpoint information, where each item will contain
|
||||
@@ -1600,6 +1629,12 @@ private:
|
||||
const rclcpp::NodeOptions node_options_;
|
||||
const std::string sub_namespace_;
|
||||
const std::string effective_namespace_;
|
||||
|
||||
class NodeImpl;
|
||||
// This member is meant to be a place to backport features into stable distributions,
|
||||
// and new features targeting Rolling should not use this.
|
||||
// See the comment in node.cpp for more information.
|
||||
std::shared_ptr<NodeImpl> hidden_impl_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -113,6 +113,14 @@ public:
|
||||
size_t
|
||||
count_subscribers(const std::string & topic_name) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
count_clients(const std::string & service_name) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
count_services(const std::string & service_name) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rcl_guard_condition_t *
|
||||
get_graph_guard_condition() const override;
|
||||
|
||||
@@ -305,6 +305,24 @@ public:
|
||||
size_t
|
||||
count_subscribers(const std::string & topic_name) const = 0;
|
||||
|
||||
/// Return the number of clients created for a given service.
|
||||
/*
|
||||
* \param[in] service_name the actual service name used; it will not be automatically remapped.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
size_t
|
||||
count_clients(const std::string & service_name) const = 0;
|
||||
|
||||
/// Return the number of services created for a given service.
|
||||
/*
|
||||
* \param[in] service_name the actual service name used; it will not be automatically remapped.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
size_t
|
||||
count_services(const std::string & service_name) const = 0;
|
||||
|
||||
/// Return the rcl guard condition which is triggered when the ROS graph changes.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "rcl_interfaces/msg/parameter_type.hpp"
|
||||
#include "rcl_interfaces/msg/parameter_value.hpp"
|
||||
#include "rclcpp/exceptions/exceptions.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
|
||||
@@ -456,6 +456,7 @@ protected:
|
||||
do_loaned_message_publish(
|
||||
std::unique_ptr<ROSMessageType, std::function<void(ROSMessageType *)>> msg)
|
||||
{
|
||||
TRACETOOLS_TRACEPOINT(rclcpp_publish, nullptr, static_cast<const void *>(msg.get()));
|
||||
auto status = rcl_publish_loaned_message(publisher_handle_.get(), msg.get(), nullptr);
|
||||
|
||||
if (RCL_RET_PUBLISHER_INVALID == status) {
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "rclcpp/clock.hpp"
|
||||
#include "rclcpp/duration.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
@@ -31,9 +33,20 @@ class RateBase
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(RateBase)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~RateBase() {}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual bool sleep() = 0;
|
||||
|
||||
[[deprecated("use get_type() instead")]]
|
||||
RCLCPP_PUBLIC
|
||||
virtual bool is_steady() const = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual rcl_clock_type_t get_type() const = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual void reset() = 0;
|
||||
};
|
||||
|
||||
@@ -42,14 +55,13 @@ using std::chrono::duration_cast;
|
||||
using std::chrono::nanoseconds;
|
||||
|
||||
template<class Clock = std::chrono::high_resolution_clock>
|
||||
class GenericRate : public RateBase
|
||||
class [[deprecated("use rclcpp::Rate class instead of GenericRate")]] GenericRate : public RateBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(GenericRate)
|
||||
|
||||
explicit GenericRate(double rate)
|
||||
: GenericRate<Clock>(
|
||||
duration_cast<nanoseconds>(duration<double>(1.0 / rate)))
|
||||
: period_(duration_cast<nanoseconds>(duration<double>(1.0 / rate))), last_interval_(Clock::now())
|
||||
{}
|
||||
explicit GenericRate(std::chrono::nanoseconds period)
|
||||
: period_(period), last_interval_(Clock::now())
|
||||
@@ -87,12 +99,18 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
[[deprecated("use get_type() instead")]]
|
||||
virtual bool
|
||||
is_steady() const
|
||||
{
|
||||
return Clock::is_steady;
|
||||
}
|
||||
|
||||
virtual rcl_clock_type_t get_type() const
|
||||
{
|
||||
return Clock::is_steady ? RCL_STEADY_TIME : RCL_SYSTEM_TIME;
|
||||
}
|
||||
|
||||
virtual void
|
||||
reset()
|
||||
{
|
||||
@@ -112,8 +130,69 @@ private:
|
||||
std::chrono::time_point<Clock, ClockDurationNano> last_interval_;
|
||||
};
|
||||
|
||||
using Rate = GenericRate<std::chrono::system_clock>;
|
||||
using WallRate = GenericRate<std::chrono::steady_clock>;
|
||||
class Rate : public RateBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Rate)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit Rate(
|
||||
const double rate,
|
||||
Clock::SharedPtr clock = std::make_shared<Clock>(RCL_SYSTEM_TIME));
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit Rate(
|
||||
const Duration & period,
|
||||
Clock::SharedPtr clock = std::make_shared<Clock>(RCL_SYSTEM_TIME));
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual bool
|
||||
sleep();
|
||||
|
||||
[[deprecated("use get_type() instead")]]
|
||||
RCLCPP_PUBLIC
|
||||
virtual bool
|
||||
is_steady() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual rcl_clock_type_t
|
||||
get_type() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
reset();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::chrono::nanoseconds
|
||||
period() const;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Rate)
|
||||
|
||||
Clock::SharedPtr clock_;
|
||||
Duration period_;
|
||||
Time last_interval_;
|
||||
};
|
||||
|
||||
class WallRate : public Rate
|
||||
{
|
||||
public:
|
||||
RCLCPP_PUBLIC
|
||||
explicit WallRate(const double rate);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit WallRate(const Duration & period);
|
||||
};
|
||||
|
||||
class ROSRate : public Rate
|
||||
{
|
||||
public:
|
||||
RCLCPP_PUBLIC
|
||||
explicit ROSRate(const double rate);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit ROSRate(const Duration & period);
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
* - rclcpp::ParameterValue
|
||||
* - rclcpp::AsyncParametersClient
|
||||
* - rclcpp::SyncParametersClient
|
||||
* - rclcpp::copy_all_parameter_values()
|
||||
* - rclcpp/parameter.hpp
|
||||
* - rclcpp/parameter_value.hpp
|
||||
* - rclcpp/parameter_client.hpp
|
||||
@@ -95,6 +96,9 @@
|
||||
* - Get the number of publishers or subscribers on a topic:
|
||||
* - rclcpp::Node::count_publishers()
|
||||
* - rclcpp::Node::count_subscribers()
|
||||
* - Get the number of clients or servers on a service:
|
||||
* - rclcpp::Node::count_clients()
|
||||
* - rclcpp::Node::count_services()
|
||||
*
|
||||
* And components related to logging:
|
||||
*
|
||||
@@ -164,6 +168,7 @@
|
||||
#include <csignal>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/copy_all_parameter_values.hpp"
|
||||
#include "rclcpp/executors.hpp"
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
|
||||
@@ -486,6 +486,14 @@ public:
|
||||
{
|
||||
rcl_ret_t ret = rcl_send_response(get_service_handle().get(), &req_id, &response);
|
||||
|
||||
if (ret == RCL_RET_TIMEOUT) {
|
||||
RCLCPP_WARN(
|
||||
node_logger_.get_child("rclcpp"),
|
||||
"failed to send response to %s (timeout): %s",
|
||||
this->get_service_name(), rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
return;
|
||||
}
|
||||
if (ret != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send response");
|
||||
}
|
||||
|
||||
@@ -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>22.0.0</version>
|
||||
<version>23.2.0</version>
|
||||
<description>The ROS client library in C++.</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
<depend>libstatistics_collector</depend>
|
||||
<depend>rcl</depend>
|
||||
<depend>rcl_logging_interface</depend>
|
||||
<depend>rcl_yaml_param_parser</depend>
|
||||
<depend>rcpputils</depend>
|
||||
<depend>rcutils</depend>
|
||||
|
||||
@@ -125,7 +125,6 @@ bool
|
||||
ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
|
||||
{
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
// make an event to reuse, rather than create a new one each time
|
||||
auto node_ptr = node_graph_.lock();
|
||||
if (!node_ptr) {
|
||||
throw InvalidNodeError();
|
||||
@@ -138,6 +137,7 @@ ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
|
||||
// check was non-blocking, return immediately
|
||||
return false;
|
||||
}
|
||||
// make an event to reuse, rather than create a new one each time
|
||||
auto event = node_ptr->get_graph_event();
|
||||
// update the time even on the first loop to account for time spent in the first call
|
||||
// to this->server_is_ready()
|
||||
|
||||
@@ -431,6 +431,22 @@ void Executor::spin_some(std::chrono::nanoseconds max_duration)
|
||||
return this->spin_some_impl(max_duration, false);
|
||||
}
|
||||
|
||||
void
|
||||
Executor::spin_node_all(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
|
||||
std::chrono::nanoseconds max_duration)
|
||||
{
|
||||
this->add_node(node, false);
|
||||
spin_all(max_duration);
|
||||
this->remove_node(node, false);
|
||||
}
|
||||
|
||||
void
|
||||
Executor::spin_node_all(std::shared_ptr<rclcpp::Node> node, std::chrono::nanoseconds max_duration)
|
||||
{
|
||||
this->spin_node_all(node->get_node_base_interface(), max_duration);
|
||||
}
|
||||
|
||||
void Executor::spin_all(std::chrono::nanoseconds max_duration)
|
||||
{
|
||||
if (max_duration < 0ns) {
|
||||
|
||||
@@ -14,6 +14,21 @@
|
||||
|
||||
#include "rclcpp/executors.hpp"
|
||||
|
||||
void
|
||||
rclcpp::spin_all(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
std::chrono::nanoseconds max_duration)
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor exec;
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -28,9 +28,9 @@ using rclcpp::experimental::TimersManager;
|
||||
TimersManager::TimersManager(
|
||||
std::shared_ptr<rclcpp::Context> context,
|
||||
std::function<void(const rclcpp::TimerBase *)> on_ready_callback)
|
||||
: on_ready_callback_(on_ready_callback),
|
||||
context_(context)
|
||||
{
|
||||
context_ = context;
|
||||
on_ready_callback_ = on_ready_callback;
|
||||
}
|
||||
|
||||
TimersManager::~TimersManager()
|
||||
|
||||
@@ -110,6 +110,22 @@ create_effective_namespace(const std::string & node_namespace, const std::string
|
||||
|
||||
} // namespace
|
||||
|
||||
/// Internal implementation to provide hidden and API/ABI stable changes to the Node.
|
||||
/**
|
||||
* This class is intended to be an "escape hatch" within a stable distribution, so that certain
|
||||
* smaller features and bugfixes can be backported, having a place to put new members, while
|
||||
* maintaining the ABI.
|
||||
*
|
||||
* This is not intended to be a parking place for new features, it should be used for backports
|
||||
* only, left empty and unallocated in Rolling.
|
||||
*/
|
||||
class Node::NodeImpl
|
||||
{
|
||||
public:
|
||||
NodeImpl() = default;
|
||||
~NodeImpl() = default;
|
||||
};
|
||||
|
||||
Node::Node(
|
||||
const std::string & node_name,
|
||||
const NodeOptions & options)
|
||||
@@ -253,7 +269,8 @@ Node::Node(
|
||||
node_waitables_(other.node_waitables_),
|
||||
node_options_(other.node_options_),
|
||||
sub_namespace_(extend_sub_namespace(other.get_sub_namespace(), sub_namespace)),
|
||||
effective_namespace_(create_effective_namespace(other.get_namespace(), sub_namespace_))
|
||||
effective_namespace_(create_effective_namespace(other.get_namespace(), sub_namespace_)),
|
||||
hidden_impl_(other.hidden_impl_)
|
||||
{
|
||||
// Validate new effective namespace.
|
||||
int validation_result;
|
||||
@@ -505,6 +522,18 @@ Node::count_subscribers(const std::string & topic_name) const
|
||||
return node_graph_->count_subscribers(topic_name);
|
||||
}
|
||||
|
||||
size_t
|
||||
Node::count_clients(const std::string & service_name) const
|
||||
{
|
||||
return node_graph_->count_clients(service_name);
|
||||
}
|
||||
|
||||
size_t
|
||||
Node::count_services(const std::string & service_name) const
|
||||
{
|
||||
return node_graph_->count_services(service_name);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::TopicEndpointInfo>
|
||||
Node::get_publishers_info_by_topic(const std::string & topic_name, bool no_mangle) const
|
||||
{
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#include "rcl/arguments.h"
|
||||
#include "rcl/node_type_cache.h"
|
||||
#include "rcl/logging.h"
|
||||
#include "rcl/logging_rosout.h"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rcutils/logging_macros.h"
|
||||
#include "rmw/validate_namespace.h"
|
||||
@@ -55,17 +57,12 @@ NodeBase::NodeBase(
|
||||
std::shared_ptr<std::recursive_mutex> logging_mutex = get_global_logging_mutex();
|
||||
|
||||
rcl_ret_t ret;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
|
||||
// TODO(ivanpauno): /rosout Qos should be reconfigurable.
|
||||
// TODO(ivanpauno): Instead of mutually excluding rcl_node_init with the global logger mutex,
|
||||
// rcl_logging_rosout_init_publisher_for_node could be decoupled from there and be called
|
||||
// here directly.
|
||||
ret = rcl_node_init(
|
||||
rcl_node.get(),
|
||||
node_name.c_str(), namespace_.c_str(),
|
||||
context_->get_rcl_context().get(), &rcl_node_options);
|
||||
}
|
||||
|
||||
// TODO(ivanpauno): /rosout Qos should be reconfigurable.
|
||||
ret = rcl_node_init(
|
||||
rcl_node.get(),
|
||||
node_name.c_str(), namespace_.c_str(),
|
||||
context_->get_rcl_context().get(), &rcl_node_options);
|
||||
if (ret != RCL_RET_OK) {
|
||||
if (ret == RCL_RET_NODE_INVALID_NAME) {
|
||||
rcl_reset_error(); // discard rcl_node_init error
|
||||
@@ -115,13 +112,29 @@ NodeBase::NodeBase(
|
||||
throw_from_rcl_error(ret, "failed to initialize rcl node");
|
||||
}
|
||||
|
||||
// The initialization for the rosout publisher
|
||||
if (rcl_logging_rosout_enabled() && rcl_node_options.enable_rosout) {
|
||||
std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
|
||||
ret = rcl_logging_rosout_init_publisher_for_node(rcl_node.get());
|
||||
if (ret != RCL_RET_OK) {
|
||||
throw_from_rcl_error(ret, "failed to initialize rosout publisher");
|
||||
}
|
||||
}
|
||||
|
||||
node_handle_.reset(
|
||||
rcl_node.release(),
|
||||
[logging_mutex](rcl_node_t * node) -> void {
|
||||
std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
|
||||
// TODO(ivanpauno): Instead of mutually excluding rcl_node_fini with the global logger mutex,
|
||||
// rcl_logging_rosout_fini_publisher_for_node could be decoupled from there and be called
|
||||
// here directly.
|
||||
[logging_mutex, rcl_node_options](rcl_node_t * node) -> void {
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
|
||||
if (rcl_logging_rosout_enabled() && rcl_node_options.enable_rosout) {
|
||||
rcl_ret_t ret = rcl_logging_rosout_fini_publisher_for_node(node);
|
||||
if (ret != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Error in destruction of rosout publisher: %s", rcl_get_error_string().str);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rcl_node_fini(node) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
|
||||
@@ -498,6 +498,50 @@ NodeGraph::count_subscribers(const std::string & topic_name) const
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t
|
||||
NodeGraph::count_clients(const std::string & service_name) const
|
||||
{
|
||||
auto rcl_node_handle = node_base_->get_rcl_node_handle();
|
||||
|
||||
auto fqdn = rclcpp::expand_topic_or_service_name(
|
||||
service_name,
|
||||
rcl_node_get_name(rcl_node_handle),
|
||||
rcl_node_get_namespace(rcl_node_handle),
|
||||
true);
|
||||
|
||||
size_t count;
|
||||
auto ret = rcl_count_clients(rcl_node_handle, fqdn.c_str(), &count);
|
||||
if (ret != RMW_RET_OK) {
|
||||
// *INDENT-OFF*
|
||||
throw std::runtime_error(
|
||||
std::string("could not count clients: ") + rmw_get_error_string().str);
|
||||
// *INDENT-ON*
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t
|
||||
NodeGraph::count_services(const std::string & service_name) const
|
||||
{
|
||||
auto rcl_node_handle = node_base_->get_rcl_node_handle();
|
||||
|
||||
auto fqdn = rclcpp::expand_topic_or_service_name(
|
||||
service_name,
|
||||
rcl_node_get_name(rcl_node_handle),
|
||||
rcl_node_get_namespace(rcl_node_handle),
|
||||
true);
|
||||
|
||||
size_t count;
|
||||
auto ret = rcl_count_services(rcl_node_handle, fqdn.c_str(), &count);
|
||||
if (ret != RMW_RET_OK) {
|
||||
// *INDENT-OFF*
|
||||
throw std::runtime_error(
|
||||
std::string("could not count services: ") + rmw_get_error_string().str);
|
||||
// *INDENT-ON*
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
const rcl_guard_condition_t *
|
||||
NodeGraph::get_graph_guard_condition() const
|
||||
{
|
||||
|
||||
@@ -1038,37 +1038,50 @@ NodeParameters::list_parameters(const std::vector<std::string> & prefixes, uint6
|
||||
// TODO(mikaelarguedas) define parameter separator different from "/" to avoid ambiguity
|
||||
// using "." for now
|
||||
const char * separator = ".";
|
||||
for (auto & kv : parameters_) {
|
||||
bool get_all = (prefixes.size() == 0) &&
|
||||
((depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) ||
|
||||
(static_cast<uint64_t>(std::count(kv.first.begin(), kv.first.end(), *separator)) < depth));
|
||||
bool prefix_matches = std::any_of(
|
||||
prefixes.cbegin(), prefixes.cend(),
|
||||
[&kv, &depth, &separator](const std::string & prefix) {
|
||||
if (kv.first == prefix) {
|
||||
return true;
|
||||
} else if (kv.first.find(prefix + separator) == 0) {
|
||||
size_t length = prefix.length();
|
||||
std::string substr = kv.first.substr(length);
|
||||
// Cast as unsigned integer to avoid warning
|
||||
return (depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) ||
|
||||
(static_cast<uint64_t>(std::count(substr.begin(), substr.end(), *separator)) < depth);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (get_all || prefix_matches) {
|
||||
result.names.push_back(kv.first);
|
||||
size_t last_separator = kv.first.find_last_of(separator);
|
||||
if (std::string::npos != last_separator) {
|
||||
std::string prefix = kv.first.substr(0, last_separator);
|
||||
if (
|
||||
std::find(result.prefixes.cbegin(), result.prefixes.cend(), prefix) ==
|
||||
result.prefixes.cend())
|
||||
{
|
||||
result.prefixes.push_back(prefix);
|
||||
|
||||
auto separators_less_than_depth = [&depth, &separator](const std::string & str) -> bool {
|
||||
return static_cast<uint64_t>(std::count(str.begin(), str.end(), *separator)) < depth;
|
||||
};
|
||||
|
||||
bool recursive = (prefixes.size() == 0) &&
|
||||
(depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE);
|
||||
|
||||
for (const std::pair<const std::string, ParameterInfo> & kv : parameters_) {
|
||||
if (!recursive) {
|
||||
bool get_all = (prefixes.size() == 0) && separators_less_than_depth(kv.first);
|
||||
if (!get_all) {
|
||||
bool prefix_matches = std::any_of(
|
||||
prefixes.cbegin(), prefixes.cend(),
|
||||
[&kv, &depth, &separator, &separators_less_than_depth](const std::string & prefix) {
|
||||
if (kv.first == prefix) {
|
||||
return true;
|
||||
} else if (kv.first.find(prefix + separator) == 0) {
|
||||
if (depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) {
|
||||
return true;
|
||||
}
|
||||
std::string substr = kv.first.substr(prefix.length() + 1);
|
||||
return separators_less_than_depth(substr);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!prefix_matches) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.names.push_back(kv.first);
|
||||
size_t last_separator = kv.first.find_last_of(separator);
|
||||
if (std::string::npos != last_separator) {
|
||||
std::string prefix = kv.first.substr(0, last_separator);
|
||||
if (
|
||||
std::find(result.prefixes.cbegin(), result.prefixes.cend(), prefix) ==
|
||||
result.prefixes.cend())
|
||||
{
|
||||
result.prefixes.push_back(prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -32,8 +32,7 @@ NodeServices::add_service(
|
||||
{
|
||||
if (group) {
|
||||
if (!node_base_->callback_group_in_node(group)) {
|
||||
// TODO(jacquelinekay): use custom exception
|
||||
throw std::runtime_error("Cannot create service, group not in node.");
|
||||
throw rclcpp::exceptions::MissingGroupNodeException("service");
|
||||
}
|
||||
} else {
|
||||
group = node_base_->get_default_callback_group();
|
||||
@@ -58,8 +57,7 @@ NodeServices::add_client(
|
||||
{
|
||||
if (group) {
|
||||
if (!node_base_->callback_group_in_node(group)) {
|
||||
// TODO(jacquelinekay): use custom exception
|
||||
throw std::runtime_error("Cannot create client, group not in node.");
|
||||
throw rclcpp::exceptions::MissingGroupNodeException("client");
|
||||
}
|
||||
} else {
|
||||
group = node_base_->get_default_callback_group();
|
||||
|
||||
@@ -34,8 +34,7 @@ NodeTimers::add_timer(
|
||||
{
|
||||
if (callback_group) {
|
||||
if (!node_base_->callback_group_in_node(callback_group)) {
|
||||
// TODO(jacquelinekay): use custom exception
|
||||
throw std::runtime_error("Cannot create timer, group not in node.");
|
||||
throw rclcpp::exceptions::MissingGroupNodeException("timer");
|
||||
}
|
||||
} else {
|
||||
callback_group = node_base_->get_default_callback_group();
|
||||
|
||||
@@ -58,7 +58,7 @@ NodeTopics::add_publisher(
|
||||
// Assign to a group.
|
||||
if (callback_group) {
|
||||
if (!node_base_->callback_group_in_node(callback_group)) {
|
||||
throw std::runtime_error("Cannot create publisher, callback group not in node.");
|
||||
throw rclcpp::exceptions::MissingGroupNodeException("publisher");
|
||||
}
|
||||
} else {
|
||||
callback_group = node_base_->get_default_callback_group();
|
||||
@@ -97,8 +97,7 @@ NodeTopics::add_subscription(
|
||||
// Assign to a group.
|
||||
if (callback_group) {
|
||||
if (!node_base_->callback_group_in_node(callback_group)) {
|
||||
// TODO(jacquelinekay): use custom exception
|
||||
throw std::runtime_error("Cannot create subscription, callback group not in node.");
|
||||
throw rclcpp::exceptions::MissingGroupNodeException("subscription");
|
||||
}
|
||||
} else {
|
||||
callback_group = node_base_->get_default_callback_group();
|
||||
|
||||
@@ -32,8 +32,7 @@ NodeWaitables::add_waitable(
|
||||
{
|
||||
if (group) {
|
||||
if (!node_base_->callback_group_in_node(group)) {
|
||||
// TODO(jacobperron): use custom exception
|
||||
throw std::runtime_error("Cannot create waitable, group not in node.");
|
||||
throw rclcpp::exceptions::MissingGroupNodeException("waitable");
|
||||
}
|
||||
} else {
|
||||
group = node_base_->get_default_callback_group();
|
||||
|
||||
@@ -129,8 +129,7 @@ ParameterValue::ParameterValue(const rcl_interfaces::msg::ParameterValue & value
|
||||
case PARAMETER_NOT_SET:
|
||||
break;
|
||||
default:
|
||||
// TODO(wjwwood): use custom exception
|
||||
throw std::runtime_error("Unknown type: " + std::to_string(value.type));
|
||||
throw rclcpp::exceptions::UnknownTypeError(std::to_string(value.type));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
113
rclcpp/src/rclcpp/rate.cpp
Normal file
113
rclcpp/src/rclcpp/rate.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2023 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/rate.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
Rate::Rate(
|
||||
const double rate, Clock::SharedPtr clock)
|
||||
: clock_(clock), period_(0, 0), last_interval_(clock_->now())
|
||||
{
|
||||
if (rate <= 0.0) {
|
||||
throw std::invalid_argument{"rate must be greater than 0"};
|
||||
}
|
||||
period_ = Duration::from_seconds(1.0 / rate);
|
||||
}
|
||||
|
||||
Rate::Rate(
|
||||
const Duration & period, Clock::SharedPtr clock)
|
||||
: clock_(clock), period_(period), last_interval_(clock_->now())
|
||||
{
|
||||
if (period <= Duration(0, 0)) {
|
||||
throw std::invalid_argument{"period must be greater than 0"};
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Rate::sleep()
|
||||
{
|
||||
// Time coming into sleep
|
||||
auto now = clock_->now();
|
||||
// Time of next interval
|
||||
auto next_interval = last_interval_ + period_;
|
||||
// Detect backwards time flow
|
||||
if (now < last_interval_) {
|
||||
// Best thing to do is to set the next_interval to now + period
|
||||
next_interval = now + period_;
|
||||
}
|
||||
// Update the interval
|
||||
last_interval_ += period_;
|
||||
// If the time_to_sleep is negative or zero, don't sleep
|
||||
if (next_interval <= now) {
|
||||
// If an entire cycle was missed then reset next interval.
|
||||
// This might happen if the loop took more than a cycle.
|
||||
// Or if time jumps forward.
|
||||
if (now > next_interval + period_) {
|
||||
last_interval_ = now + period_;
|
||||
}
|
||||
// Either way do not sleep and return false
|
||||
return false;
|
||||
}
|
||||
// Calculate the time to sleep
|
||||
auto time_to_sleep = next_interval - now;
|
||||
// Sleep (will get interrupted by ctrl-c, may not sleep full time)
|
||||
return clock_->sleep_for(time_to_sleep);
|
||||
}
|
||||
|
||||
bool
|
||||
Rate::is_steady() const
|
||||
{
|
||||
return clock_->get_clock_type() == RCL_STEADY_TIME;
|
||||
}
|
||||
|
||||
rcl_clock_type_t
|
||||
Rate::get_type() const
|
||||
{
|
||||
return clock_->get_clock_type();
|
||||
}
|
||||
|
||||
void
|
||||
Rate::reset()
|
||||
{
|
||||
last_interval_ = clock_->now();
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds
|
||||
Rate::period() const
|
||||
{
|
||||
return std::chrono::nanoseconds(period_.nanoseconds());
|
||||
}
|
||||
|
||||
WallRate::WallRate(const double rate)
|
||||
: Rate(rate, std::make_shared<Clock>(RCL_STEADY_TIME))
|
||||
{}
|
||||
|
||||
WallRate::WallRate(const Duration & period)
|
||||
: Rate(period, std::make_shared<Clock>(RCL_STEADY_TIME))
|
||||
{}
|
||||
|
||||
ROSRate::ROSRate(const double rate)
|
||||
: Rate(rate, std::make_shared<Clock>(RCL_ROS_TIME))
|
||||
{}
|
||||
|
||||
ROSRate::ROSRate(const Duration & period)
|
||||
: Rate(period, std::make_shared<Clock>(RCL_ROS_TIME))
|
||||
{}
|
||||
|
||||
} // namespace rclcpp
|
||||
@@ -113,7 +113,7 @@ SignalHandler::get_logger()
|
||||
SignalHandler &
|
||||
SignalHandler::get_global_signal_handler()
|
||||
{
|
||||
static SignalHandler signal_handler;
|
||||
static SignalHandler & signal_handler = *new SignalHandler();
|
||||
return signal_handler;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -54,9 +55,7 @@ public:
|
||||
ros_time_active_ = true;
|
||||
|
||||
// Update all attached clocks to zero or last recorded time
|
||||
for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
|
||||
set_clock(last_time_msg_, true, *it);
|
||||
}
|
||||
set_all_clocks(last_time_msg_, true);
|
||||
}
|
||||
|
||||
// An internal method to use in the clock callback that iterates and disables all clocks
|
||||
@@ -71,11 +70,8 @@ public:
|
||||
ros_time_active_ = false;
|
||||
|
||||
// Update all attached clocks
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
|
||||
auto msg = std::make_shared<builtin_interfaces::msg::Time>();
|
||||
set_clock(msg, false, *it);
|
||||
}
|
||||
auto msg = std::make_shared<builtin_interfaces::msg::Time>();
|
||||
set_all_clocks(msg, false);
|
||||
}
|
||||
|
||||
// Check if ROS time is active
|
||||
@@ -95,7 +91,7 @@ public:
|
||||
}
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
associated_clocks_.push_back(clock);
|
||||
associated_clocks_.insert(clock);
|
||||
// Set the clock to zero unless there's a recently received message
|
||||
set_clock(last_time_msg_, ros_time_active_, clock);
|
||||
}
|
||||
@@ -104,10 +100,8 @@ public:
|
||||
void detachClock(rclcpp::Clock::SharedPtr clock)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
auto result = std::find(associated_clocks_.begin(), associated_clocks_.end(), clock);
|
||||
if (result != associated_clocks_.end()) {
|
||||
associated_clocks_.erase(result);
|
||||
} else {
|
||||
auto removed = associated_clocks_.erase(clock);
|
||||
if (removed == 0) {
|
||||
RCLCPP_ERROR(logger_, "failed to remove clock");
|
||||
}
|
||||
}
|
||||
@@ -184,8 +178,8 @@ private:
|
||||
|
||||
// A lock to protect iterating the associated_clocks_ field.
|
||||
std::mutex clock_list_lock_;
|
||||
// A vector to store references to associated clocks.
|
||||
std::vector<rclcpp::Clock::SharedPtr> associated_clocks_;
|
||||
// An unordered_set to store references to associated clocks.
|
||||
std::unordered_set<rclcpp::Clock::SharedPtr> associated_clocks_;
|
||||
|
||||
// Local storage of validity of ROS time
|
||||
// This is needed when new clocks are added.
|
||||
@@ -242,6 +236,7 @@ public:
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_interface,
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_interface)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(node_base_lock_);
|
||||
node_base_ = node_base_interface;
|
||||
node_topics_ = node_topics_interface;
|
||||
node_graph_ = node_graph_interface;
|
||||
@@ -286,17 +281,14 @@ public:
|
||||
parameter_subscription_ = rclcpp::AsyncParametersClient::on_parameter_event(
|
||||
node_topics_,
|
||||
[this](std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event) {
|
||||
if (node_base_ != nullptr) {
|
||||
this->on_parameter_event(event);
|
||||
}
|
||||
// Do nothing if node_base_ is nullptr because it means the TimeSource is now
|
||||
// without an attached node
|
||||
this->on_parameter_event(event);
|
||||
});
|
||||
}
|
||||
|
||||
// Detach the attached node
|
||||
void detachNode()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(node_base_lock_);
|
||||
// destroy_clock_sub() *must* be first here, to ensure that the executor
|
||||
// can't possibly call any of the callbacks as we are cleaning up.
|
||||
destroy_clock_sub();
|
||||
@@ -333,6 +325,7 @@ private:
|
||||
std::thread clock_executor_thread_;
|
||||
|
||||
// Preserve the node reference
|
||||
std::mutex node_base_lock_;
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_{nullptr};
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_{nullptr};
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_{nullptr};
|
||||
@@ -470,6 +463,14 @@ private:
|
||||
// Callback for parameter updates
|
||||
void on_parameter_event(std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(node_base_lock_);
|
||||
|
||||
if (node_base_ == nullptr) {
|
||||
// Do nothing if node_base_ is nullptr because it means the TimeSource is now
|
||||
// without an attached node
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter out events on 'use_sim_time' parameter instances in other nodes.
|
||||
if (event->node != node_base_->get_fully_qualified_name()) {
|
||||
return;
|
||||
|
||||
@@ -34,13 +34,7 @@ if(TARGET test_exceptions)
|
||||
target_link_libraries(test_exceptions ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
|
||||
# Increasing timeout because connext can take a long time to destroy nodes
|
||||
# TODO(brawner) remove when destroying Node for Connext is resolved. See:
|
||||
# https://github.com/ros2/rclcpp/issues/1250
|
||||
ament_add_gtest(
|
||||
test_allocator_memory_strategy
|
||||
strategies/test_allocator_memory_strategy.cpp
|
||||
TIMEOUT 360)
|
||||
ament_add_gtest(test_allocator_memory_strategy strategies/test_allocator_memory_strategy.cpp)
|
||||
if(TARGET test_allocator_memory_strategy)
|
||||
ament_target_dependencies(test_allocator_memory_strategy
|
||||
"rcl"
|
||||
@@ -81,6 +75,13 @@ if(TARGET test_client)
|
||||
)
|
||||
target_link_libraries(test_client ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
ament_add_gtest(test_copy_all_parameter_values test_copy_all_parameter_values.cpp)
|
||||
if(TARGET test_copy_all_parameter_values)
|
||||
ament_target_dependencies(test_copy_all_parameter_values
|
||||
"rcl_interfaces"
|
||||
)
|
||||
target_link_libraries(test_copy_all_parameter_values ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_create_timer test_create_timer.cpp)
|
||||
if(TARGET test_create_timer)
|
||||
ament_target_dependencies(test_create_timer
|
||||
@@ -669,8 +670,6 @@ if(TARGET test_interface_traits)
|
||||
target_link_libraries(test_interface_traits ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
# TODO(brawner) remove when destroying Node for Connext is resolved. See:
|
||||
# https://github.com/ros2/rclcpp/issues/1250
|
||||
ament_add_gtest(
|
||||
test_executors
|
||||
executors/test_executors.cpp
|
||||
|
||||
@@ -60,7 +60,7 @@ TEST_F(TestEventsExecutor, run_pub_sub)
|
||||
executor.add_node(node);
|
||||
|
||||
bool spin_exited = false;
|
||||
std::thread spinner([&spin_exited, &executor, this]() {
|
||||
std::thread spinner([&spin_exited, &executor]() {
|
||||
executor.spin();
|
||||
spin_exited = true;
|
||||
});
|
||||
@@ -75,8 +75,6 @@ TEST_F(TestEventsExecutor, run_pub_sub)
|
||||
!spin_exited &&
|
||||
(std::chrono::high_resolution_clock::now() - start < 1s))
|
||||
{
|
||||
auto time = std::chrono::high_resolution_clock::now() - start;
|
||||
auto time_msec = std::chrono::duration_cast<std::chrono::milliseconds>(time);
|
||||
std::this_thread::sleep_for(25ms);
|
||||
}
|
||||
|
||||
@@ -109,7 +107,7 @@ TEST_F(TestEventsExecutor, run_clients_servers)
|
||||
executor.add_node(node);
|
||||
|
||||
bool spin_exited = false;
|
||||
std::thread spinner([&spin_exited, &executor, this]() {
|
||||
std::thread spinner([&spin_exited, &executor]() {
|
||||
executor.spin();
|
||||
spin_exited = true;
|
||||
});
|
||||
@@ -348,7 +346,7 @@ TEST_F(TestEventsExecutor, cancel_while_timers_running)
|
||||
});
|
||||
|
||||
|
||||
std::thread spinner([&executor, this]() {executor.spin();});
|
||||
std::thread spinner([&executor]() {executor.spin();});
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
// Call cancel while t1 callback is still being executed
|
||||
@@ -375,7 +373,7 @@ TEST_F(TestEventsExecutor, cancel_while_timers_waiting)
|
||||
executor.add_node(node);
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
std::thread spinner([&executor, this]() {executor.spin();});
|
||||
std::thread spinner([&executor]() {executor.spin();});
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
executor.cancel();
|
||||
@@ -397,7 +395,7 @@ TEST_F(TestEventsExecutor, destroy_entities)
|
||||
2ms, [&]() {publisher->publish(std::make_unique<test_msgs::msg::Empty>());});
|
||||
EventsExecutor executor_pub;
|
||||
executor_pub.add_node(node_pub);
|
||||
std::thread spinner([&executor_pub, this]() {executor_pub.spin();});
|
||||
std::thread spinner([&executor_pub]() {executor_pub.spin();});
|
||||
|
||||
// Create a node with two different subscriptions to the topic
|
||||
auto node_sub = std::make_shared<rclcpp::Node>("node_sub");
|
||||
|
||||
@@ -779,7 +779,7 @@ TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) {
|
||||
// that publishers aren't continuing to publish.
|
||||
// This was previously broken in that intraprocess guard conditions were only triggered on
|
||||
// publish and the test was added to prevent future regressions.
|
||||
const size_t kNumMessages = 100;
|
||||
static constexpr size_t kNumMessages = 100;
|
||||
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
@@ -808,11 +808,9 @@ TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) {
|
||||
// Fire a timer every 10ms up to 5 seconds waiting for subscriptions to be read.
|
||||
loops = 0;
|
||||
auto timer = this->node->create_wall_timer(
|
||||
std::chrono::milliseconds(10), [this, &executor, &loops, &kNumMessages]() {
|
||||
std::chrono::milliseconds(10), [this, &executor, &loops]() {
|
||||
loops++;
|
||||
if (kNumMessages == this->callback_count.load() ||
|
||||
loops == 500)
|
||||
{
|
||||
if (kNumMessages == this->callback_count.load() || loops == 500) {
|
||||
executor.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -129,6 +129,9 @@ TEST_F(TestNodeGraph, construct_from_node)
|
||||
|
||||
EXPECT_EQ(0u, node_graph()->count_publishers("not_a_topic"));
|
||||
EXPECT_EQ(0u, node_graph()->count_subscribers("not_a_topic"));
|
||||
EXPECT_EQ(0u, node_graph()->count_clients("not_a_service"));
|
||||
EXPECT_EQ(0u, node_graph()->count_services("not_a_service"));
|
||||
|
||||
EXPECT_NE(nullptr, node_graph()->get_graph_guard_condition());
|
||||
|
||||
// get_graph_event is non-const
|
||||
@@ -534,6 +537,22 @@ TEST_F(TestNodeGraph, count_subscribers_rcl_error)
|
||||
std::runtime_error("could not count subscribers: error not set"));
|
||||
}
|
||||
|
||||
TEST_F(TestNodeGraph, count_clients_rcl_error)
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_count_clients, RCL_RET_ERROR);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
node_graph()->count_clients("service"),
|
||||
std::runtime_error("could not count clients: error not set"));
|
||||
}
|
||||
|
||||
TEST_F(TestNodeGraph, count_services_rcl_error)
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_count_services, RCL_RET_ERROR);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
node_graph()->count_services("service"),
|
||||
std::runtime_error("could not count services: error not set"));
|
||||
}
|
||||
|
||||
TEST_F(TestNodeGraph, notify_shutdown)
|
||||
{
|
||||
EXPECT_NO_THROW(node()->get_node_graph_interface()->notify_shutdown());
|
||||
|
||||
@@ -95,15 +95,15 @@ TEST_F(TestNodeParameters, list_parameters)
|
||||
std::find(list_result2.names.begin(), list_result2.names.end(), parameter_name),
|
||||
list_result2.names.end());
|
||||
|
||||
// Check prefixes
|
||||
// Check prefixes and the depth relative to the given prefixes
|
||||
const std::string parameter_name2 = "prefix.new_parameter";
|
||||
const rclcpp::ParameterValue value2(true);
|
||||
const rcl_interfaces::msg::ParameterDescriptor descriptor2;
|
||||
const auto added_parameter_value2 =
|
||||
node_parameters->declare_parameter(parameter_name2, value2, descriptor2, false);
|
||||
EXPECT_EQ(value.get<bool>(), added_parameter_value.get<bool>());
|
||||
EXPECT_EQ(value2.get<bool>(), added_parameter_value2.get<bool>());
|
||||
prefixes = {"prefix"};
|
||||
auto list_result3 = node_parameters->list_parameters(prefixes, 2u);
|
||||
auto list_result3 = node_parameters->list_parameters(prefixes, 1u);
|
||||
EXPECT_EQ(1u, list_result3.names.size());
|
||||
EXPECT_NE(
|
||||
std::find(list_result3.names.begin(), list_result3.names.end(), parameter_name2),
|
||||
@@ -116,6 +116,13 @@ TEST_F(TestNodeParameters, list_parameters)
|
||||
EXPECT_NE(
|
||||
std::find(list_result4.names.begin(), list_result4.names.end(), parameter_name),
|
||||
list_result4.names.end());
|
||||
|
||||
// Return all parameters when the depth = 0
|
||||
auto list_result5 = node_parameters->list_parameters(prefixes, 0u);
|
||||
EXPECT_EQ(1u, list_result5.names.size());
|
||||
EXPECT_NE(
|
||||
std::find(list_result5.names.begin(), list_result5.names.end(), parameter_name),
|
||||
list_result5.names.end());
|
||||
}
|
||||
|
||||
TEST_F(TestNodeParameters, parameter_overrides)
|
||||
|
||||
@@ -92,7 +92,7 @@ TEST_F(TestNodeService, add_service)
|
||||
different_node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
node_services->add_service(service, callback_group_in_different_node),
|
||||
std::runtime_error("Cannot create service, group not in node."));
|
||||
rclcpp::exceptions::MissingGroupNodeException("service"));
|
||||
}
|
||||
|
||||
TEST_F(TestNodeService, add_service_rcl_trigger_guard_condition_error)
|
||||
@@ -119,7 +119,7 @@ TEST_F(TestNodeService, add_client)
|
||||
different_node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
node_services->add_client(client, callback_group_in_different_node),
|
||||
std::runtime_error("Cannot create client, group not in node."));
|
||||
rclcpp::exceptions::MissingGroupNodeException("client"));
|
||||
}
|
||||
|
||||
TEST_F(TestNodeService, add_client_rcl_trigger_guard_condition_error)
|
||||
|
||||
@@ -75,7 +75,7 @@ TEST_F(TestNodeTimers, add_timer)
|
||||
different_node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
node_timers->add_timer(timer, callback_group_in_different_node),
|
||||
std::runtime_error("Cannot create timer, group not in node."));
|
||||
rclcpp::exceptions::MissingGroupNodeException("timer"));
|
||||
}
|
||||
|
||||
TEST_F(TestNodeTimers, add_timer_rcl_trigger_guard_condition_error)
|
||||
|
||||
@@ -78,7 +78,7 @@ TEST_F(TestNodeWaitables, add_remove_waitable)
|
||||
node_waitables->add_waitable(waitable, callback_group1));
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
node_waitables->add_waitable(waitable, callback_group2),
|
||||
std::runtime_error("Cannot create waitable, group not in node."));
|
||||
rclcpp::exceptions::MissingGroupNodeException("waitable"));
|
||||
EXPECT_NO_THROW(node_waitables->remove_waitable(waitable, callback_group1));
|
||||
EXPECT_NO_THROW(node_waitables->remove_waitable(waitable, callback_group2));
|
||||
|
||||
|
||||
88
rclcpp/test/rclcpp/test_copy_all_parameter_values.cpp
Normal file
88
rclcpp/test/rclcpp/test_copy_all_parameter_values.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2023 Open Navigation LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "rclcpp/copy_all_parameter_values.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
class TestNode : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestNode, TestParamCopying)
|
||||
{
|
||||
auto node1 = std::make_shared<rclcpp::Node>("test_node1");
|
||||
auto node2 = std::make_shared<rclcpp::Node>("test_node2");
|
||||
|
||||
// Tests for (1) multiple types, (2) recursion, (3) overriding values
|
||||
node1->declare_parameter("Foo1", rclcpp::ParameterValue(std::string(("bar1"))));
|
||||
node1->declare_parameter("Foo2", rclcpp::ParameterValue(0.123));
|
||||
node1->declare_parameter("Foo", rclcpp::ParameterValue(std::string(("bar"))));
|
||||
node1->declare_parameter("Foo.bar", rclcpp::ParameterValue(std::string(("steve"))));
|
||||
node2->declare_parameter("Foo", rclcpp::ParameterValue(std::string(("barz2"))));
|
||||
|
||||
// Show Node2 is empty of Node1's parameters, but contains its own
|
||||
EXPECT_FALSE(node2->has_parameter("Foo1"));
|
||||
EXPECT_FALSE(node2->has_parameter("Foo2"));
|
||||
EXPECT_FALSE(node2->has_parameter("Foo.bar"));
|
||||
EXPECT_TRUE(node2->has_parameter("Foo"));
|
||||
EXPECT_EQ(node2->get_parameter("Foo").as_string(), std::string("barz2"));
|
||||
|
||||
bool override = false;
|
||||
rclcpp::copy_all_parameter_values(node1, node2, override);
|
||||
|
||||
// Test new parameters exist, of expected value, and original param is not overridden
|
||||
EXPECT_TRUE(node2->has_parameter("Foo1"));
|
||||
EXPECT_EQ(node2->get_parameter("Foo1").as_string(), std::string("bar1"));
|
||||
EXPECT_TRUE(node2->has_parameter("Foo2"));
|
||||
EXPECT_EQ(node2->get_parameter("Foo2").as_double(), 0.123);
|
||||
EXPECT_TRUE(node2->has_parameter("Foo.bar"));
|
||||
EXPECT_EQ(node2->get_parameter("Foo.bar").as_string(), std::string("steve"));
|
||||
EXPECT_TRUE(node2->has_parameter("Foo"));
|
||||
EXPECT_EQ(node2->get_parameter("Foo").as_string(), std::string("barz2"));
|
||||
|
||||
// Test if parameter overrides are permissible that Node2's value is overridden
|
||||
override = true;
|
||||
rclcpp::copy_all_parameter_values(node1, node2, override);
|
||||
EXPECT_EQ(node2->get_parameter("Foo").as_string(), std::string("bar"));
|
||||
}
|
||||
|
||||
TEST_F(TestNode, TestParamCopyingExceptions)
|
||||
{
|
||||
auto node1 = std::make_shared<rclcpp::Node>("test_node1");
|
||||
auto node2 = std::make_shared<rclcpp::Node>("test_node2");
|
||||
|
||||
// Tests for Parameter value conflicts handled
|
||||
node1->declare_parameter("Foo", rclcpp::ParameterValue(std::string(("bar"))));
|
||||
node2->declare_parameter("Foo", rclcpp::ParameterValue(0.123));
|
||||
|
||||
bool override = true;
|
||||
EXPECT_NO_THROW(
|
||||
rclcpp::copy_all_parameter_values(node1, node2, override));
|
||||
|
||||
// Tests for Parameter read-only handled
|
||||
node1->declare_parameter("Foo1", rclcpp::ParameterValue(std::string(("bar"))));
|
||||
node2->declare_parameter("Foo1", rclcpp::ParameterValue(0.123));
|
||||
EXPECT_NO_THROW(rclcpp::copy_all_parameter_values(node1, node2, override));
|
||||
}
|
||||
@@ -546,6 +546,40 @@ TEST_F(TestExecutor, spin_node_once_node) {
|
||||
EXPECT_TRUE(spin_called);
|
||||
}
|
||||
|
||||
TEST_F(TestExecutor, spin_node_all_base_interface) {
|
||||
DummyExecutor dummy;
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
bool spin_called = false;
|
||||
auto timer =
|
||||
node->create_wall_timer(
|
||||
std::chrono::milliseconds(1), [&]() {
|
||||
spin_called = true;
|
||||
});
|
||||
|
||||
// Wait for the wall timer to have expired.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
EXPECT_FALSE(spin_called);
|
||||
dummy.spin_node_all(node->get_node_base_interface(), std::chrono::milliseconds(50));
|
||||
EXPECT_TRUE(spin_called);
|
||||
}
|
||||
|
||||
TEST_F(TestExecutor, spin_node_all_node) {
|
||||
DummyExecutor dummy;
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
bool spin_called = false;
|
||||
auto timer =
|
||||
node->create_wall_timer(
|
||||
std::chrono::milliseconds(1), [&]() {
|
||||
spin_called = true;
|
||||
});
|
||||
|
||||
// Wait for the wall timer to have expired.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
EXPECT_FALSE(spin_called);
|
||||
dummy.spin_node_all(node, std::chrono::milliseconds(50));
|
||||
EXPECT_TRUE(spin_called);
|
||||
}
|
||||
|
||||
TEST_F(TestExecutor, spin_until_future_complete_future_already_complete) {
|
||||
DummyExecutor dummy;
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
|
||||
@@ -3311,6 +3311,9 @@ TEST_F(TestNode, get_entity_names) {
|
||||
const auto service_names_and_types = node->get_service_names_and_types();
|
||||
EXPECT_EQ(service_names_and_types.end(), service_names_and_types.find("service"));
|
||||
|
||||
EXPECT_EQ(0u, node->count_clients("service"));
|
||||
EXPECT_EQ(0u, node->count_services("service"));
|
||||
|
||||
const auto service_names_and_types_by_node =
|
||||
node->get_service_names_and_types_by_node("node", "/ns");
|
||||
EXPECT_EQ(
|
||||
|
||||
@@ -152,33 +152,54 @@ TEST_F(TestPublisher, conversion_exception_is_passed_up) {
|
||||
}
|
||||
}
|
||||
|
||||
using UseTakeSharedMethod = bool;
|
||||
class TestPublisherFixture
|
||||
: public TestPublisher,
|
||||
public ::testing::WithParamInterface<UseTakeSharedMethod>
|
||||
{
|
||||
};
|
||||
|
||||
/*
|
||||
* Testing that publisher sends type adapted types and ROS message types with intra proccess communications.
|
||||
*/
|
||||
TEST_F(
|
||||
TestPublisher,
|
||||
TEST_P(
|
||||
TestPublisherFixture,
|
||||
check_type_adapted_message_is_sent_and_received_intra_process) {
|
||||
using StringTypeAdapter = rclcpp::TypeAdapter<std::string, rclcpp::msg::String>;
|
||||
const std::string message_data = "Message Data";
|
||||
const std::string topic_name = "topic_name";
|
||||
bool is_received;
|
||||
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
const rclcpp::msg::String::ConstSharedPtr msg,
|
||||
const rclcpp::MessageInfo & message_info
|
||||
) -> void
|
||||
{
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
|
||||
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
|
||||
auto node = rclcpp::Node::make_shared(
|
||||
"test_intra_process",
|
||||
rclcpp::NodeOptions().use_intra_process_comms(true));
|
||||
auto pub = node->create_publisher<StringTypeAdapter>(topic_name, 10);
|
||||
auto sub = node->create_subscription<rclcpp::msg::String>(topic_name, 1, callback);
|
||||
rclcpp::Subscription<rclcpp::msg::String>::SharedPtr sub;
|
||||
if (GetParam()) {
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
const rclcpp::msg::String::ConstSharedPtr msg,
|
||||
const rclcpp::MessageInfo & message_info
|
||||
) -> void
|
||||
{
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
|
||||
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
sub = node->create_subscription<rclcpp::msg::String>(topic_name, 1, callback);
|
||||
} else {
|
||||
auto callback_unique =
|
||||
[message_data, &is_received](
|
||||
rclcpp::msg::String::UniquePtr msg,
|
||||
const rclcpp::MessageInfo & message_info
|
||||
) -> void
|
||||
{
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
|
||||
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
sub = node->create_subscription<rclcpp::msg::String>(topic_name, 1, callback_unique);
|
||||
}
|
||||
|
||||
auto wait_for_message_to_be_received = [&is_received, &node]() {
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
@@ -239,6 +260,14 @@ TEST_F(
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
TestPublisherFixtureWithParam,
|
||||
TestPublisherFixture,
|
||||
::testing::Values(
|
||||
true, // use take shared method
|
||||
false // not use take shared method
|
||||
));
|
||||
|
||||
/*
|
||||
* Testing that publisher sends type adapted types and ROS message types with inter proccess communications.
|
||||
*/
|
||||
|
||||
@@ -14,14 +14,19 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/rate.hpp"
|
||||
|
||||
#include "../utils/rclcpp_gtest_macros.hpp"
|
||||
|
||||
/*
|
||||
Basic tests for the Rate and WallRate classes.
|
||||
Basic tests for the Rate, WallRate and ROSRate classes.
|
||||
*/
|
||||
TEST(TestRate, rate_basics) {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
auto period = std::chrono::milliseconds(1000);
|
||||
auto offset = std::chrono::milliseconds(500);
|
||||
auto epsilon = std::chrono::milliseconds(100);
|
||||
@@ -29,8 +34,23 @@ TEST(TestRate, rate_basics) {
|
||||
|
||||
auto start = std::chrono::system_clock::now();
|
||||
rclcpp::Rate r(period);
|
||||
EXPECT_EQ(period, r.period());
|
||||
EXPECT_EQ(rclcpp::Duration(period), r.period());
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
ASSERT_FALSE(r.is_steady());
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
ASSERT_EQ(RCL_SYSTEM_TIME, r.get_type());
|
||||
ASSERT_TRUE(r.sleep());
|
||||
auto one = std::chrono::system_clock::now();
|
||||
auto delta = one - start;
|
||||
@@ -59,9 +79,13 @@ TEST(TestRate, rate_basics) {
|
||||
auto five = std::chrono::system_clock::now();
|
||||
delta = five - four;
|
||||
ASSERT_TRUE(epsilon > delta);
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
TEST(TestRate, wall_rate_basics) {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
auto period = std::chrono::milliseconds(100);
|
||||
auto offset = std::chrono::milliseconds(50);
|
||||
auto epsilon = std::chrono::milliseconds(1);
|
||||
@@ -69,8 +93,25 @@ TEST(TestRate, wall_rate_basics) {
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
rclcpp::WallRate r(period);
|
||||
EXPECT_EQ(period, r.period());
|
||||
EXPECT_EQ(rclcpp::Duration(period), r.period());
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
ASSERT_TRUE(r.is_steady());
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
ASSERT_EQ(RCL_STEADY_TIME, r.get_type());
|
||||
ASSERT_TRUE(r.sleep());
|
||||
auto one = std::chrono::steady_clock::now();
|
||||
auto delta = one - start;
|
||||
@@ -99,23 +140,288 @@ TEST(TestRate, wall_rate_basics) {
|
||||
auto five = std::chrono::steady_clock::now();
|
||||
delta = five - four;
|
||||
EXPECT_GT(epsilon, delta);
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
TEST(TestRate, ros_rate_basics) {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
auto period = std::chrono::milliseconds(100);
|
||||
auto offset = std::chrono::milliseconds(50);
|
||||
auto epsilon = std::chrono::milliseconds(1);
|
||||
double overrun_ratio = 1.5;
|
||||
|
||||
rclcpp::Clock clock(RCL_ROS_TIME);
|
||||
|
||||
auto start = clock.now();
|
||||
rclcpp::ROSRate r(period);
|
||||
EXPECT_EQ(rclcpp::Duration(period), r.period());
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
ASSERT_FALSE(r.is_steady());
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
ASSERT_EQ(RCL_ROS_TIME, r.get_type());
|
||||
ASSERT_TRUE(r.sleep());
|
||||
auto one = clock.now();
|
||||
auto delta = one - start;
|
||||
EXPECT_LT(rclcpp::Duration(period), delta);
|
||||
EXPECT_GT(rclcpp::Duration(period * overrun_ratio), delta);
|
||||
|
||||
clock.sleep_for(offset);
|
||||
ASSERT_TRUE(r.sleep());
|
||||
auto two = clock.now();
|
||||
delta = two - start;
|
||||
EXPECT_LT(rclcpp::Duration(2 * period), delta + epsilon);
|
||||
EXPECT_GT(rclcpp::Duration(2 * period * overrun_ratio), delta);
|
||||
|
||||
clock.sleep_for(offset);
|
||||
auto two_offset = clock.now();
|
||||
r.reset();
|
||||
ASSERT_TRUE(r.sleep());
|
||||
auto three = clock.now();
|
||||
delta = three - two_offset;
|
||||
EXPECT_LT(rclcpp::Duration(period), delta);
|
||||
EXPECT_GT(rclcpp::Duration(period * overrun_ratio), delta);
|
||||
|
||||
clock.sleep_for(offset + period);
|
||||
auto four = clock.now();
|
||||
ASSERT_FALSE(r.sleep());
|
||||
auto five = clock.now();
|
||||
delta = five - four;
|
||||
EXPECT_GT(rclcpp::Duration(epsilon), delta);
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
/*
|
||||
Basic test for the deprecated GenericRate class.
|
||||
*/
|
||||
TEST(TestRate, generic_rate) {
|
||||
auto period = std::chrono::milliseconds(100);
|
||||
auto offset = std::chrono::milliseconds(50);
|
||||
auto epsilon = std::chrono::milliseconds(1);
|
||||
double overrun_ratio = 1.5;
|
||||
|
||||
{
|
||||
auto start = std::chrono::system_clock::now();
|
||||
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
rclcpp::GenericRate<std::chrono::system_clock> gr(period);
|
||||
ASSERT_FALSE(gr.is_steady());
|
||||
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
ASSERT_EQ(gr.get_type(), RCL_SYSTEM_TIME);
|
||||
EXPECT_EQ(period, gr.period());
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto one = std::chrono::system_clock::now();
|
||||
auto delta = one - start;
|
||||
EXPECT_LT(period, delta + epsilon);
|
||||
EXPECT_GT(period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset);
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto two = std::chrono::system_clock::now();
|
||||
delta = two - start;
|
||||
EXPECT_LT(2 * period, delta);
|
||||
EXPECT_GT(2 * period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset);
|
||||
auto two_offset = std::chrono::system_clock::now();
|
||||
gr.reset();
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto three = std::chrono::system_clock::now();
|
||||
delta = three - two_offset;
|
||||
EXPECT_LT(period, delta + epsilon);
|
||||
EXPECT_GT(period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset + period);
|
||||
auto four = std::chrono::system_clock::now();
|
||||
ASSERT_FALSE(gr.sleep());
|
||||
auto five = std::chrono::system_clock::now();
|
||||
delta = five - four;
|
||||
ASSERT_TRUE(epsilon > delta);
|
||||
}
|
||||
|
||||
{
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
rclcpp::GenericRate<std::chrono::steady_clock> gr(period);
|
||||
ASSERT_TRUE(gr.is_steady());
|
||||
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
ASSERT_EQ(gr.get_type(), RCL_STEADY_TIME);
|
||||
EXPECT_EQ(period, gr.period());
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto one = std::chrono::steady_clock::now();
|
||||
auto delta = one - start;
|
||||
EXPECT_LT(period, delta);
|
||||
EXPECT_GT(period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset);
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto two = std::chrono::steady_clock::now();
|
||||
delta = two - start;
|
||||
EXPECT_LT(2 * period, delta + epsilon);
|
||||
EXPECT_GT(2 * period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset);
|
||||
auto two_offset = std::chrono::steady_clock::now();
|
||||
gr.reset();
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto three = std::chrono::steady_clock::now();
|
||||
delta = three - two_offset;
|
||||
EXPECT_LT(period, delta);
|
||||
EXPECT_GT(period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset + period);
|
||||
auto four = std::chrono::steady_clock::now();
|
||||
ASSERT_FALSE(gr.sleep());
|
||||
auto five = std::chrono::steady_clock::now();
|
||||
delta = five - four;
|
||||
EXPECT_GT(epsilon, delta);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TestRate, from_double) {
|
||||
{
|
||||
rclcpp::WallRate rate(1.0);
|
||||
EXPECT_EQ(std::chrono::seconds(1), rate.period());
|
||||
rclcpp::Rate rate(1.0);
|
||||
EXPECT_EQ(rclcpp::Duration(std::chrono::seconds(1)), rate.period());
|
||||
}
|
||||
{
|
||||
rclcpp::WallRate rate(2.0);
|
||||
EXPECT_EQ(std::chrono::milliseconds(500), rate.period());
|
||||
EXPECT_EQ(rclcpp::Duration(std::chrono::milliseconds(500)), rate.period());
|
||||
}
|
||||
{
|
||||
rclcpp::WallRate rate(0.5);
|
||||
EXPECT_EQ(std::chrono::seconds(2), rate.period());
|
||||
EXPECT_EQ(rclcpp::Duration(std::chrono::seconds(2)), rate.period());
|
||||
}
|
||||
{
|
||||
rclcpp::WallRate rate(4.0);
|
||||
EXPECT_EQ(std::chrono::milliseconds(250), rate.period());
|
||||
rclcpp::ROSRate rate(4.0);
|
||||
EXPECT_EQ(rclcpp::Duration(std::chrono::milliseconds(250)), rate.period());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TestRate, clock_types) {
|
||||
{
|
||||
rclcpp::Rate rate(1.0, std::make_shared<rclcpp::Clock>(RCL_SYSTEM_TIME));
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
EXPECT_FALSE(rate.is_steady());
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
EXPECT_EQ(RCL_SYSTEM_TIME, rate.get_type());
|
||||
}
|
||||
{
|
||||
rclcpp::Rate rate(1.0, std::make_shared<rclcpp::Clock>(RCL_STEADY_TIME));
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
EXPECT_TRUE(rate.is_steady());
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
EXPECT_EQ(RCL_STEADY_TIME, rate.get_type());
|
||||
}
|
||||
{
|
||||
rclcpp::Rate rate(1.0, std::make_shared<rclcpp::Clock>(RCL_ROS_TIME));
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
EXPECT_FALSE(rate.is_steady());
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
EXPECT_EQ(RCL_ROS_TIME, rate.get_type());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TestRate, incorrect_constuctor) {
|
||||
// Constructor with 0-frequency
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
rclcpp::Rate rate(0.0),
|
||||
std::invalid_argument("rate must be greater than 0"));
|
||||
|
||||
// Constructor with negative frequency
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
rclcpp::Rate rate(-1.0),
|
||||
std::invalid_argument("rate must be greater than 0"));
|
||||
|
||||
// Constructor with 0-duration
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
rclcpp::Rate rate(rclcpp::Duration(0, 0)),
|
||||
std::invalid_argument("period must be greater than 0"));
|
||||
|
||||
// Constructor with negative duration
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
rclcpp::Rate rate(rclcpp::Duration(-1, 0)),
|
||||
std::invalid_argument("period must be greater than 0"));
|
||||
}
|
||||
|
||||
@@ -145,11 +145,6 @@ TEST(TestSerializedMessage, reserve) {
|
||||
// Resize using reserve method
|
||||
serialized_msg.reserve(15);
|
||||
EXPECT_EQ(15u, serialized_msg.capacity());
|
||||
|
||||
// Use invalid value throws exception
|
||||
EXPECT_THROW(
|
||||
{serialized_msg.reserve(-1);},
|
||||
rclcpp::exceptions::RCLBadAlloc);
|
||||
}
|
||||
|
||||
TEST(TestSerializedMessage, serialization) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
@@ -65,8 +66,10 @@ TEST_F(TestTimersManager, empty_manager)
|
||||
TEST_F(TestTimersManager, add_run_remove_timer)
|
||||
{
|
||||
size_t t_runs = 0;
|
||||
std::chrono::milliseconds timer_period(10);
|
||||
|
||||
auto t = TimerT::make_shared(
|
||||
10ms,
|
||||
timer_period,
|
||||
[&t_runs]() {
|
||||
t_runs++;
|
||||
},
|
||||
@@ -79,7 +82,7 @@ TEST_F(TestTimersManager, add_run_remove_timer)
|
||||
timers_manager->add_timer(t);
|
||||
|
||||
// Sleep for more 3 times the timer period
|
||||
std::this_thread::sleep_for(30ms);
|
||||
std::this_thread::sleep_for(3 * timer_period);
|
||||
|
||||
// The timer is executed only once, even if we slept 3 times the period
|
||||
execute_all_ready_timers(timers_manager);
|
||||
@@ -191,67 +194,6 @@ TEST_F(TestTimersManager, head_not_ready)
|
||||
EXPECT_EQ(0u, t_runs);
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, timers_order)
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t1_runs = 0;
|
||||
auto t1 = TimerT::make_shared(
|
||||
10ms,
|
||||
[&t1_runs]() {
|
||||
t1_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t2_runs = 0;
|
||||
auto t2 = TimerT::make_shared(
|
||||
30ms,
|
||||
[&t2_runs]() {
|
||||
t2_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t3_runs = 0;
|
||||
auto t3 = TimerT::make_shared(
|
||||
100ms,
|
||||
[&t3_runs]() {
|
||||
t3_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
// Add timers in a random order
|
||||
timers_manager->add_timer(t2);
|
||||
timers_manager->add_timer(t3);
|
||||
timers_manager->add_timer(t1);
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
execute_all_ready_timers(timers_manager);
|
||||
EXPECT_EQ(1u, t1_runs);
|
||||
EXPECT_EQ(0u, t2_runs);
|
||||
EXPECT_EQ(0u, t3_runs);
|
||||
|
||||
std::this_thread::sleep_for(30ms);
|
||||
execute_all_ready_timers(timers_manager);
|
||||
EXPECT_EQ(2u, t1_runs);
|
||||
EXPECT_EQ(1u, t2_runs);
|
||||
EXPECT_EQ(0u, t3_runs);
|
||||
|
||||
std::this_thread::sleep_for(100ms);
|
||||
execute_all_ready_timers(timers_manager);
|
||||
EXPECT_EQ(3u, t1_runs);
|
||||
EXPECT_EQ(2u, t2_runs);
|
||||
EXPECT_EQ(1u, t3_runs);
|
||||
|
||||
timers_manager->remove_timer(t1);
|
||||
|
||||
std::this_thread::sleep_for(30ms);
|
||||
execute_all_ready_timers(timers_manager);
|
||||
EXPECT_EQ(3u, t1_runs);
|
||||
EXPECT_EQ(3u, t2_runs);
|
||||
EXPECT_EQ(1u, t3_runs);
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, start_stop_timers_thread)
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
@@ -274,7 +216,7 @@ TEST_F(TestTimersManager, timers_thread)
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t1_runs = 0;
|
||||
int t1_runs = 0;
|
||||
auto t1 = TimerT::make_shared(
|
||||
1ms,
|
||||
[&t1_runs]() {
|
||||
@@ -282,7 +224,7 @@ TEST_F(TestTimersManager, timers_thread)
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t2_runs = 0;
|
||||
int t2_runs = 0;
|
||||
auto t2 = TimerT::make_shared(
|
||||
1ms,
|
||||
[&t2_runs]() {
|
||||
@@ -296,12 +238,12 @@ TEST_F(TestTimersManager, timers_thread)
|
||||
|
||||
// Run timers thread for a while
|
||||
timers_manager->start();
|
||||
std::this_thread::sleep_for(5ms);
|
||||
std::this_thread::sleep_for(50ms);
|
||||
timers_manager->stop();
|
||||
|
||||
EXPECT_LT(1u, t1_runs);
|
||||
EXPECT_LT(1u, t2_runs);
|
||||
EXPECT_EQ(t1_runs, t2_runs);
|
||||
EXPECT_LE(std::abs(t1_runs - t2_runs), 1);
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, destructor)
|
||||
@@ -365,13 +307,13 @@ TEST_F(TestTimersManager, add_remove_while_thread_running)
|
||||
timers_manager->start();
|
||||
|
||||
// After a while remove t1 and add t2
|
||||
std::this_thread::sleep_for(5ms);
|
||||
std::this_thread::sleep_for(50ms);
|
||||
timers_manager->remove_timer(t1);
|
||||
size_t tmp_t1 = t1_runs;
|
||||
timers_manager->add_timer(t2);
|
||||
|
||||
// Wait some more time and then stop
|
||||
std::this_thread::sleep_for(5ms);
|
||||
std::this_thread::sleep_for(50ms);
|
||||
timers_manager->stop();
|
||||
|
||||
// t1 has stopped running
|
||||
|
||||
@@ -3,6 +3,28 @@ Changelog for package rclcpp_action
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
23.2.0 (2023-10-09)
|
||||
-------------------
|
||||
|
||||
23.1.0 (2023-10-04)
|
||||
-------------------
|
||||
|
||||
23.0.0 (2023-09-08)
|
||||
-------------------
|
||||
* Update API docs links in package READMEs (`#2302 <https://github.com/ros2/rclcpp/issues/2302>`_)
|
||||
* fix(ClientGoalHandle): Made mutex recursive to prevent deadlocks (`#2267 <https://github.com/ros2/rclcpp/issues/2267>`_)
|
||||
* Contributors: Christophe Bedard, jmachowinski
|
||||
|
||||
22.2.0 (2023-09-07)
|
||||
-------------------
|
||||
* Correct the position of a comment. (`#2290 <https://github.com/ros2/rclcpp/issues/2290>`_)
|
||||
* Fix a typo in a comment. (`#2283 <https://github.com/ros2/rclcpp/issues/2283>`_)
|
||||
* doc fix: call `canceled` only after goal state is in canceling. (`#2266 <https://github.com/ros2/rclcpp/issues/2266>`_)
|
||||
* Contributors: Chris Lalancette, Jiaqi Li, Tomoya Fujita
|
||||
|
||||
22.1.0 (2023-08-21)
|
||||
-------------------
|
||||
|
||||
22.0.0 (2023-07-11)
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
Adds action APIs for C++.
|
||||
|
||||
Visit the [rclcpp_action API documentation](http://docs.ros2.org/latest/api/rclcpp_action/) for a complete list of its main components and features. For more information about Actions in ROS 2, see the [design document](http://design.ros2.org/articles/actions.html).
|
||||
The link to the latest rclcpp_action API documentation, which includes a complete list of its main components and features, can be found on the rclcpp_action package info page, at the [ROS Index](https://index.ros.org/p/rclcpp_action/).
|
||||
For more information about Actions in ROS 2, see the [design document](http://design.ros2.org/articles/actions.html).
|
||||
|
||||
## Quality Declaration
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ private:
|
||||
ResultCallback result_callback_{nullptr};
|
||||
int8_t status_{GoalStatus::STATUS_ACCEPTED};
|
||||
|
||||
std::mutex handle_mutex_;
|
||||
std::recursive_mutex handle_mutex_;
|
||||
};
|
||||
} // namespace rclcpp_action
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ template<typename ActionT>
|
||||
std::shared_future<typename ClientGoalHandle<ActionT>::WrappedResult>
|
||||
ClientGoalHandle<ActionT>::async_get_result()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
if (!is_result_aware_) {
|
||||
throw exceptions::UnawareGoalHandleError();
|
||||
}
|
||||
@@ -70,7 +70,7 @@ template<typename ActionT>
|
||||
void
|
||||
ClientGoalHandle<ActionT>::set_result(const WrappedResult & wrapped_result)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
status_ = static_cast<int8_t>(wrapped_result.code);
|
||||
result_promise_.set_value(wrapped_result);
|
||||
if (result_callback_) {
|
||||
@@ -82,7 +82,7 @@ template<typename ActionT>
|
||||
void
|
||||
ClientGoalHandle<ActionT>::set_feedback_callback(FeedbackCallback callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
feedback_callback_ = callback;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ template<typename ActionT>
|
||||
void
|
||||
ClientGoalHandle<ActionT>::set_result_callback(ResultCallback callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
result_callback_ = callback;
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ template<typename ActionT>
|
||||
int8_t
|
||||
ClientGoalHandle<ActionT>::get_status()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
return status_;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ template<typename ActionT>
|
||||
void
|
||||
ClientGoalHandle<ActionT>::set_status(int8_t status)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
status_ = status;
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ template<typename ActionT>
|
||||
bool
|
||||
ClientGoalHandle<ActionT>::is_feedback_aware()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
return feedback_callback_ != nullptr;
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ template<typename ActionT>
|
||||
bool
|
||||
ClientGoalHandle<ActionT>::is_result_aware()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
return is_result_aware_;
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ template<typename ActionT>
|
||||
bool
|
||||
ClientGoalHandle<ActionT>::set_result_awareness(bool awareness)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
bool previous = is_result_aware_;
|
||||
is_result_aware_ = awareness;
|
||||
return previous;
|
||||
@@ -140,7 +140,7 @@ template<typename ActionT>
|
||||
void
|
||||
ClientGoalHandle<ActionT>::invalidate(const exceptions::UnawareGoalHandleError & ex)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
// Guard against multiple calls
|
||||
if (is_invalidated()) {
|
||||
return;
|
||||
@@ -168,7 +168,7 @@ ClientGoalHandle<ActionT>::call_feedback_callback(
|
||||
RCLCPP_ERROR(rclcpp::get_logger("rclcpp_action"), "Sent feedback to wrong goal handle.");
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
if (nullptr == feedback_callback_) {
|
||||
// Normal, some feedback messages may arrive after the goal result.
|
||||
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp_action"), "Received feedback but goal ignores it.");
|
||||
|
||||
@@ -128,7 +128,7 @@ class Server;
|
||||
* accepted.
|
||||
* A `Server` will create an instance and give it to the user in their `handle_accepted` callback.
|
||||
*
|
||||
* Internally, this class is responsible for coverting between the C++ action type and generic
|
||||
* Internally, this class is responsible for converting between the C++ action type and generic
|
||||
* types for `rclcpp_action::ServerGoalHandleBase`.
|
||||
*/
|
||||
template<typename ActionT>
|
||||
@@ -196,7 +196,7 @@ public:
|
||||
|
||||
/// Indicate that a goal has been canceled.
|
||||
/**
|
||||
* Only call this if the goal is executing or pending, but has been canceled.
|
||||
* Only call this if the goal is canceling.
|
||||
* This is a terminal state, no more methods should be called on a goal handle after this is
|
||||
* called.
|
||||
*
|
||||
|
||||
@@ -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>22.0.0</version>
|
||||
<version>23.2.0</version>
|
||||
<description>Adds action APIs for C++.</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
|
||||
@@ -163,7 +163,6 @@ bool
|
||||
ClientBase::wait_for_action_server_nanoseconds(std::chrono::nanoseconds timeout)
|
||||
{
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
// make an event to reuse, rather than create a new one each time
|
||||
auto node_ptr = pimpl_->node_graph_.lock();
|
||||
if (!node_ptr) {
|
||||
throw rclcpp::exceptions::InvalidNodeError();
|
||||
@@ -172,6 +171,7 @@ ClientBase::wait_for_action_server_nanoseconds(std::chrono::nanoseconds timeout)
|
||||
if (this->action_server_is_ready()) {
|
||||
return true;
|
||||
}
|
||||
// make an event to reuse, rather than create a new one each time
|
||||
auto event = node_ptr->get_graph_event();
|
||||
if (timeout == std::chrono::nanoseconds(0)) {
|
||||
// check was non-blocking, return immediately
|
||||
|
||||
@@ -852,6 +852,86 @@ TEST_F(TestClientAgainstServer, async_cancel_some_goals_with_callback)
|
||||
EXPECT_EQ(rclcpp_action::GoalStatus::STATUS_CANCELED, goal_handle0->get_status());
|
||||
}
|
||||
|
||||
TEST_F(TestClientAgainstServer, deadlock_in_callbacks)
|
||||
{
|
||||
std::atomic<bool> feedback_callback_called = false;
|
||||
std::atomic<bool> response_callback_called = false;
|
||||
std::atomic<bool> result_callback_called = false;
|
||||
std::atomic<bool> no_deadlock = false;
|
||||
|
||||
std::thread tr = std::thread(
|
||||
[&]() {
|
||||
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
|
||||
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
|
||||
|
||||
ActionGoal goal;
|
||||
|
||||
using GoalHandle = rclcpp_action::ClientGoalHandle<ActionType>;
|
||||
rclcpp_action::Client<ActionType>::SendGoalOptions ops;
|
||||
ops.feedback_callback =
|
||||
[&feedback_callback_called](const GoalHandle::SharedPtr handle,
|
||||
ActionType::Feedback::ConstSharedPtr) {
|
||||
// call functions on the handle that acquire the lock
|
||||
handle->get_status();
|
||||
handle->is_feedback_aware();
|
||||
handle->is_result_aware();
|
||||
|
||||
feedback_callback_called = true;
|
||||
};
|
||||
ops.goal_response_callback = [&response_callback_called](
|
||||
const GoalHandle::SharedPtr & handle) {
|
||||
// call functions on the handle that acquire the lock
|
||||
handle->get_status();
|
||||
handle->is_feedback_aware();
|
||||
handle->is_result_aware();
|
||||
|
||||
response_callback_called = true;
|
||||
};
|
||||
ops.result_callback = [&result_callback_called](
|
||||
const GoalHandle::WrappedResult &) {
|
||||
result_callback_called = true;
|
||||
};
|
||||
|
||||
goal.order = 6;
|
||||
auto future_goal_handle = action_client->async_send_goal(goal, ops);
|
||||
dual_spin_until_future_complete(future_goal_handle);
|
||||
auto goal_handle = future_goal_handle.get();
|
||||
|
||||
ASSERT_TRUE(goal_handle);
|
||||
|
||||
ASSERT_EQ(RCL_RET_OK, rcl_set_ros_time_override(clock.get_clock_handle(), RCL_S_TO_NS(2)));
|
||||
|
||||
auto result_future = action_client->async_get_result(goal_handle);
|
||||
dual_spin_until_future_complete(result_future);
|
||||
|
||||
EXPECT_TRUE(result_future.valid());
|
||||
auto result = result_future.get();
|
||||
|
||||
no_deadlock = true;
|
||||
});
|
||||
|
||||
auto start_time = std::chrono::system_clock::now();
|
||||
|
||||
while (std::chrono::system_clock::now() - start_time < std::chrono::milliseconds(2000) &&
|
||||
!no_deadlock)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
if (no_deadlock) {
|
||||
tr.join();
|
||||
} else {
|
||||
// In case of a failure, the thread is assumed to be in a deadlock.
|
||||
// We detach the thread so we don't block further tests.
|
||||
tr.detach();
|
||||
}
|
||||
|
||||
EXPECT_TRUE(no_deadlock);
|
||||
EXPECT_TRUE(response_callback_called);
|
||||
EXPECT_TRUE(result_callback_called);
|
||||
EXPECT_TRUE(feedback_callback_called);
|
||||
}
|
||||
|
||||
TEST_F(TestClientAgainstServer, send_rcl_errors)
|
||||
{
|
||||
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
|
||||
|
||||
@@ -2,6 +2,25 @@
|
||||
Changelog for package rclcpp_components
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
23.2.0 (2023-10-09)
|
||||
-------------------
|
||||
|
||||
23.1.0 (2023-10-04)
|
||||
-------------------
|
||||
* Add missing header required by the rclcpp::NodeOptions type (`#2324 <https://github.com/ros2/rclcpp/issues/2324>`_)
|
||||
* Contributors: Ignacio Vizzo
|
||||
|
||||
23.0.0 (2023-09-08)
|
||||
-------------------
|
||||
* Update API docs links in package READMEs (`#2302 <https://github.com/ros2/rclcpp/issues/2302>`_)
|
||||
* Contributors: Christophe Bedard
|
||||
|
||||
22.2.0 (2023-09-07)
|
||||
-------------------
|
||||
|
||||
22.1.0 (2023-08-21)
|
||||
-------------------
|
||||
|
||||
22.0.0 (2023-07-11)
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Package containing tools for dynamically loadable components.
|
||||
|
||||
Visit the [rclcpp_components API documentation](http://docs.ros2.org/latest/api/rclcpp_components/) for a complete list of its main components and features.
|
||||
The link to the latest rclcpp_components API documentation, which includes a complete list of its main components and features, can be found on the rclcpp_components package info page, at the [ROS Index](https://index.ros.org/p/rclcpp_components/).
|
||||
|
||||
## Quality Declaration
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef RCLCPP_COMPONENTS__NODE_FACTORY_HPP__
|
||||
#define RCLCPP_COMPONENTS__NODE_FACTORY_HPP__
|
||||
|
||||
#include "rclcpp/node_options.hpp"
|
||||
#include "rclcpp_components/node_instance_wrapper.hpp"
|
||||
|
||||
namespace rclcpp_components
|
||||
|
||||
@@ -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>22.0.0</version>
|
||||
<version>23.2.0</version>
|
||||
<description>Package containing tools for dynamically loadable components</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
|
||||
@@ -3,6 +3,29 @@ Changelog for package rclcpp_lifecycle
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
23.2.0 (2023-10-09)
|
||||
-------------------
|
||||
* add clients & services count (`#2072 <https://github.com/ros2/rclcpp/issues/2072>`_)
|
||||
* Contributors: Minju, Lee
|
||||
|
||||
23.1.0 (2023-10-04)
|
||||
-------------------
|
||||
|
||||
23.0.0 (2023-09-08)
|
||||
-------------------
|
||||
* Update API docs links in package READMEs (`#2302 <https://github.com/ros2/rclcpp/issues/2302>`_)
|
||||
* Contributors: Christophe Bedard
|
||||
|
||||
22.2.0 (2023-09-07)
|
||||
-------------------
|
||||
* add logger level service to lifecycle node. (`#2277 <https://github.com/ros2/rclcpp/issues/2277>`_)
|
||||
* Contributors: Tomoya Fujita
|
||||
|
||||
22.1.0 (2023-08-21)
|
||||
-------------------
|
||||
* Stop using constref signature of benchmark DoNotOptimize. (`#2238 <https://github.com/ros2/rclcpp/issues/2238>`_)
|
||||
* Contributors: Chris Lalancette
|
||||
|
||||
22.0.0 (2023-07-11)
|
||||
-------------------
|
||||
* Implement get_node_type_descriptions_interface for lifecyclenode and add smoke test for it (`#2237 <https://github.com/ros2/rclcpp/issues/2237>`_)
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
Package containing a prototype for lifecycle implementation.
|
||||
|
||||
Visit the [rclcpp_lifecycle API documentation](http://docs.ros2.org/latest/api/rclcpp_lifecycle/) for a complete list of its main components and features. For more information about LifeCycle in ROS 2, see the [design document](http://design.ros2.org/articles/node_lifecycle.html).
|
||||
The link to the latest rclcpp_lifecycle API documentation, which includes a complete list of its main components and features, can be found on the rclcpp_lifecycle package info page, at the [ROS Index](https://index.ros.org/p/rclcpp_lifecycle/).
|
||||
For more information about LifeCycle in ROS 2, see the [design document](http://design.ros2.org/articles/node_lifecycle.html).
|
||||
|
||||
## Quality Declaration
|
||||
|
||||
|
||||
@@ -690,6 +690,22 @@ public:
|
||||
size_t
|
||||
count_subscribers(const std::string & topic_name) const;
|
||||
|
||||
/// Return the number of clients created for a given service.
|
||||
/**
|
||||
* \sa rclcpp::Node::count_clients
|
||||
*/
|
||||
RCLCPP_LIFECYCLE_PUBLIC
|
||||
size_t
|
||||
count_clients(const std::string & service_name) const;
|
||||
|
||||
/// Return the number of services created for a given service.
|
||||
/**
|
||||
* \sa rclcpp::Node::count_services
|
||||
*/
|
||||
RCLCPP_LIFECYCLE_PUBLIC
|
||||
size_t
|
||||
count_services(const std::string & service_name) const;
|
||||
|
||||
/// Return the topic endpoint information about publishers on a given topic.
|
||||
/**
|
||||
* \sa rclcpp::Node::get_publishers_info_by_topic
|
||||
|
||||
@@ -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>22.0.0</version>
|
||||
<version>23.2.0</version>
|
||||
<description>Package containing a prototype for lifecycle implementation</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
|
||||
@@ -144,6 +144,10 @@ LifecycleNode::LifecycleNode(
|
||||
&LifecycleNodeInterface::on_deactivate, this,
|
||||
std::placeholders::_1));
|
||||
register_on_error(std::bind(&LifecycleNodeInterface::on_error, this, std::placeholders::_1));
|
||||
|
||||
if (options.enable_logger_service()) {
|
||||
node_logging_->create_logger_services(node_services_);
|
||||
}
|
||||
}
|
||||
|
||||
LifecycleNode::~LifecycleNode()
|
||||
@@ -382,6 +386,18 @@ LifecycleNode::count_subscribers(const std::string & topic_name) const
|
||||
return node_graph_->count_subscribers(topic_name);
|
||||
}
|
||||
|
||||
size_t
|
||||
LifecycleNode::count_clients(const std::string & service_name) const
|
||||
{
|
||||
return node_graph_->count_clients(service_name);
|
||||
}
|
||||
|
||||
size_t
|
||||
LifecycleNode::count_services(const std::string & service_name) const
|
||||
{
|
||||
return node_graph_->count_services(service_name);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::TopicEndpointInfo>
|
||||
LifecycleNode::get_publishers_info_by_topic(const std::string & topic_name, bool no_mangle) const
|
||||
{
|
||||
|
||||
@@ -228,7 +228,8 @@ protected:
|
||||
BENCHMARK_F(BenchmarkLifecycleClient, get_state)(benchmark::State & state) {
|
||||
for (auto _ : state) {
|
||||
(void)_;
|
||||
const auto lifecycle_state = lifecycle_client->get_state();
|
||||
|
||||
lifecycle_msgs::msg::State lifecycle_state = lifecycle_client->get_state();
|
||||
if (lifecycle_state.id != lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED) {
|
||||
const std::string msg =
|
||||
std::string("Expected state did not match actual: ") +
|
||||
@@ -268,7 +269,7 @@ BENCHMARK_F(BenchmarkLifecycleClient, get_available_states)(benchmark::State & s
|
||||
for (auto _ : state) {
|
||||
(void)_;
|
||||
constexpr size_t expected_states = 11u;
|
||||
const auto states = lifecycle_client->get_available_states();
|
||||
std::vector<lifecycle_msgs::msg::State> states = lifecycle_client->get_available_states();
|
||||
if (states.size() != expected_states) {
|
||||
const std::string msg =
|
||||
std::string("Expected number of states did not match actual: ") +
|
||||
@@ -284,7 +285,8 @@ BENCHMARK_F(BenchmarkLifecycleClient, get_available_transitions)(benchmark::Stat
|
||||
for (auto _ : state) {
|
||||
(void)_;
|
||||
constexpr size_t expected_transitions = 2u;
|
||||
const auto transitions = lifecycle_client->get_available_transitions();
|
||||
std::vector<lifecycle_msgs::msg::TransitionDescription> transitions =
|
||||
lifecycle_client->get_available_transitions();
|
||||
if (transitions.size() != expected_transitions) {
|
||||
const std::string msg =
|
||||
std::string("Expected number of transitions did not match actual: ") +
|
||||
@@ -300,7 +302,8 @@ BENCHMARK_F(BenchmarkLifecycleClient, get_transition_graph)(benchmark::State & s
|
||||
for (auto _ : state) {
|
||||
(void)_;
|
||||
constexpr size_t expected_transitions = 25u;
|
||||
const auto transitions = lifecycle_client->get_transition_graph();
|
||||
std::vector<lifecycle_msgs::msg::TransitionDescription> transitions =
|
||||
lifecycle_client->get_transition_graph();
|
||||
if (transitions.size() != expected_transitions) {
|
||||
const std::string msg =
|
||||
std::string("Expected number of transitions did not match actual: ") +
|
||||
|
||||
@@ -97,13 +97,15 @@ protected:
|
||||
BENCHMARK_F(BenchmarkLifecycleNode, get_current_state)(benchmark::State & state) {
|
||||
for (auto _ : state) {
|
||||
(void)_;
|
||||
const auto & lifecycle_state = node->get_current_state();
|
||||
const rclcpp_lifecycle::State & lifecycle_state = node->get_current_state();
|
||||
if (lifecycle_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED) {
|
||||
const std::string message =
|
||||
std::string("Node's current state is: ") + std::to_string(lifecycle_state.id());
|
||||
state.SkipWithError(message.c_str());
|
||||
}
|
||||
benchmark::DoNotOptimize(lifecycle_state);
|
||||
// Google benchmark 1.8.2 warns that the constref DoNotOptimize signature may be optimized away
|
||||
// by the compiler. Cast const away to ensure we don't get that problem (and warning).
|
||||
benchmark::DoNotOptimize(const_cast<rclcpp_lifecycle::State &>(lifecycle_state));
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
}
|
||||
@@ -112,7 +114,7 @@ BENCHMARK_F(BenchmarkLifecycleNode, get_available_states)(benchmark::State & sta
|
||||
for (auto _ : state) {
|
||||
(void)_;
|
||||
constexpr size_t expected_states = 11u;
|
||||
const auto lifecycle_states = node->get_available_states();
|
||||
std::vector<rclcpp_lifecycle::State> lifecycle_states = node->get_available_states();
|
||||
if (lifecycle_states.size() != expected_states) {
|
||||
const std::string msg = std::to_string(lifecycle_states.size());
|
||||
state.SkipWithError(msg.c_str());
|
||||
@@ -126,7 +128,7 @@ BENCHMARK_F(BenchmarkLifecycleNode, get_available_transitions)(benchmark::State
|
||||
for (auto _ : state) {
|
||||
(void)_;
|
||||
constexpr size_t expected_transitions = 2u;
|
||||
const auto & transitions = node->get_available_transitions();
|
||||
std::vector<rclcpp_lifecycle::Transition> transitions = node->get_available_transitions();
|
||||
if (transitions.size() != expected_transitions) {
|
||||
const std::string msg = std::to_string(transitions.size());
|
||||
state.SkipWithError(msg.c_str());
|
||||
@@ -140,7 +142,7 @@ BENCHMARK_F(BenchmarkLifecycleNode, get_transition_graph)(benchmark::State & sta
|
||||
for (auto _ : state) {
|
||||
(void)_;
|
||||
constexpr size_t expected_transitions = 25u;
|
||||
const auto & transitions = node->get_transition_graph();
|
||||
std::vector<rclcpp_lifecycle::Transition> transitions = node->get_transition_graph();
|
||||
if (transitions.size() != expected_transitions) {
|
||||
const std::string msg =
|
||||
std::string("Expected number of transitions did not match actual: ") +
|
||||
@@ -162,18 +164,20 @@ BENCHMARK_F(BenchmarkLifecycleNode, transition_valid_state)(benchmark::State & s
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
(void)_;
|
||||
const auto & active =
|
||||
const rclcpp_lifecycle::State & active =
|
||||
node->trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_ACTIVATE);
|
||||
if (active.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) {
|
||||
state.SkipWithError("Transition to active state failed");
|
||||
}
|
||||
const auto & inactive =
|
||||
const rclcpp_lifecycle::State & inactive =
|
||||
node->trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_DEACTIVATE);
|
||||
if (inactive.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE) {
|
||||
state.SkipWithError("Transition to inactive state failed");
|
||||
}
|
||||
benchmark::DoNotOptimize(active);
|
||||
benchmark::DoNotOptimize(inactive);
|
||||
// Google benchmark 1.8.2 warns that the constref DoNotOptimize signature may be optimized away
|
||||
// by the compiler. Cast const away to ensure we don't get that problem (and warning).
|
||||
benchmark::DoNotOptimize(const_cast<rclcpp_lifecycle::State &>(active));
|
||||
benchmark::DoNotOptimize(const_cast<rclcpp_lifecycle::State &>(inactive));
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include "lifecycle_msgs/msg/transition.hpp"
|
||||
|
||||
#include "rcl_lifecycle/rcl_lifecycle.h"
|
||||
#include "rcl_interfaces/srv/get_logger_levels.hpp"
|
||||
#include "rcl_interfaces/srv/set_logger_levels.hpp"
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "rclcpp_lifecycle/lifecycle_node.hpp"
|
||||
@@ -34,6 +36,8 @@
|
||||
using lifecycle_msgs::msg::State;
|
||||
using lifecycle_msgs::msg::Transition;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static const std::chrono::nanoseconds DEFAULT_EVENT_TIMEOUT = std::chrono::seconds(3);
|
||||
static const std::chrono::nanoseconds DEFAULT_EVENT_SLEEP_PERIOD = std::chrono::milliseconds(100);
|
||||
|
||||
@@ -249,6 +253,35 @@ TEST_F(TestDefaultStateMachine, empty_initializer_rcl_errors) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, check_logger_services_exist) {
|
||||
// Logger level services are disabled
|
||||
{
|
||||
rclcpp::NodeOptions options = rclcpp::NodeOptions();
|
||||
options.enable_logger_service(false);
|
||||
auto node = std::make_shared<rclcpp_lifecycle::LifecycleNode>(
|
||||
"test_logger_service", "/test", options);
|
||||
auto get_client = node->create_client<rcl_interfaces::srv::GetLoggerLevels>(
|
||||
"/test/test_logger_service/get_logger_levels");
|
||||
ASSERT_FALSE(get_client->wait_for_service(2s));
|
||||
auto set_client = node->create_client<rcl_interfaces::srv::SetLoggerLevels>(
|
||||
"/test/test_logger_service/set_logger_levels");
|
||||
ASSERT_FALSE(set_client->wait_for_service(2s));
|
||||
}
|
||||
// Logger level services are enabled
|
||||
{
|
||||
rclcpp::NodeOptions options = rclcpp::NodeOptions();
|
||||
options.enable_logger_service(true);
|
||||
auto node = std::make_shared<rclcpp_lifecycle::LifecycleNode>(
|
||||
"test_logger_service", "/test", options);
|
||||
auto get_client = node->create_client<rcl_interfaces::srv::GetLoggerLevels>(
|
||||
"/test/test_logger_service/get_logger_levels");
|
||||
ASSERT_TRUE(get_client->wait_for_service(2s));
|
||||
auto set_client = node->create_client<rcl_interfaces::srv::SetLoggerLevels>(
|
||||
"/test/test_logger_service/set_logger_levels");
|
||||
ASSERT_TRUE(set_client->wait_for_service(2s));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, trigger_transition) {
|
||||
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
|
||||
|
||||
@@ -693,6 +726,17 @@ TEST_F(TestDefaultStateMachine, test_graph_services) {
|
||||
EXPECT_STREQ(
|
||||
service_names_and_types["/testnode/get_transition_graph"][0].c_str(),
|
||||
"lifecycle_msgs/srv/GetAvailableTransitions");
|
||||
|
||||
EXPECT_EQ(0u, test_node->count_clients("/testnode/change_state"));
|
||||
EXPECT_EQ(0u, test_node->count_clients("/testnode/get_available_states"));
|
||||
EXPECT_EQ(0u, test_node->count_clients("/testnode/get_available_transitions"));
|
||||
EXPECT_EQ(0u, test_node->count_clients("/testnode/get_state"));
|
||||
EXPECT_EQ(0u, test_node->count_clients("/testnode/get_transition_graph"));
|
||||
EXPECT_EQ(1u, test_node->count_services("/testnode/change_state"));
|
||||
EXPECT_EQ(1u, test_node->count_services("/testnode/get_available_states"));
|
||||
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"));
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, test_graph_services_by_node) {
|
||||
|
||||
Reference in New Issue
Block a user