Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83c282a161 | ||
|
|
226044022a | ||
|
|
7e9ff5f4c7 | ||
|
|
64dd644469 | ||
|
|
322b5f5d79 | ||
|
|
e854bb29cd | ||
|
|
88ebea94e9 | ||
|
|
1e6767ac13 | ||
|
|
37e3688026 | ||
|
|
9b654942f9 | ||
|
|
f12e3c69dc | ||
|
|
bcc14755f9 | ||
|
|
4567b6ec80 | ||
|
|
0be8aa013e | ||
|
|
41d3a27437 | ||
|
|
c50f0c9c3d | ||
|
|
f8aea8cc51 | ||
|
|
16290fb352 | ||
|
|
e133cc65f6 | ||
|
|
50a1e50133 | ||
|
|
1a0092a65b | ||
|
|
2813045a96 | ||
|
|
51dfdc3708 | ||
|
|
1f408e3b19 | ||
|
|
63105cd8a6 | ||
|
|
a78d0cbd33 | ||
|
|
918363d081 | ||
|
|
97c386ce40 | ||
|
|
1a3dfaf45c | ||
|
|
f7056c0d86 | ||
|
|
2f71d6e249 | ||
|
|
e846f56224 | ||
|
|
ee94bc63e4 | ||
|
|
b7c789328a | ||
|
|
4b1c125cac | ||
|
|
dfaaf4739a | ||
|
|
e6b6faf152 | ||
|
|
f961ca7855 | ||
|
|
6da8363582 | ||
|
|
ab7cf878c2 | ||
|
|
496c45549b | ||
|
|
2739327ee9 | ||
|
|
4e4f0cf43b | ||
|
|
c46896731c | ||
|
|
45adf6565f | ||
|
|
6f5c6f45d7 | ||
|
|
9b1e6c9d52 |
@@ -2,6 +2,54 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
29.2.0 (2024-11-25)
|
||||
-------------------
|
||||
* accept custom allocator for LoanedMessage. (`#2672 <https://github.com/ros2/rclcpp/issues/2672>`_)
|
||||
* Contributors: Tomoya Fujita
|
||||
|
||||
29.1.0 (2024-11-20)
|
||||
-------------------
|
||||
* a couple of typo fixes in doc section for LoanedMessage. (`#2676 <https://github.com/ros2/rclcpp/issues/2676>`_)
|
||||
* Make sure callback_end tracepoint is triggered in AnyServiceCallback (`#2670 <https://github.com/ros2/rclcpp/issues/2670>`_)
|
||||
* Correct the incorrect comments in generic_client.hpp (`#2662 <https://github.com/ros2/rclcpp/issues/2662>`_)
|
||||
* Fix NodeOptions assignment operator (`#2656 <https://github.com/ros2/rclcpp/issues/2656>`_)
|
||||
* set QoS History KEEP_ALL explicitly for statistics publisher. (`#2650 <https://github.com/ros2/rclcpp/issues/2650>`_)
|
||||
* Fix test_intra_process_manager.cpp with rmw_zenoh_cpp (`#2653 <https://github.com/ros2/rclcpp/issues/2653>`_)
|
||||
* Fixed test_events_executors in zenoh (`#2643 <https://github.com/ros2/rclcpp/issues/2643>`_)
|
||||
* rmw_fastrtps supports service event gid uniqueness test. (`#2638 <https://github.com/ros2/rclcpp/issues/2638>`_)
|
||||
* print warning if event callback is not supported instead of passing exception. (`#2648 <https://github.com/ros2/rclcpp/issues/2648>`_)
|
||||
* Implement callback support of async_send_request for service generic client (`#2614 <https://github.com/ros2/rclcpp/issues/2614>`_)
|
||||
* Contributors: Alejandro Hernández Cordero, Barry Xu, Chris Lalancette, Christophe Bedard, Romain DESILLE, Tomoya Fujita
|
||||
|
||||
29.0.0 (2024-10-03)
|
||||
-------------------
|
||||
* Fixed test qos rmw zenoh (`#2639 <https://github.com/ros2/rclcpp/issues/2639>`_)
|
||||
* verify client gid uniqueness for a single service event. (`#2636 <https://github.com/ros2/rclcpp/issues/2636>`_)
|
||||
* Skip some tests in test_qos_event and run others with event types supported by rmw_zenoh (`#2626 <https://github.com/ros2/rclcpp/issues/2626>`_)
|
||||
* Shutdown the context before context's destructor is invoked in tests (`#2633 <https://github.com/ros2/rclcpp/issues/2633>`_)
|
||||
* Skip rmw zenoh content filtering tests (`#2627 <https://github.com/ros2/rclcpp/issues/2627>`_)
|
||||
* Use InvalidServiceTypeError for unavailable service type in GenericClient (`#2629 <https://github.com/ros2/rclcpp/issues/2629>`_)
|
||||
* Implement generic service (`#2617 <https://github.com/ros2/rclcpp/issues/2617>`_)
|
||||
* fix events-executor warm-up bug and add unit-tests (`#2591 <https://github.com/ros2/rclcpp/issues/2591>`_)
|
||||
* remove unnecessary gtest-skip in test_executors (`#2600 <https://github.com/ros2/rclcpp/issues/2600>`_)
|
||||
* Correct node name in service test code (`#2615 <https://github.com/ros2/rclcpp/issues/2615>`_)
|
||||
* Minor naming fixes for ParameterValue to_string() function (`#2609 <https://github.com/ros2/rclcpp/issues/2609>`_)
|
||||
* Removed clang warnings (`#2605 <https://github.com/ros2/rclcpp/issues/2605>`_)
|
||||
* Fix a couple of issues in the documentation. (`#2608 <https://github.com/ros2/rclcpp/issues/2608>`_)
|
||||
* deprecate the static single threaded executor (`#2598 <https://github.com/ros2/rclcpp/issues/2598>`_)
|
||||
* Fix name of ParameterEventHandler class in doc (`#2604 <https://github.com/ros2/rclcpp/issues/2604>`_)
|
||||
* subscriber_statistics_collectors\_ should be protected by mutex. (`#2592 <https://github.com/ros2/rclcpp/issues/2592>`_)
|
||||
* Fix bug in timers lifecycle for events executor (`#2586 <https://github.com/ros2/rclcpp/issues/2586>`_)
|
||||
* fix rclcpp/test/rclcpp/CMakeLists.txt to check for the correct targets existance (`#2596 <https://github.com/ros2/rclcpp/issues/2596>`_)
|
||||
* Shut down context during init if logging config fails (`#2594 <https://github.com/ros2/rclcpp/issues/2594>`_)
|
||||
* Make more of the Waitable API abstract (`#2593 <https://github.com/ros2/rclcpp/issues/2593>`_)
|
||||
* Contributors: Alberto Soragna, Alejandro Hernández Cordero, Alexis Pojomovsky, Barry Xu, Chris Lalancette, Christophe Bedard, Kang, Hsin-Yi, Tomoya Fujita
|
||||
|
||||
28.3.3 (2024-07-29)
|
||||
-------------------
|
||||
* Only compile the tests once. (`#2590 <https://github.com/ros2/rclcpp/issues/2590>`_)
|
||||
* Contributors: Chris Lalancette
|
||||
|
||||
28.3.2 (2024-07-24)
|
||||
-------------------
|
||||
* Updated rcpputils path API (`#2579 <https://github.com/ros2/rclcpp/issues/2579>`_)
|
||||
|
||||
@@ -77,6 +77,7 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/future_return_code.cpp
|
||||
src/rclcpp/generic_client.cpp
|
||||
src/rclcpp/generic_publisher.cpp
|
||||
src/rclcpp/generic_service.cpp
|
||||
src/rclcpp/generic_subscription.cpp
|
||||
src/rclcpp/graph_listener.cpp
|
||||
src/rclcpp/guard_condition.cpp
|
||||
|
||||
@@ -165,11 +165,13 @@ public:
|
||||
if (std::holds_alternative<SharedPtrDeferResponseCallback>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrDeferResponseCallback>(callback_);
|
||||
cb(request_header, std::move(request));
|
||||
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
return nullptr;
|
||||
}
|
||||
if (std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_);
|
||||
cb(service_handle, request_header, std::move(request));
|
||||
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
return nullptr;
|
||||
}
|
||||
// auto response = allocate_shared<typename ServiceT::Response, Allocator>();
|
||||
|
||||
102
rclcpp/include/rclcpp/create_generic_service.hpp
Normal file
102
rclcpp/include/rclcpp/create_generic_service.hpp
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright 2024 Sony Group Corporation.
|
||||
//
|
||||
// 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__CREATE_GENERIC_SERVICE_HPP_
|
||||
#define RCLCPP__CREATE_GENERIC_SERVICE_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/generic_service.hpp"
|
||||
#include "rclcpp/node_interfaces/get_node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/get_node_services_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_services_interface.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
/// Create a generic service with a given type.
|
||||
/**
|
||||
* \param[in] node_base NodeBaseInterface implementation of the node on which
|
||||
* to create the generic service.
|
||||
* \param[in] node_services NodeServicesInterface implementation of the node on
|
||||
* which to create the service.
|
||||
* \param[in] service_name The name on which the service is accessible.
|
||||
* \param[in] service_type The name of service type, e.g. "std_srvs/srv/SetBool".
|
||||
* \param[in] callback The callback to call when the service gets a request.
|
||||
* \param[in] qos Quality of service profile for the service.
|
||||
* \param[in] group Callback group to handle the reply to service calls.
|
||||
* \return Shared pointer to the created service.
|
||||
*/
|
||||
template<typename CallbackT>
|
||||
typename rclcpp::GenericService::SharedPtr
|
||||
create_generic_service(
|
||||
std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
|
||||
std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
|
||||
const std::string & service_name,
|
||||
const std::string & service_type,
|
||||
CallbackT && callback,
|
||||
const rclcpp::QoS & qos,
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
rclcpp::GenericServiceCallback any_service_callback;
|
||||
any_service_callback.set(std::forward<CallbackT>(callback));
|
||||
|
||||
rcl_service_options_t service_options = rcl_service_get_default_options();
|
||||
service_options.qos = qos.get_rmw_qos_profile();
|
||||
|
||||
auto serv = GenericService::make_shared(
|
||||
node_base->get_shared_rcl_node_handle(),
|
||||
service_name, service_type, any_service_callback, service_options);
|
||||
auto serv_base_ptr = std::dynamic_pointer_cast<ServiceBase>(serv);
|
||||
node_services->add_service(serv_base_ptr, group);
|
||||
return serv;
|
||||
}
|
||||
|
||||
/// Create a generic service with a given type.
|
||||
/**
|
||||
* The NodeT type needs to have NodeBaseInterface implementation and NodeServicesInterface
|
||||
* implementation of the node which to create the generic service.
|
||||
*
|
||||
* \param[in] node The node on which to create the generic service.
|
||||
* \param[in] service_name The name on which the service is accessible.
|
||||
* \param[in] service_type The name of service type, e.g. "std_srvs/srv/SetBool".
|
||||
* \param[in] callback The callback to call when the service gets a request.
|
||||
* \param[in] qos Quality of service profile for the service.
|
||||
* \param[in] group Callback group to handle the reply to service calls.
|
||||
* \return Shared pointer to the created service.
|
||||
*/
|
||||
template<typename NodeT, typename CallbackT>
|
||||
typename rclcpp::GenericService::SharedPtr
|
||||
create_generic_service(
|
||||
NodeT node,
|
||||
const std::string & service_name,
|
||||
const std::string & service_type,
|
||||
CallbackT && callback,
|
||||
const rclcpp::QoS & qos,
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
return create_generic_service<CallbackT>(
|
||||
rclcpp::node_interfaces::get_node_base_interface(node),
|
||||
rclcpp::node_interfaces::get_node_services_interface(node),
|
||||
service_name,
|
||||
service_type,
|
||||
std::forward<CallbackT>(callback), qos.get_rmw_qos_profile(), group);
|
||||
}
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__CREATE_GENERIC_SERVICE_HPP_
|
||||
@@ -100,6 +100,15 @@ public:
|
||||
{}
|
||||
};
|
||||
|
||||
class InvalidServiceTypeError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
InvalidServiceTypeError()
|
||||
: std::runtime_error("Service type is invalid.") {}
|
||||
explicit InvalidServiceTypeError(const std::string & msg)
|
||||
: std::runtime_error(msg) {}
|
||||
};
|
||||
|
||||
class UnimplementedError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -31,9 +31,15 @@ namespace executors
|
||||
/// Static executor implementation
|
||||
/**
|
||||
* This executor is a static version of the original single threaded executor.
|
||||
* It's static because it doesn't reconstruct the executable list for every iteration.
|
||||
* It contains some performance optimization to avoid unnecessary reconstructions of
|
||||
* the executable list for every iteration.
|
||||
* All nodes, callbackgroups, timers, subscriptions etc. are created before
|
||||
* spin() is called, and modified only when an entity is added/removed to/from a node.
|
||||
* This executor is deprecated because these performance improvements have now been
|
||||
* applied to all other executors.
|
||||
* This executor is also considered unstable due to known bugs.
|
||||
* See the unit-tests that are only applied to `StandardExecutors` for information
|
||||
* on the known limitations.
|
||||
*
|
||||
* To run this executor instead of SingleThreadedExecutor replace:
|
||||
* rclcpp::executors::SingleThreadedExecutor exec;
|
||||
@@ -44,7 +50,8 @@ namespace executors
|
||||
* exec.spin();
|
||||
* exec.remove_node(node);
|
||||
*/
|
||||
class StaticSingleThreadedExecutor : public rclcpp::Executor
|
||||
class [[deprecated("Use rclcpp::executors::SingleThreadedExecutor")]] StaticSingleThreadedExecutor
|
||||
: public rclcpp::Executor
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(StaticSingleThreadedExecutor)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <memory>
|
||||
#include <future>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
@@ -35,8 +36,8 @@ namespace rclcpp
|
||||
class GenericClient : public ClientBase
|
||||
{
|
||||
public:
|
||||
using Request = void *; // Serialized data pointer of request message
|
||||
using Response = void *; // Serialized data pointer of response message
|
||||
using Request = void *; // Deserialized data pointer of request message
|
||||
using Response = void *; // Deserialized data pointer of response message
|
||||
|
||||
using SharedResponse = std::shared_ptr<void>;
|
||||
|
||||
@@ -46,6 +47,8 @@ public:
|
||||
using Future = std::future<SharedResponse>;
|
||||
using SharedFuture = std::shared_future<SharedResponse>;
|
||||
|
||||
using CallbackType = std::function<void (SharedFuture)>;
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(GenericClient)
|
||||
|
||||
/// A convenient GenericClient::Future and request id pair.
|
||||
@@ -76,6 +79,20 @@ public:
|
||||
~FutureAndRequestId() = default;
|
||||
};
|
||||
|
||||
/// A convenient GenericClient::SharedFuture and request id pair.
|
||||
/**
|
||||
* Public members:
|
||||
* - future: a std::shared_future<SharedResponse>.
|
||||
* - request_id: the request id associated with the future.
|
||||
*
|
||||
* All the other methods are equivalent to the ones std::shared_future provides.
|
||||
*/
|
||||
struct SharedFutureAndRequestId
|
||||
: detail::FutureAndRequestId<std::shared_future<SharedResponse>>
|
||||
{
|
||||
using detail::FutureAndRequestId<std::shared_future<SharedResponse>>::FutureAndRequestId;
|
||||
};
|
||||
|
||||
GenericClient(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
|
||||
@@ -106,16 +123,16 @@ public:
|
||||
* If the future never completes,
|
||||
* e.g. the call to Executor::spin_until_future_complete() times out,
|
||||
* GenericClient::remove_pending_request() must be called to clean the client internal state.
|
||||
* Not doing so will make the `Client` instance to use more memory each time a response is not
|
||||
* received from the service server.
|
||||
* Not doing so will make the `GenericClient` instance to use more memory each time a response is
|
||||
* not received from the service server.
|
||||
*
|
||||
* ```cpp
|
||||
* auto future = client->async_send_request(my_request);
|
||||
* auto future = generic_client->async_send_request(my_request);
|
||||
* if (
|
||||
* rclcpp::FutureReturnCode::TIMEOUT ==
|
||||
* executor->spin_until_future_complete(future, timeout))
|
||||
* {
|
||||
* client->remove_pending_request(future);
|
||||
* generic_client->remove_pending_request(future);
|
||||
* // handle timeout
|
||||
* } else {
|
||||
* handle_response(future.get());
|
||||
@@ -129,6 +146,45 @@ public:
|
||||
FutureAndRequestId
|
||||
async_send_request(const Request request);
|
||||
|
||||
/// Send a request to the service server and schedule a callback in the executor.
|
||||
/**
|
||||
* Similar to the previous overload, but a callback will automatically be called when a response
|
||||
* is received.
|
||||
*
|
||||
* If the callback is never called, because we never got a reply for the service server,
|
||||
* remove_pending_request() has to be called with the returned request id or
|
||||
* prune_pending_requests().
|
||||
* Not doing so will make the `GenericClient` instance use more memory each time a response is not
|
||||
* received from the service server.
|
||||
* In this case, it's convenient to setup a timer to cleanup the pending requests.
|
||||
*
|
||||
* \param[in] request request to be send.
|
||||
* \param[in] cb callback that will be called when we get a response for this request.
|
||||
* \return the request id representing the request just sent.
|
||||
*/
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
CallbackType
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
SharedFutureAndRequestId
|
||||
async_send_request(const Request request, CallbackT && cb)
|
||||
{
|
||||
Promise promise;
|
||||
auto shared_future = promise.get_future().share();
|
||||
auto req_id = async_send_request_impl(
|
||||
request,
|
||||
std::make_tuple(
|
||||
CallbackType{std::forward<CallbackT>(cb)},
|
||||
shared_future,
|
||||
std::move(promise)));
|
||||
return SharedFutureAndRequestId{std::move(shared_future), req_id};
|
||||
}
|
||||
|
||||
/// Clean all pending requests older than a time_point.
|
||||
/**
|
||||
* \param[in] time_point Requests that were sent before this point are going to be removed.
|
||||
@@ -149,15 +205,52 @@ public:
|
||||
pruned_requests);
|
||||
}
|
||||
|
||||
/// Clean all pending requests.
|
||||
/**
|
||||
* \return number of pending requests that were removed.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
prune_pending_requests();
|
||||
|
||||
/// Cleanup a pending request.
|
||||
/**
|
||||
* This notifies the client that we have waited long enough for a response from the server
|
||||
* to come, we have given up and we are not waiting for a response anymore.
|
||||
*
|
||||
* Not calling this will make the client start using more memory for each request
|
||||
* that never got a reply from the server.
|
||||
*
|
||||
* \param[in] request_id request id returned by async_send_request().
|
||||
* \return true when a pending request was removed, false if not (e.g. a response was received).
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
remove_pending_request(
|
||||
int64_t request_id);
|
||||
|
||||
/// Cleanup a pending request.
|
||||
/**
|
||||
* Convenient overload, same as:
|
||||
*
|
||||
* `GenericClient::remove_pending_request(this, future.request_id)`.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
remove_pending_request(
|
||||
const FutureAndRequestId & future);
|
||||
|
||||
/// Cleanup a pending request.
|
||||
/**
|
||||
* Convenient overload, same as:
|
||||
*
|
||||
* `GenericClient::remove_pending_request(this, future.request_id)`.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
remove_pending_request(
|
||||
const SharedFutureAndRequestId & future);
|
||||
|
||||
/// Take the next response for this client.
|
||||
/**
|
||||
* \sa ClientBase::take_type_erased_response().
|
||||
@@ -179,9 +272,12 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
using CallbackTypeValueVariant = std::tuple<CallbackType, SharedFuture, Promise>;
|
||||
using CallbackInfoVariant = std::variant<
|
||||
std::promise<SharedResponse>>; // Use variant for extension
|
||||
std::promise<SharedResponse>,
|
||||
CallbackTypeValueVariant>; // Use variant for extension
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
int64_t
|
||||
async_send_request_impl(
|
||||
const Request request,
|
||||
|
||||
308
rclcpp/include/rclcpp/generic_service.hpp
Normal file
308
rclcpp/include/rclcpp/generic_service.hpp
Normal file
@@ -0,0 +1,308 @@
|
||||
// Copyright 2024 Sony Group Corporation.
|
||||
//
|
||||
// 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__GENERIC_SERVICE_HPP_
|
||||
#define RCLCPP__GENERIC_SERVICE_HPP_
|
||||
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "rclcpp/typesupport_helpers.hpp"
|
||||
|
||||
#include "rosidl_runtime_c/service_type_support_struct.h"
|
||||
#include "rosidl_typesupport_introspection_cpp/identifier.hpp"
|
||||
#include "rosidl_typesupport_introspection_cpp/service_introspection.hpp"
|
||||
|
||||
#include "service.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
class GenericService;
|
||||
|
||||
class GenericServiceCallback
|
||||
{
|
||||
public:
|
||||
using SharedRequest = std::shared_ptr<void>;
|
||||
using SharedResponse = std::shared_ptr<void>;
|
||||
|
||||
GenericServiceCallback()
|
||||
: callback_(std::monostate{})
|
||||
{}
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if_t<!detail::can_be_nullptr<CallbackT>::value, int> = 0>
|
||||
void
|
||||
set(CallbackT && callback)
|
||||
{
|
||||
// Workaround Windows issue with std::bind
|
||||
if constexpr (
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrCallback>(callback);
|
||||
} else if constexpr ( // NOLINT, can't satisfy both cpplint and uncrustify
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrWithRequestHeaderCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
|
||||
} else if constexpr ( // NOLINT
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrDeferResponseCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
|
||||
} else if constexpr ( // NOLINT
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrDeferResponseCallbackWithServiceHandle
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
|
||||
} else {
|
||||
// the else clause is not needed, but anyways we should only be doing this instead
|
||||
// of all the above workaround ...
|
||||
callback_ = std::forward<CallbackT>(callback);
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if_t<detail::can_be_nullptr<CallbackT>::value, int> = 0>
|
||||
void
|
||||
set(CallbackT && callback)
|
||||
{
|
||||
if (!callback) {
|
||||
throw std::invalid_argument("AnyServiceCallback::set(): callback cannot be nullptr");
|
||||
}
|
||||
// Workaround Windows issue with std::bind
|
||||
if constexpr (
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrCallback>(callback);
|
||||
} else if constexpr ( // NOLINT
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrWithRequestHeaderCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
|
||||
} else if constexpr ( // NOLINT
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrDeferResponseCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
|
||||
} else if constexpr ( // NOLINT
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrDeferResponseCallbackWithServiceHandle
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
|
||||
} else {
|
||||
// the else clause is not needed, but anyways we should only be doing this instead
|
||||
// of all the above workaround ...
|
||||
callback_ = std::forward<CallbackT>(callback);
|
||||
}
|
||||
}
|
||||
|
||||
SharedResponse
|
||||
dispatch(
|
||||
const std::shared_ptr<rclcpp::GenericService> & service_handle,
|
||||
const std::shared_ptr<rmw_request_id_t> & request_header,
|
||||
SharedRequest request,
|
||||
SharedRequest response)
|
||||
{
|
||||
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
|
||||
if (std::holds_alternative<std::monostate>(callback_)) {
|
||||
// TODO(ivanpauno): Remove the set method, and force the users of this class
|
||||
// to pass a callback at construnciton.
|
||||
throw std::runtime_error{"unexpected request without any callback set"};
|
||||
}
|
||||
if (std::holds_alternative<SharedPtrDeferResponseCallback>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrDeferResponseCallback>(callback_);
|
||||
cb(request_header, std::move(request));
|
||||
return nullptr;
|
||||
}
|
||||
if (std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_);
|
||||
cb(service_handle, request_header, std::move(request));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (std::holds_alternative<SharedPtrCallback>(callback_)) {
|
||||
(void)request_header;
|
||||
const auto & cb = std::get<SharedPtrCallback>(callback_);
|
||||
cb(std::move(request), std::move(response));
|
||||
} else if (std::holds_alternative<SharedPtrWithRequestHeaderCallback>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrWithRequestHeaderCallback>(callback_);
|
||||
cb(request_header, std::move(request), std::move(response));
|
||||
}
|
||||
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
return response;
|
||||
}
|
||||
|
||||
void register_callback_for_tracing()
|
||||
{
|
||||
#ifndef TRACETOOLS_DISABLED
|
||||
std::visit(
|
||||
[this](auto && arg) {
|
||||
if (TRACETOOLS_TRACEPOINT_ENABLED(rclcpp_callback_register)) {
|
||||
char * symbol = tracetools::get_symbol(arg);
|
||||
TRACETOOLS_DO_TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
static_cast<const void *>(this),
|
||||
symbol);
|
||||
std::free(symbol);
|
||||
}
|
||||
}, callback_);
|
||||
#endif // TRACETOOLS_DISABLED
|
||||
}
|
||||
|
||||
private:
|
||||
using SharedPtrCallback = std::function<void (SharedRequest, SharedResponse)>;
|
||||
using SharedPtrWithRequestHeaderCallback = std::function<
|
||||
void (
|
||||
std::shared_ptr<rmw_request_id_t>,
|
||||
SharedRequest,
|
||||
SharedResponse
|
||||
)>;
|
||||
using SharedPtrDeferResponseCallback = std::function<
|
||||
void (
|
||||
std::shared_ptr<rmw_request_id_t>,
|
||||
SharedRequest
|
||||
)>;
|
||||
using SharedPtrDeferResponseCallbackWithServiceHandle = std::function<
|
||||
void (
|
||||
std::shared_ptr<rclcpp::GenericService>,
|
||||
std::shared_ptr<rmw_request_id_t>,
|
||||
SharedRequest
|
||||
)>;
|
||||
|
||||
std::variant<
|
||||
std::monostate,
|
||||
SharedPtrCallback,
|
||||
SharedPtrWithRequestHeaderCallback,
|
||||
SharedPtrDeferResponseCallback,
|
||||
SharedPtrDeferResponseCallbackWithServiceHandle> callback_;
|
||||
};
|
||||
|
||||
class GenericService
|
||||
: public ServiceBase,
|
||||
public std::enable_shared_from_this<GenericService>
|
||||
{
|
||||
public:
|
||||
using Request = void *; // Serialized/Deserialized data pointer of request message
|
||||
using Response = void *; // Serialized/Deserialized data pointer of response message
|
||||
using SharedRequest = std::shared_ptr<void>;
|
||||
using SharedResponse = std::shared_ptr<void>;
|
||||
using CallbackType = std::function<void (const SharedRequest, SharedResponse)>;
|
||||
|
||||
using CallbackWithHeaderType =
|
||||
std::function<void (const std::shared_ptr<rmw_request_id_t>,
|
||||
const SharedRequest,
|
||||
SharedResponse)>;
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(GenericService)
|
||||
|
||||
/// Default constructor.
|
||||
/**
|
||||
* The constructor for a Service is almost never called directly.
|
||||
* Instead, services should be instantiated through the function
|
||||
* rclcpp::create_service().
|
||||
*
|
||||
* \param[in] node_handle NodeBaseInterface pointer that is used in part of the setup.
|
||||
* \param[in] service_name Name of the topic to publish to.
|
||||
* \param[in] service_type The name of service type, e.g. "std_srvs/srv/SetBool".
|
||||
* \param[in] any_callback User defined callback to call when a client request is received.
|
||||
* \param[in] service_options options for the service.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
GenericService(
|
||||
std::shared_ptr<rcl_node_t> node_handle,
|
||||
const std::string & service_name,
|
||||
const std::string & service_type,
|
||||
GenericServiceCallback any_callback,
|
||||
rcl_service_options_t & service_options);
|
||||
|
||||
GenericService() = delete;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~GenericService() {}
|
||||
|
||||
/// Take the next request from the service.
|
||||
/**
|
||||
* \sa ServiceBase::take_type_erased_request().
|
||||
*
|
||||
* \param[out] request_out The reference to a service deserialized request object
|
||||
* into which the middleware will copy the taken request.
|
||||
* \param[out] request_id_out The output id for the request which can be used
|
||||
* to associate response with this request in the future.
|
||||
* \returns true if the request was taken, otherwise false.
|
||||
* \throws rclcpp::exceptions::RCLError based exceptions if the underlying
|
||||
* rcl calls fail.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
take_request(SharedRequest request_out, rmw_request_id_t & request_id_out);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<void>
|
||||
create_request() override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<void>
|
||||
create_response();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<rmw_request_id_t>
|
||||
create_request_header() override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
handle_request(
|
||||
std::shared_ptr<rmw_request_id_t> request_header,
|
||||
std::shared_ptr<void> request) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
send_response(rmw_request_id_t & req_id, SharedResponse & response);
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(GenericService)
|
||||
|
||||
GenericServiceCallback any_callback_;
|
||||
|
||||
std::shared_ptr<rcpputils::SharedLibrary> ts_lib_;
|
||||
const rosidl_typesupport_introspection_cpp::MessageMembers * request_members_;
|
||||
const rosidl_typesupport_introspection_cpp::MessageMembers * response_members_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
#endif // RCLCPP__GENERIC_SERVICE_HPP_
|
||||
@@ -31,21 +31,20 @@ namespace rclcpp
|
||||
template<typename MessageT, typename AllocatorT = std::allocator<void>>
|
||||
class LoanedMessage
|
||||
{
|
||||
public:
|
||||
using MessageAllocatorTraits = rclcpp::allocator::AllocRebind<MessageT, AllocatorT>;
|
||||
using MessageAllocator = typename MessageAllocatorTraits::allocator_type;
|
||||
|
||||
public:
|
||||
/// Constructor of the LoanedMessage class.
|
||||
/**
|
||||
* The constructor of this class allocates memory for a given message type
|
||||
* and associates this with a given publisher.
|
||||
*
|
||||
* Given the publisher instance, a case differentiation is being performaned
|
||||
* which decides whether the underlying middleware is able to allocate the appropriate
|
||||
* memory for this message type or not.
|
||||
* In the case that the middleware can not loan messages, the passed in allocator instance
|
||||
* is being used to allocate the message within the scope of this class.
|
||||
* Otherwise, the allocator is being ignored and the allocation is solely performaned
|
||||
* The underlying middleware is queried to determine whether it is able to allocate the
|
||||
* appropriate memory for this message type or not.
|
||||
* In the case that the middleware cannot loan messages, the passed in allocator instance
|
||||
* is used to allocate the message within the scope of this class.
|
||||
* Otherwise, the allocator is ignored and the allocation is solely performed
|
||||
* in the underlying middleware with its appropriate allocation strategy.
|
||||
* The need for this arises as the user code can be written explicitly targeting a middleware
|
||||
* capable of loaning messages.
|
||||
@@ -53,12 +52,12 @@ public:
|
||||
* a middleware which doesn't support message loaning in which case the allocator will be used.
|
||||
*
|
||||
* \param[in] pub rclcpp::Publisher instance to which the memory belongs
|
||||
* \param[in] allocator Allocator instance in case middleware can not allocate messages
|
||||
* \param[in] allocator Allocator instance in case middleware cannot allocate messages
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
*/
|
||||
LoanedMessage(
|
||||
const rclcpp::PublisherBase & pub,
|
||||
std::allocator<MessageT> allocator)
|
||||
MessageAllocator allocator)
|
||||
: pub_(pub),
|
||||
message_(nullptr),
|
||||
message_allocator_(std::move(allocator))
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "rclcpp/event.hpp"
|
||||
#include "rclcpp/generic_client.hpp"
|
||||
#include "rclcpp/generic_publisher.hpp"
|
||||
#include "rclcpp/generic_service.hpp"
|
||||
#include "rclcpp/generic_subscription.hpp"
|
||||
#include "rclcpp/logger.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
@@ -303,6 +304,24 @@ public:
|
||||
const rclcpp::QoS & qos = rclcpp::ServicesQoS(),
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
/// Create and return a GenericService.
|
||||
/**
|
||||
* \param[in] service_name The topic to service on.
|
||||
* \param[in] service_type The name of service type, e.g. "std_srvs/srv/SetBool"
|
||||
* \param[in] callback User-defined callback function.
|
||||
* \param[in] qos Quality of service profile for the service.
|
||||
* \param[in] group Callback group to call the service.
|
||||
* \return Shared pointer to the created service.
|
||||
*/
|
||||
template<typename CallbackT>
|
||||
typename rclcpp::GenericService::SharedPtr
|
||||
create_generic_service(
|
||||
const std::string & service_name,
|
||||
const std::string & service_type,
|
||||
CallbackT && callback,
|
||||
const rclcpp::QoS & qos = rclcpp::ServicesQoS(),
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
/// Create and return a GenericPublisher.
|
||||
/**
|
||||
* The returned pointer will never be empty, but this function can throw various exceptions, for
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "rclcpp/create_generic_subscription.hpp"
|
||||
#include "rclcpp/create_publisher.hpp"
|
||||
#include "rclcpp/create_service.hpp"
|
||||
#include "rclcpp/create_generic_service.hpp"
|
||||
#include "rclcpp/create_subscription.hpp"
|
||||
#include "rclcpp/create_timer.hpp"
|
||||
#include "rclcpp/detail/resolve_enable_topic_statistics.hpp"
|
||||
@@ -171,6 +172,25 @@ Node::create_service(
|
||||
group);
|
||||
}
|
||||
|
||||
template<typename CallbackT>
|
||||
typename rclcpp::GenericService::SharedPtr
|
||||
Node::create_generic_service(
|
||||
const std::string & service_name,
|
||||
const std::string & service_type,
|
||||
CallbackT && callback,
|
||||
const rclcpp::QoS & qos,
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
return rclcpp::create_generic_service<CallbackT>(
|
||||
node_base_,
|
||||
node_services_,
|
||||
extend_name_with_sub_namespace(service_name, this->get_sub_namespace()),
|
||||
service_type,
|
||||
std::forward<CallbackT>(callback),
|
||||
qos,
|
||||
group);
|
||||
}
|
||||
|
||||
template<typename AllocatorT>
|
||||
std::shared_ptr<rclcpp::GenericPublisher>
|
||||
Node::create_generic_publisher(
|
||||
|
||||
@@ -67,19 +67,23 @@ struct ParameterEventCallbackHandle
|
||||
* The first step is to instantiate a ParameterEventHandler, providing a ROS node to use
|
||||
* to create any required subscriptions:
|
||||
*
|
||||
* auto param_handler = std::make_shared<rclcpp::ParameterEventHandler>(node);
|
||||
* ```cpp
|
||||
* auto param_handler = std::make_shared<rclcpp::ParameterEventHandler>(node);
|
||||
* ```
|
||||
*
|
||||
* Next, you can supply a callback to the add_parameter_callback method, as follows:
|
||||
*
|
||||
* auto cb1 = [&node](const rclcpp::Parameter & p) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb1: Received an update to parameter \"%s\" of type %s: \"%ld\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_int());
|
||||
* };
|
||||
* auto handle1 = param_handler->add_parameter_callback("an_int_param", cb1);
|
||||
* ```cpp
|
||||
* auto cb1 = [&node](const rclcpp::Parameter & p) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb1: Received an update to parameter \"%s\" of type %s: \"%ld\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_int());
|
||||
* };
|
||||
* auto handle1 = param_handler->add_parameter_callback("an_int_param", cb1);
|
||||
* ```
|
||||
*
|
||||
* In this case, we didn't supply a node name (the third, optional, parameter) so the
|
||||
* default will be to monitor for changes to the "an_int_param" parameter associated with
|
||||
@@ -92,16 +96,18 @@ struct ParameterEventCallbackHandle
|
||||
* You may also monitor for changes to parameters in other nodes by supplying the node
|
||||
* name to add_parameter_callback:
|
||||
*
|
||||
* auto cb2 = [&node](const rclcpp::Parameter & p) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb2: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_string().c_str());
|
||||
* };
|
||||
* auto handle2 = param_handler->add_parameter_callback(
|
||||
* "some_remote_param_name", cb2, "some_remote_node_name");
|
||||
* ```cpp
|
||||
* auto cb2 = [&node](const rclcpp::Parameter & p) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb2: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_string().c_str());
|
||||
* };
|
||||
* auto handle2 = param_handler->add_parameter_callback(
|
||||
* "some_remote_param_name", cb2, "some_remote_node_name");
|
||||
* ```
|
||||
*
|
||||
* In this case, the callback will be invoked whenever "some_remote_param_name" changes
|
||||
* on remote node "some_remote_node_name".
|
||||
@@ -109,7 +115,9 @@ struct ParameterEventCallbackHandle
|
||||
* To remove a parameter callback, reset the callback handle smart pointer or call
|
||||
* remove_parameter_callback, passing the handle returned from add_parameter_callback:
|
||||
*
|
||||
* param_handler->remove_parameter_callback(handle2);
|
||||
* ```cpp
|
||||
* param_handler->remove_parameter_callback(handle2);
|
||||
* ```
|
||||
*
|
||||
* You can also monitor for *all* parameter changes, using add_parameter_event_callback.
|
||||
* In this case, the callback will be invoked whenever any parameter changes in the system.
|
||||
@@ -117,40 +125,42 @@ struct ParameterEventCallbackHandle
|
||||
* is convenient to use a regular expression on the node names or namespaces of interest.
|
||||
* For example:
|
||||
*
|
||||
* auto cb3 =
|
||||
* [fqn, remote_param_name, &node](const rcl_interfaces::msg::ParameterEvent & event) {
|
||||
* // Look for any updates to parameters in "/a_namespace" as well as any parameter changes
|
||||
* // to our own node ("this_node")
|
||||
* std::regex re("(/a_namespace/.*)|(/this_node)");
|
||||
* if (regex_match(event.node, re)) {
|
||||
* // Now that we know the event matches the regular expression we scanned for, we can
|
||||
* // use 'get_parameter_from_event' to get a specific parameter name that we're looking for
|
||||
* rclcpp::Parameter p;
|
||||
* if (rclcpp::ParameterEventsSubscriber::get_parameter_from_event(
|
||||
* event, p, remote_param_name, fqn))
|
||||
* {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_string().c_str());
|
||||
* }
|
||||
*
|
||||
* // You can also use 'get_parameter*s*_from_event' to enumerate all changes that came
|
||||
* // in on this event
|
||||
* auto params = rclcpp::ParameterEventsSubscriber::get_parameters_from_event(event);
|
||||
* for (auto & p : params) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.value_to_string().c_str());
|
||||
* }
|
||||
* ```cpp
|
||||
* auto cb3 =
|
||||
* [fqn, remote_param_name, &node](const rcl_interfaces::msg::ParameterEvent & event) {
|
||||
* // Look for any updates to parameters in "/a_namespace" as well as any parameter changes
|
||||
* // to our own node ("this_node")
|
||||
* std::regex re("(/a_namespace/.*)|(/this_node)");
|
||||
* if (regex_match(event.node, re)) {
|
||||
* // Now that we know the event matches the regular expression we scanned for, we can
|
||||
* // use 'get_parameter_from_event' to get a specific parameter name that we're looking for
|
||||
* rclcpp::Parameter p;
|
||||
* if (rclcpp::ParameterEventHandler::get_parameter_from_event(
|
||||
* event, p, remote_param_name, fqn))
|
||||
* {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_string().c_str());
|
||||
* }
|
||||
* };
|
||||
* auto handle3 = param_handler->add_parameter_event_callback(cb3);
|
||||
*
|
||||
* // You can also use 'get_parameter*s*_from_event' to enumerate all changes that came
|
||||
* // in on this event
|
||||
* auto params = rclcpp::ParameterEventHandler::get_parameters_from_event(event);
|
||||
* for (auto & p : params) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.value_to_string().c_str());
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
* auto handle3 = param_handler->add_parameter_event_callback(cb3);
|
||||
* ```
|
||||
*
|
||||
* For both parameter callbacks and parameter event callbacks, when multiple callbacks are added,
|
||||
* the callbacks are invoked last-in, first-called order (LIFO).
|
||||
@@ -160,7 +170,9 @@ struct ParameterEventCallbackHandle
|
||||
*
|
||||
* To remove a parameter event callback, reset the callback smart pointer or use:
|
||||
*
|
||||
* param_handler->remove_event_parameter_callback(handle3);
|
||||
* ```cpp
|
||||
* param_handler->remove_event_parameter_callback(handle3);
|
||||
* ```
|
||||
*/
|
||||
class ParameterEventHandler
|
||||
{
|
||||
|
||||
@@ -359,7 +359,7 @@ private:
|
||||
/// Return the value of a parameter as a string
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
to_string(const ParameterValue & type);
|
||||
to_string(const ParameterValue & value);
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ public:
|
||||
* the loaned message will be directly allocated in the middleware.
|
||||
* If not, the message allocator of this rclcpp::Publisher instance is being used.
|
||||
*
|
||||
* With a call to \sa `publish` the LoanedMessage instance is being returned to the middleware
|
||||
* With a call to `publish` the LoanedMessage instance is being returned to the middleware
|
||||
* or free'd accordingly to the allocator.
|
||||
* If the message is not being published but processed differently, the destructor of this
|
||||
* class will either return the message to the middleware or deallocate it via the internal
|
||||
|
||||
@@ -80,7 +80,8 @@ struct SubscriptionOptionsBase
|
||||
|
||||
// An optional QoS which can provide topic_statistics with a stable QoS separate from
|
||||
// the subscription's current QoS settings which could be unstable.
|
||||
rclcpp::QoS qos = SystemDefaultsQoS();
|
||||
// Explicitly set the enough depth to avoid missing the statistics messages.
|
||||
rclcpp::QoS qos = SystemDefaultsQoS().keep_last(10);
|
||||
};
|
||||
|
||||
TopicStatisticsOptions topic_stats_options;
|
||||
|
||||
@@ -172,12 +172,11 @@ private:
|
||||
{
|
||||
auto received_message_age = std::make_unique<ReceivedMessageAge>();
|
||||
received_message_age->Start();
|
||||
subscriber_statistics_collectors_.emplace_back(std::move(received_message_age));
|
||||
|
||||
auto received_message_period = std::make_unique<ReceivedMessagePeriod>();
|
||||
received_message_period->Start();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
subscriber_statistics_collectors_.emplace_back(std::move(received_message_age));
|
||||
subscriber_statistics_collectors_.emplace_back(std::move(received_message_period));
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
add_to_wait_set(rcl_wait_set_t & wait_set);
|
||||
add_to_wait_set(rcl_wait_set_t & wait_set) = 0;
|
||||
|
||||
/// Check if the Waitable is ready.
|
||||
/**
|
||||
@@ -124,7 +124,7 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
is_ready(const rcl_wait_set_t & wait_set);
|
||||
is_ready(const rcl_wait_set_t & wait_set) = 0;
|
||||
|
||||
/// Take the data so that it can be consumed with `execute`.
|
||||
/**
|
||||
@@ -176,7 +176,7 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::shared_ptr<void>
|
||||
take_data_by_entity_id(size_t id);
|
||||
take_data_by_entity_id(size_t id) = 0;
|
||||
|
||||
/// Execute data that is passed in.
|
||||
/**
|
||||
@@ -203,7 +203,7 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
execute(const std::shared_ptr<void> & data);
|
||||
execute(const std::shared_ptr<void> & data) = 0;
|
||||
|
||||
/// Exchange the "in use by wait set" state for this timer.
|
||||
/**
|
||||
@@ -246,7 +246,7 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
set_on_ready_callback(std::function<void(size_t, int)> callback);
|
||||
set_on_ready_callback(std::function<void(size_t, int)> callback) = 0;
|
||||
|
||||
/// Unset any callback registered via set_on_ready_callback.
|
||||
/**
|
||||
@@ -256,7 +256,7 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
clear_on_ready_callback();
|
||||
clear_on_ready_callback() = 0;
|
||||
|
||||
private:
|
||||
std::atomic<bool> in_use_by_wait_set_{false};
|
||||
|
||||
@@ -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>28.3.2</version>
|
||||
<version>29.2.0</version>
|
||||
<description>The ROS client library in C++.</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
|
||||
@@ -212,28 +212,27 @@ Context::init(
|
||||
}
|
||||
rcl_context_.reset(context, __delete_context);
|
||||
|
||||
if (init_options.auto_initialize_logging()) {
|
||||
logging_mutex_ = get_global_logging_mutex();
|
||||
std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
|
||||
size_t & count = get_logging_reference_count();
|
||||
if (0u == count) {
|
||||
ret = rcl_logging_configure_with_output_handler(
|
||||
&rcl_context_->global_arguments,
|
||||
rcl_init_options_get_allocator(init_options.get_rcl_init_options()),
|
||||
rclcpp_logging_output_handler);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rcl_context_.reset();
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to configure logging");
|
||||
}
|
||||
} else {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"logging was initialized more than once");
|
||||
}
|
||||
++count;
|
||||
}
|
||||
|
||||
try {
|
||||
if (init_options.auto_initialize_logging()) {
|
||||
logging_mutex_ = get_global_logging_mutex();
|
||||
std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
|
||||
size_t & count = get_logging_reference_count();
|
||||
if (0u == count) {
|
||||
ret = rcl_logging_configure_with_output_handler(
|
||||
&rcl_context_->global_arguments,
|
||||
rcl_init_options_get_allocator(init_options.get_rcl_init_options()),
|
||||
rclcpp_logging_output_handler);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to configure logging");
|
||||
}
|
||||
} else {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"logging was initialized more than once");
|
||||
}
|
||||
++count;
|
||||
}
|
||||
|
||||
std::vector<std::string> unparsed_ros_arguments = detail::get_unparsed_ros_arguments(
|
||||
argc, argv, &(rcl_context_->global_arguments), rcl_get_default_allocator());
|
||||
if (!unparsed_ros_arguments.empty()) {
|
||||
|
||||
49
rclcpp/src/rclcpp/create_generic_service.cpp
Normal file
49
rclcpp/src/rclcpp/create_generic_service.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2024 Sony Group Corporation.
|
||||
//
|
||||
// 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 <string>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/create_generic_service.hpp"
|
||||
#include "rclcpp/generic_service.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
rclcpp::GenericService::SharedPtr
|
||||
create_generic_service(
|
||||
std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
|
||||
std::shared_ptr<node_interfaces::NodeGraphInterface> node_graph,
|
||||
std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
|
||||
const std::string & service_name,
|
||||
const std::string & service_type,
|
||||
GenericServiceCallback any_callback,
|
||||
const rclcpp::QoS & qos,
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
rcl_service_options_t options = rcl_service_get_default_options();
|
||||
options.qos = qos.get_rmw_qos_profile();
|
||||
|
||||
auto srv = rclcpp::GenericService::make_shared(
|
||||
node_base.get(),
|
||||
node_graph,
|
||||
service_name,
|
||||
service_type,
|
||||
any_callback,
|
||||
options);
|
||||
|
||||
auto srv_base_ptr = std::dynamic_pointer_cast<rclcpp::ServiceBase>(srv);
|
||||
node_services->add_service(srv_base_ptr, group);
|
||||
return srv;
|
||||
}
|
||||
} // namespace rclcpp
|
||||
@@ -79,7 +79,6 @@ EventsExecutor::setup_notify_waitable()
|
||||
// ---> we need to wake up the executor so that it can terminate
|
||||
// - a node or callback group guard condition is triggered:
|
||||
// ---> the entities collection is changed, we need to update callbacks
|
||||
entities_need_rebuild_ = false;
|
||||
this->handle_updated_entities(false);
|
||||
});
|
||||
|
||||
@@ -168,6 +167,14 @@ EventsExecutor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhau
|
||||
return false;
|
||||
};
|
||||
|
||||
// If this spin is not exhaustive (e.g. spin_some), we need to explicitly check
|
||||
// if entities need to be rebuilt here rather than letting the notify waitable event do it.
|
||||
// A non-exhaustive spin would not check for work a second time, thus delaying the execution
|
||||
// of some entities to the next invocation of spin.
|
||||
if (!exhaustive) {
|
||||
this->handle_updated_entities(false);
|
||||
}
|
||||
|
||||
// Get the number of events and timers ready at start
|
||||
const size_t ready_events_at_start = events_queue_->size();
|
||||
size_t executed_events = 0;
|
||||
@@ -314,6 +321,17 @@ void
|
||||
EventsExecutor::handle_updated_entities(bool notify)
|
||||
{
|
||||
(void)notify;
|
||||
|
||||
// Do not rebuild if we don't need to.
|
||||
// A rebuild event could be generated, but then
|
||||
// this function could end up being called from somewhere else
|
||||
// before that event gets processed, for example if
|
||||
// a node or callback group is manually added to the executor.
|
||||
const bool notify_waitable_triggered = entities_need_rebuild_.exchange(false);
|
||||
if (!notify_waitable_triggered && !this->collector_.has_pending()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the new collection
|
||||
this->collector_.update_collections();
|
||||
auto callback_groups = this->collector_.get_all_callback_groups();
|
||||
@@ -341,6 +359,11 @@ EventsExecutor::refresh_current_collection(
|
||||
// Acquire lock before modifying the current collection
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
|
||||
// Remove expired entities to ensure re-initialized objects
|
||||
// are updated. This fixes issues with stale state entities.
|
||||
// See: https://github.com/ros2/rclcpp/pull/2586
|
||||
current_collection_.remove_expired_entities();
|
||||
|
||||
current_collection_.timers.update(
|
||||
new_collection.timers,
|
||||
[this](rclcpp::TimerBase::SharedPtr timer) {timers_manager_->add_timer(timer);},
|
||||
|
||||
@@ -31,22 +31,31 @@ GenericClient::GenericClient(
|
||||
rcl_client_options_t & client_options)
|
||||
: ClientBase(node_base, node_graph)
|
||||
{
|
||||
ts_lib_ = get_typesupport_library(
|
||||
service_type, "rosidl_typesupport_cpp");
|
||||
const rosidl_service_type_support_t * service_ts;
|
||||
try {
|
||||
ts_lib_ = get_typesupport_library(
|
||||
service_type, "rosidl_typesupport_cpp");
|
||||
|
||||
auto service_ts_ = get_service_typesupport_handle(
|
||||
service_type, "rosidl_typesupport_cpp", *ts_lib_);
|
||||
service_ts = get_service_typesupport_handle(
|
||||
service_type, "rosidl_typesupport_cpp", *ts_lib_);
|
||||
|
||||
auto response_type_support_intro = get_message_typesupport_handle(
|
||||
service_ts_->response_typesupport,
|
||||
rosidl_typesupport_introspection_cpp::typesupport_identifier);
|
||||
response_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
|
||||
response_type_support_intro->data);
|
||||
auto response_type_support_intro = get_message_typesupport_handle(
|
||||
service_ts->response_typesupport,
|
||||
rosidl_typesupport_introspection_cpp::typesupport_identifier);
|
||||
response_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
|
||||
response_type_support_intro->data);
|
||||
} catch (std::runtime_error & err) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_node_logger(node_handle_.get()).get_child("rclcpp"),
|
||||
"Invalid service type: %s",
|
||||
err.what());
|
||||
throw rclcpp::exceptions::InvalidServiceTypeError(err.what());
|
||||
}
|
||||
|
||||
rcl_ret_t ret = rcl_client_init(
|
||||
this->get_client_handle().get(),
|
||||
this->get_rcl_node_handle(),
|
||||
service_ts_,
|
||||
service_ts,
|
||||
service_name.c_str(),
|
||||
&client_options);
|
||||
if (ret != RCL_RET_OK) {
|
||||
@@ -100,6 +109,13 @@ GenericClient::handle_response(
|
||||
if (std::holds_alternative<Promise>(value)) {
|
||||
auto & promise = std::get<Promise>(value);
|
||||
promise.set_value(std::move(response));
|
||||
} else if (std::holds_alternative<CallbackTypeValueVariant>(value)) {
|
||||
auto & inner = std::get<CallbackTypeValueVariant>(value);
|
||||
const auto & callback = std::get<CallbackType>(inner);
|
||||
auto & promise = std::get<Promise>(inner);
|
||||
auto & future = std::get<SharedFuture>(inner);
|
||||
promise.set_value(std::move(response));
|
||||
callback(std::move(future));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +135,18 @@ GenericClient::remove_pending_request(int64_t request_id)
|
||||
return pending_requests_.erase(request_id) != 0u;
|
||||
}
|
||||
|
||||
bool
|
||||
GenericClient::remove_pending_request(const FutureAndRequestId & future)
|
||||
{
|
||||
return this->remove_pending_request(future.request_id);
|
||||
}
|
||||
|
||||
bool
|
||||
GenericClient::remove_pending_request(const SharedFutureAndRequestId & future)
|
||||
{
|
||||
return this->remove_pending_request(future.request_id);
|
||||
}
|
||||
|
||||
std::optional<GenericClient::CallbackInfoVariant>
|
||||
GenericClient::get_and_erase_pending_request(int64_t request_number)
|
||||
{
|
||||
|
||||
172
rclcpp/src/rclcpp/generic_service.cpp
Normal file
172
rclcpp/src/rclcpp/generic_service.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright 2024 Sony Group Corporation.
|
||||
//
|
||||
// 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/generic_service.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
GenericService::GenericService(
|
||||
std::shared_ptr<rcl_node_t> node_handle,
|
||||
const std::string & service_name,
|
||||
const std::string & service_type,
|
||||
GenericServiceCallback any_callback,
|
||||
rcl_service_options_t & service_options)
|
||||
: ServiceBase(node_handle),
|
||||
any_callback_(any_callback)
|
||||
{
|
||||
const rosidl_service_type_support_t * service_ts;
|
||||
try {
|
||||
ts_lib_ = get_typesupport_library(
|
||||
service_type, "rosidl_typesupport_cpp");
|
||||
|
||||
service_ts = get_service_typesupport_handle(
|
||||
service_type, "rosidl_typesupport_cpp", *ts_lib_);
|
||||
|
||||
auto request_type_support_intro = get_message_typesupport_handle(
|
||||
service_ts->request_typesupport,
|
||||
rosidl_typesupport_introspection_cpp::typesupport_identifier);
|
||||
request_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
|
||||
request_type_support_intro->data);
|
||||
|
||||
auto response_type_support_intro = get_message_typesupport_handle(
|
||||
service_ts->response_typesupport,
|
||||
rosidl_typesupport_introspection_cpp::typesupport_identifier);
|
||||
response_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
|
||||
response_type_support_intro->data);
|
||||
} catch (std::runtime_error & err) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_node_logger(node_handle_.get()).get_child("rclcpp"),
|
||||
"Invalid service type: %s",
|
||||
err.what());
|
||||
throw rclcpp::exceptions::InvalidServiceTypeError(err.what());
|
||||
}
|
||||
|
||||
// rcl does the static memory allocation here
|
||||
service_handle_ = std::shared_ptr<rcl_service_t>(
|
||||
new rcl_service_t, [handle = node_handle_, service_name](rcl_service_t * service)
|
||||
{
|
||||
if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
|
||||
"Error in destruction of rcl service handle: %s",
|
||||
rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
delete service;
|
||||
});
|
||||
*service_handle_.get() = rcl_get_zero_initialized_service();
|
||||
|
||||
rcl_ret_t ret = rcl_service_init(
|
||||
service_handle_.get(),
|
||||
node_handle.get(),
|
||||
service_ts,
|
||||
service_name.c_str(),
|
||||
&service_options);
|
||||
if (ret != RCL_RET_OK) {
|
||||
if (ret == RCL_RET_SERVICE_NAME_INVALID) {
|
||||
auto rcl_node_handle = get_rcl_node_handle();
|
||||
// this will throw on any validation problem
|
||||
rcl_reset_error();
|
||||
expand_topic_or_service_name(
|
||||
service_name,
|
||||
rcl_node_get_name(rcl_node_handle),
|
||||
rcl_node_get_namespace(rcl_node_handle),
|
||||
true);
|
||||
}
|
||||
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create service");
|
||||
}
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_service_callback_added,
|
||||
static_cast<const void *>(get_service_handle().get()),
|
||||
static_cast<const void *>(&any_callback_));
|
||||
#ifndef TRACETOOLS_DISABLED
|
||||
any_callback_.register_callback_for_tracing();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
GenericService::take_request(
|
||||
SharedRequest request_out,
|
||||
rmw_request_id_t & request_id_out)
|
||||
{
|
||||
request_out = create_request();
|
||||
return this->take_type_erased_request(request_out.get(), request_id_out);
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
GenericService::create_request()
|
||||
{
|
||||
Request request = new uint8_t[request_members_->size_of_];
|
||||
request_members_->init_function(request, rosidl_runtime_cpp::MessageInitialization::ZERO);
|
||||
return std::shared_ptr<void>(
|
||||
request,
|
||||
[this](void * p)
|
||||
{
|
||||
request_members_->fini_function(p);
|
||||
delete[] reinterpret_cast<uint8_t *>(p);
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
GenericService::create_response()
|
||||
{
|
||||
Response response = new uint8_t[response_members_->size_of_];
|
||||
response_members_->init_function(response, rosidl_runtime_cpp::MessageInitialization::ZERO);
|
||||
return std::shared_ptr<void>(
|
||||
response,
|
||||
[this](void * p)
|
||||
{
|
||||
response_members_->fini_function(p);
|
||||
delete[] reinterpret_cast<uint8_t *>(p);
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<rmw_request_id_t>
|
||||
GenericService::create_request_header()
|
||||
{
|
||||
return std::make_shared<rmw_request_id_t>();
|
||||
}
|
||||
|
||||
void
|
||||
GenericService::handle_request(
|
||||
std::shared_ptr<rmw_request_id_t> request_header,
|
||||
std::shared_ptr<void> request)
|
||||
{
|
||||
auto response = any_callback_.dispatch(
|
||||
this->shared_from_this(), request_header, request, create_response());
|
||||
if (response) {
|
||||
send_response(*request_header, response);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GenericService::send_response(rmw_request_id_t & req_id, SharedResponse & response)
|
||||
{
|
||||
rcl_ret_t ret = rcl_send_response(get_service_handle().get(), &req_id, response.get());
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
@@ -80,6 +80,7 @@ NodeOptions::operator=(const NodeOptions & other)
|
||||
this->clock_type_ = other.clock_type_;
|
||||
this->clock_qos_ = other.clock_qos_;
|
||||
this->use_clock_thread_ = other.use_clock_thread_;
|
||||
this->enable_logger_service_ = other.enable_logger_service_;
|
||||
this->parameter_event_qos_ = other.parameter_event_qos_;
|
||||
this->rosout_qos_ = other.rosout_qos_;
|
||||
this->parameter_event_publisher_options_ = other.parameter_event_publisher_options_;
|
||||
|
||||
@@ -135,15 +135,28 @@ void
|
||||
PublisherBase::bind_event_callbacks(
|
||||
const PublisherEventCallbacks & event_callbacks, bool use_default_callbacks)
|
||||
{
|
||||
if (event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.deadline_callback,
|
||||
RCL_PUBLISHER_OFFERED_DEADLINE_MISSED);
|
||||
try {
|
||||
if (event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.deadline_callback,
|
||||
RCL_PUBLISHER_OFFERED_DEADLINE_MISSED);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for deadline; not supported");
|
||||
}
|
||||
if (event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.liveliness_callback,
|
||||
RCL_PUBLISHER_LIVELINESS_LOST);
|
||||
|
||||
try {
|
||||
if (event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.liveliness_callback,
|
||||
RCL_PUBLISHER_LIVELINESS_LOST);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for liveliness; not supported");
|
||||
}
|
||||
|
||||
QOSOfferedIncompatibleQoSCallbackType incompatible_qos_cb;
|
||||
@@ -160,9 +173,9 @@ PublisherBase::bind_event_callbacks(
|
||||
this->add_event_handler(incompatible_qos_cb, RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_DEBUG(
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for incompatible qos; wrong callback type");
|
||||
"Failed to add event handler for incompatible qos; not supported");
|
||||
}
|
||||
|
||||
IncompatibleTypeCallbackType incompatible_type_cb;
|
||||
@@ -179,14 +192,21 @@ PublisherBase::bind_event_callbacks(
|
||||
this->add_event_handler(incompatible_type_cb, RCL_PUBLISHER_INCOMPATIBLE_TYPE);
|
||||
}
|
||||
} catch (UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_DEBUG(
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for incompatible type; wrong callback type");
|
||||
"Failed to add event handler for incompatible type; not supported");
|
||||
}
|
||||
if (event_callbacks.matched_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.matched_callback,
|
||||
RCL_PUBLISHER_MATCHED);
|
||||
|
||||
try {
|
||||
if (event_callbacks.matched_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.matched_callback,
|
||||
RCL_PUBLISHER_MATCHED);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for matched; not supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -112,16 +112,28 @@ void
|
||||
SubscriptionBase::bind_event_callbacks(
|
||||
const SubscriptionEventCallbacks & event_callbacks, bool use_default_callbacks)
|
||||
{
|
||||
if (event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.deadline_callback,
|
||||
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
|
||||
try {
|
||||
if (event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.deadline_callback,
|
||||
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for deadline; not supported");
|
||||
}
|
||||
|
||||
if (event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.liveliness_callback,
|
||||
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
|
||||
try {
|
||||
if (event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.liveliness_callback,
|
||||
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for liveliness; not supported");
|
||||
}
|
||||
|
||||
QOSRequestedIncompatibleQoSCallbackType incompatible_qos_cb;
|
||||
@@ -139,7 +151,9 @@ SubscriptionBase::bind_event_callbacks(
|
||||
this->add_event_handler(incompatible_qos_cb, RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
// pass
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for incompatible qos; not supported");
|
||||
}
|
||||
|
||||
IncompatibleTypeCallbackType incompatible_type_cb;
|
||||
@@ -156,18 +170,33 @@ SubscriptionBase::bind_event_callbacks(
|
||||
this->add_event_handler(incompatible_type_cb, RCL_SUBSCRIPTION_INCOMPATIBLE_TYPE);
|
||||
}
|
||||
} catch (UnsupportedEventTypeException & /*exc*/) {
|
||||
// pass
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for incompatible type; not supported");
|
||||
}
|
||||
|
||||
if (event_callbacks.message_lost_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.message_lost_callback,
|
||||
RCL_SUBSCRIPTION_MESSAGE_LOST);
|
||||
try {
|
||||
if (event_callbacks.message_lost_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.message_lost_callback,
|
||||
RCL_SUBSCRIPTION_MESSAGE_LOST);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for message lost; not supported");
|
||||
}
|
||||
if (event_callbacks.matched_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.matched_callback,
|
||||
RCL_SUBSCRIPTION_MATCHED);
|
||||
|
||||
try {
|
||||
if (event_callbacks.matched_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.matched_callback,
|
||||
RCL_SUBSCRIPTION_MATCHED);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for matched; not supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,54 +54,8 @@ Waitable::get_number_of_ready_guard_conditions()
|
||||
return 0u;
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
Waitable::take_data_by_entity_id(size_t id)
|
||||
{
|
||||
(void)id;
|
||||
throw std::runtime_error(
|
||||
"Custom waitables should override take_data_by_entity_id "
|
||||
"if they want to use it.");
|
||||
}
|
||||
|
||||
bool
|
||||
Waitable::exchange_in_use_by_wait_set_state(bool in_use_state)
|
||||
{
|
||||
return in_use_by_wait_set_.exchange(in_use_state);
|
||||
}
|
||||
|
||||
void
|
||||
Waitable::set_on_ready_callback(std::function<void(size_t, int)> callback)
|
||||
{
|
||||
(void)callback;
|
||||
|
||||
throw std::runtime_error(
|
||||
"Custom waitables should override set_on_ready_callback "
|
||||
"if they want to use it.");
|
||||
}
|
||||
|
||||
void
|
||||
Waitable::clear_on_ready_callback()
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Custom waitables should override clear_on_ready_callback if they "
|
||||
"want to use it and make sure to call it on the waitable destructor.");
|
||||
}
|
||||
|
||||
bool
|
||||
Waitable::is_ready(const rcl_wait_set_t & wait_set)
|
||||
{
|
||||
return this->is_ready(wait_set);
|
||||
}
|
||||
|
||||
void
|
||||
Waitable::add_to_wait_set(rcl_wait_set_t & wait_set)
|
||||
{
|
||||
this->add_to_wait_set(wait_set);
|
||||
}
|
||||
|
||||
void
|
||||
Waitable::execute(const std::shared_ptr<void> & data)
|
||||
{
|
||||
// note this const cast is only required to support a deprecated function
|
||||
this->execute(const_cast<std::shared_ptr<void> &>(data));
|
||||
}
|
||||
|
||||
@@ -189,71 +189,6 @@ BENCHMARK_F(PerformanceTestExecutorSimple, multi_thread_executor_remove_node)(be
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
static_single_thread_executor_add_node)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
executor.add_node(node);
|
||||
st.PauseTiming();
|
||||
executor.remove_node(node);
|
||||
st.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
static_single_thread_executor_remove_node)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
st.PauseTiming();
|
||||
executor.add_node(node);
|
||||
st.ResumeTiming();
|
||||
executor.remove_node(node);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
static_single_thread_executor_spin_until_future_complete)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
// test success of an immediately finishing future
|
||||
std::promise<bool> promise;
|
||||
std::future<bool> future = promise.get_future();
|
||||
promise.set_value(true);
|
||||
auto shared_future = future.share();
|
||||
|
||||
auto ret = executor.spin_until_future_complete(shared_future, 100ms);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
}
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
// static_single_thread_executor has a special design. We need to add/remove the node each
|
||||
// time you call spin
|
||||
st.PauseTiming();
|
||||
executor.add_node(node);
|
||||
st.ResumeTiming();
|
||||
|
||||
ret = executor.spin_until_future_complete(shared_future, 100ms);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
break;
|
||||
}
|
||||
st.PauseTiming();
|
||||
executor.remove_node(node);
|
||||
st.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
single_thread_executor_spin_node_until_future_complete)(benchmark::State & st)
|
||||
@@ -314,30 +249,6 @@ BENCHMARK_F(
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
static_single_thread_executor_spin_node_until_future_complete)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
// test success of an immediately finishing future
|
||||
std::promise<bool> promise;
|
||||
std::future<bool> future = promise.get_future();
|
||||
promise.set_value(true);
|
||||
auto shared_future = future.share();
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
auto ret = rclcpp::executors::spin_node_until_future_complete(
|
||||
executor, node, shared_future, 1s);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(PerformanceTestExecutorSimple, spin_until_future_complete)(benchmark::State & st)
|
||||
{
|
||||
// test success of an immediately finishing future
|
||||
|
||||
@@ -83,6 +83,18 @@ if(TARGET test_generic_client)
|
||||
${test_msgs_TARGETS}
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_generic_service test_generic_service.cpp)
|
||||
ament_add_test_label(test_generic_service mimick)
|
||||
if(TARGET test_generic_service)
|
||||
target_link_libraries(test_generic_service ${PROJECT_NAME}
|
||||
mimick
|
||||
${rcl_interfaces_TARGETS}
|
||||
rmw::rmw
|
||||
rosidl_runtime_cpp::rosidl_runtime_cpp
|
||||
rosidl_typesupport_cpp::rosidl_typesupport_cpp
|
||||
${test_msgs_TARGETS}
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_client_common test_client_common.cpp)
|
||||
ament_add_test_label(test_client_common mimick)
|
||||
if(TARGET test_client_common)
|
||||
@@ -99,17 +111,6 @@ ament_add_gtest(test_create_subscription test_create_subscription.cpp)
|
||||
if(TARGET test_create_subscription)
|
||||
target_link_libraries(test_create_subscription ${PROJECT_NAME} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
function(test_add_callback_groups_to_executor_for_rmw_implementation)
|
||||
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
|
||||
ament_add_gmock(test_add_callback_groups_to_executor${target_suffix} test_add_callback_groups_to_executor.cpp
|
||||
ENV ${rmw_implementation_env_var}
|
||||
TIMEOUT 120
|
||||
)
|
||||
if(TARGET test_add_callback_groups_to_executor${target_suffix})
|
||||
target_link_libraries(test_add_callback_groups_to_executor${target_suffix} ${PROJECT_NAME} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
endfunction()
|
||||
call_for_each_rmw_implementation(test_add_callback_groups_to_executor_for_rmw_implementation)
|
||||
ament_add_gtest(test_expand_topic_or_service_name test_expand_topic_or_service_name.cpp)
|
||||
ament_add_test_label(test_expand_topic_or_service_name mimick)
|
||||
if(TARGET test_expand_topic_or_service_name)
|
||||
@@ -337,28 +338,6 @@ if(TARGET test_qos)
|
||||
rmw::rmw
|
||||
)
|
||||
endif()
|
||||
function(test_generic_pubsub_for_rmw_implementation)
|
||||
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
|
||||
ament_add_gmock(test_generic_pubsub${target_suffix} test_generic_pubsub.cpp
|
||||
ENV ${rmw_implementation_env_var}
|
||||
)
|
||||
if(TARGET test_generic_pubsub${target_suffix})
|
||||
target_link_libraries(test_generic_pubsub${target_suffix} ${PROJECT_NAME} rcl::rcl ${test_msgs_TARGETS})
|
||||
endif()
|
||||
endfunction()
|
||||
call_for_each_rmw_implementation(test_generic_pubsub_for_rmw_implementation)
|
||||
|
||||
function(test_qos_event_for_rmw_implementation)
|
||||
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
|
||||
ament_add_gmock(test_qos_event${target_suffix} test_qos_event.cpp
|
||||
ENV ${rmw_implementation_env_var}
|
||||
)
|
||||
ament_add_test_label(test_qos_event${target_suffix} mimick)
|
||||
if(TARGET test_qos_event${target_suffix})
|
||||
target_link_libraries(test_qos_event${target_suffix} ${PROJECT_NAME} mimick rcutils::rcutils rmw::rmw ${test_msgs_TARGETS})
|
||||
endif()
|
||||
endfunction()
|
||||
call_for_each_rmw_implementation(test_qos_event_for_rmw_implementation)
|
||||
|
||||
ament_add_gmock(test_qos_overriding_options test_qos_overriding_options.cpp)
|
||||
if(TARGET test_qos_overriding_options)
|
||||
@@ -483,6 +462,13 @@ if(TARGET test_interface_traits)
|
||||
target_link_libraries(test_interface_traits ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_reinitialized_timers
|
||||
executors/test_reinitialized_timers.cpp
|
||||
TIMEOUT 30)
|
||||
if(TARGET test_reinitialized_timers)
|
||||
target_link_libraries(test_reinitialized_timers ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(
|
||||
test_executors
|
||||
executors/test_executors.cpp
|
||||
@@ -501,7 +487,7 @@ ament_add_gtest(
|
||||
executors/test_executors_timer_cancel_behavior.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
TIMEOUT 180)
|
||||
if(TARGET test_executors)
|
||||
if(TARGET test_executors_timer_cancel_behavior)
|
||||
target_link_libraries(test_executors_timer_cancel_behavior ${PROJECT_NAME} ${rosgraph_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
@@ -510,7 +496,7 @@ ament_add_gtest(
|
||||
executors/test_executors_callback_group_behavior.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
TIMEOUT 180)
|
||||
if(TARGET test_executors)
|
||||
if(TARGET test_executors_callback_group_behavior)
|
||||
target_link_libraries(test_executors_callback_group_behavior ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
@@ -519,7 +505,7 @@ ament_add_gtest(
|
||||
executors/test_executors_intraprocess.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
TIMEOUT 180)
|
||||
if(TARGET test_executors)
|
||||
if(TARGET test_executors_intraprocess)
|
||||
target_link_libraries(test_executors_intraprocess ${PROJECT_NAME} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
@@ -529,10 +515,20 @@ ament_add_gtest(
|
||||
executors/test_waitable.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
TIMEOUT 180)
|
||||
if(TARGET test_executors)
|
||||
if(TARGET test_executors_busy_waiting)
|
||||
target_link_libraries(test_executors_busy_waiting ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(
|
||||
test_executors_warmup
|
||||
executors/test_executors_warmup.cpp
|
||||
executors/test_waitable.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
TIMEOUT 180)
|
||||
if(TARGET test_executors_warmup)
|
||||
target_link_libraries(test_executors_warmup ${PROJECT_NAME} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_static_single_threaded_executor executors/test_static_single_threaded_executor.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
ament_add_test_label(test_static_single_threaded_executor mimick)
|
||||
@@ -559,7 +555,7 @@ if(TARGET test_executor_notify_waitable)
|
||||
target_link_libraries(test_executor_notify_waitable ${PROJECT_NAME} mimick rcpputils::rcpputils)
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_events_executor executors/test_events_executor.cpp TIMEOUT 5)
|
||||
ament_add_gtest(test_events_executor executors/test_events_executor.cpp TIMEOUT 60)
|
||||
if(TARGET test_events_executor)
|
||||
target_link_libraries(test_events_executor ${PROJECT_NAME} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
@@ -650,16 +646,51 @@ if(TARGET test_graph_listener)
|
||||
target_link_libraries(test_graph_listener ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
|
||||
function(test_subscription_content_filter_for_rmw_implementation)
|
||||
ament_add_gmock_executable(test_qos_event test_qos_event.cpp)
|
||||
if(TARGET test_qos_event)
|
||||
target_link_libraries(test_qos_event ${PROJECT_NAME} mimick rcutils::rcutils rmw::rmw ${test_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
ament_add_gmock_executable(test_generic_pubsub test_generic_pubsub.cpp)
|
||||
if(TARGET test_generic_pubsub)
|
||||
target_link_libraries(test_generic_pubsub ${PROJECT_NAME} rcl::rcl ${test_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
ament_add_gmock_executable(test_add_callback_groups_to_executor test_add_callback_groups_to_executor.cpp)
|
||||
if(TARGET test_add_callback_groups_to_executor)
|
||||
target_link_libraries(test_add_callback_groups_to_executor ${PROJECT_NAME} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
ament_add_gmock_executable(test_subscription_content_filter test_subscription_content_filter.cpp)
|
||||
if(TARGET test_subscription_content_filter)
|
||||
target_link_libraries(test_subscription_content_filter ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
function(test_on_all_rmws)
|
||||
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
|
||||
ament_add_gmock(test_subscription_content_filter${target_suffix}
|
||||
test_subscription_content_filter.cpp
|
||||
|
||||
ament_add_gmock_test(test_qos_event
|
||||
TEST_NAME test_qos_event${target_suffix}
|
||||
ENV ${rmw_implementation_env_var}
|
||||
)
|
||||
ament_add_test_label(test_qos_event${target_suffix} mimick)
|
||||
|
||||
ament_add_gmock_test(test_generic_pubsub
|
||||
TEST_NAME test_generic_pubsub${target_suffix}
|
||||
ENV ${rmw_implementation_env_var}
|
||||
)
|
||||
|
||||
ament_add_gmock_test(test_add_callback_groups_to_executor
|
||||
TEST_NAME test_add_callback_groups_to_executor${target_suffix}
|
||||
ENV ${rmw_implementation_env_var}
|
||||
TIMEOUT 120
|
||||
)
|
||||
|
||||
ament_add_gmock_test(test_subscription_content_filter
|
||||
TEST_NAME test_subscription_content_filter${target_suffix}
|
||||
ENV ${rmw_implementation_env_var}
|
||||
TIMEOUT 120
|
||||
)
|
||||
ament_add_test_label(test_subscription_content_filter${target_suffix} mimick)
|
||||
if(TARGET test_subscription_content_filter${target_suffix})
|
||||
target_link_libraries(test_subscription_content_filter${target_suffix} ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
|
||||
endif()
|
||||
endfunction()
|
||||
call_for_each_rmw_implementation(test_subscription_content_filter_for_rmw_implementation)
|
||||
call_for_each_rmw_implementation(test_on_all_rmws)
|
||||
|
||||
@@ -25,12 +25,37 @@
|
||||
#include "rclcpp/executors/static_single_threaded_executor.hpp"
|
||||
#include "rclcpp/executors/multi_threaded_executor.hpp"
|
||||
|
||||
// suppress deprecated StaticSingleThreadedExecutor warning
|
||||
// we define an alias that explicitly indicates that this class is deprecated, while avoiding
|
||||
// polluting a lot of files the gcc pragmas
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
using DeprecatedStaticSingleThreadedExecutor = rclcpp::executors::StaticSingleThreadedExecutor;
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
using ExecutorTypes =
|
||||
::testing::Types<
|
||||
rclcpp::executors::SingleThreadedExecutor,
|
||||
rclcpp::executors::MultiThreadedExecutor,
|
||||
rclcpp::executors::StaticSingleThreadedExecutor,
|
||||
DeprecatedStaticSingleThreadedExecutor,
|
||||
rclcpp::experimental::executors::EventsExecutor>;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
class ExecutorTypeNames
|
||||
{
|
||||
@@ -46,10 +71,16 @@ public:
|
||||
if (std::is_same<T, rclcpp::executors::MultiThreadedExecutor>()) {
|
||||
return "MultiThreadedExecutor";
|
||||
}
|
||||
|
||||
if (std::is_same<T, rclcpp::executors::StaticSingleThreadedExecutor>()) {
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
if (std::is_same<T, DeprecatedStaticSingleThreadedExecutor>()) {
|
||||
return "StaticSingleThreadedExecutor";
|
||||
}
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
if (std::is_same<T, rclcpp::experimental::executors::EventsExecutor>()) {
|
||||
return "EventsExecutor";
|
||||
|
||||
@@ -479,14 +479,21 @@ TEST_F(TestEventsExecutor, test_default_incompatible_qos_callbacks)
|
||||
const auto timeout = std::chrono::seconds(10);
|
||||
ex.spin_until_future_complete(log_msgs_future, timeout);
|
||||
|
||||
EXPECT_EQ(
|
||||
"New subscription discovered on topic '/test_topic', requesting incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
pub_log_msg);
|
||||
EXPECT_EQ(
|
||||
"New publisher discovered on topic '/test_topic', offering incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
sub_log_msg);
|
||||
rclcpp::QoSCheckCompatibleResult qos_compatible = rclcpp::qos_check_compatible(
|
||||
publisher->get_actual_qos(), subscription->get_actual_qos());
|
||||
if (qos_compatible.compatibility == rclcpp::QoSCompatibility::Error) {
|
||||
EXPECT_EQ(
|
||||
"New subscription discovered on topic '/test_topic', requesting incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
pub_log_msg);
|
||||
EXPECT_EQ(
|
||||
"New publisher discovered on topic '/test_topic', offering incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
sub_log_msg);
|
||||
} else {
|
||||
EXPECT_EQ("", pub_log_msg);
|
||||
EXPECT_EQ("", sub_log_msg);
|
||||
}
|
||||
|
||||
rcutils_logging_set_output_handler(original_output_handler);
|
||||
}
|
||||
|
||||
@@ -480,20 +480,14 @@ TYPED_TEST(TestExecutors, spin_some)
|
||||
|
||||
// The purpose of this test is to check that the ExecutorT.spin_some() method:
|
||||
// - does not continue executing after max_duration has elapsed
|
||||
TYPED_TEST(TestExecutors, spin_some_max_duration)
|
||||
// TODO(wjwwood): The `StaticSingleThreadedExecutor`
|
||||
// do not properly implement max_duration (it seems), so disable this test
|
||||
// for them in the meantime.
|
||||
// see: https://github.com/ros2/rclcpp/issues/2462
|
||||
TYPED_TEST(TestExecutorsStable, spin_some_max_duration)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
|
||||
// TODO(wjwwood): The `StaticSingleThreadedExecutor`
|
||||
// do not properly implement max_duration (it seems), so disable this test
|
||||
// for them in the meantime.
|
||||
// see: https://github.com/ros2/rclcpp/issues/2462
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::executors::StaticSingleThreadedExecutor>())
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
// Use an isolated callback group to avoid interference from any housekeeping
|
||||
// items that may be in the default callback group of the node.
|
||||
constexpr bool automatically_add_to_executor_with_node = false;
|
||||
@@ -647,13 +641,6 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted)
|
||||
TYPED_TEST(TestExecutors, testRaceConditionAddNode)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
// Spawn some threads to do some heavy work
|
||||
std::atomic<bool> should_cancel = false;
|
||||
@@ -674,20 +661,20 @@ TYPED_TEST(TestExecutors, testRaceConditionAddNode)
|
||||
}
|
||||
|
||||
// Create an executor
|
||||
auto executor = std::make_shared<ExecutorType>();
|
||||
ExecutorType executor;
|
||||
// Start spinning
|
||||
auto executor_thread = std::thread(
|
||||
[executor]() {
|
||||
executor->spin();
|
||||
[&executor]() {
|
||||
executor.spin();
|
||||
});
|
||||
// Add a node to the executor
|
||||
executor->add_node(this->node);
|
||||
executor.add_node(this->node);
|
||||
|
||||
// Cancel the executor (make sure that it's already spinning first)
|
||||
while (!executor->is_spinning() && rclcpp::ok()) {
|
||||
while (!executor.is_spinning() && rclcpp::ok()) {
|
||||
continue;
|
||||
}
|
||||
executor->cancel();
|
||||
executor.cancel();
|
||||
|
||||
// Try to join the thread after cancelling the executor
|
||||
// This is the "test". We want to make sure that we can still cancel the executor
|
||||
|
||||
@@ -47,9 +47,6 @@ public:
|
||||
auto waitable_interfaces = node->get_node_waitables_interface();
|
||||
waitable = std::make_shared<TestWaitable>();
|
||||
waitable_interfaces->add_waitable(waitable, callback_group);
|
||||
|
||||
executor = std::make_shared<T>();
|
||||
executor->add_callback_group(callback_group, node->get_node_base_interface());
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
@@ -108,7 +105,6 @@ public:
|
||||
rclcpp::CallbackGroup::SharedPtr callback_group;
|
||||
std::shared_ptr<TestWaitable> waitable;
|
||||
std::chrono::steady_clock::time_point start_time;
|
||||
std::shared_ptr<T> executor;
|
||||
bool has_executed;
|
||||
};
|
||||
|
||||
@@ -116,10 +112,16 @@ TYPED_TEST_SUITE(TestBusyWaiting, ExecutorTypes, ExecutorTypeNames);
|
||||
|
||||
TYPED_TEST(TestBusyWaiting, test_spin_all)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
executor.add_callback_group(
|
||||
this->callback_group,
|
||||
this->node->get_node_base_interface());
|
||||
|
||||
this->set_up_and_trigger_waitable();
|
||||
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
this->executor->spin_all(this->max_duration);
|
||||
executor.spin_all(this->max_duration);
|
||||
this->check_for_busy_waits(start_time);
|
||||
// this should get the initial trigger, and the follow up from in the callback
|
||||
ASSERT_EQ(this->waitable->get_count(), 2u);
|
||||
@@ -127,10 +129,16 @@ TYPED_TEST(TestBusyWaiting, test_spin_all)
|
||||
|
||||
TYPED_TEST(TestBusyWaiting, test_spin_some)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
executor.add_callback_group(
|
||||
this->callback_group,
|
||||
this->node->get_node_base_interface());
|
||||
|
||||
this->set_up_and_trigger_waitable();
|
||||
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
this->executor->spin_some(this->max_duration);
|
||||
executor.spin_some(this->max_duration);
|
||||
this->check_for_busy_waits(start_time);
|
||||
// this should get the inital trigger, but not the follow up in the callback
|
||||
ASSERT_EQ(this->waitable->get_count(), 1u);
|
||||
@@ -138,6 +146,12 @@ TYPED_TEST(TestBusyWaiting, test_spin_some)
|
||||
|
||||
TYPED_TEST(TestBusyWaiting, test_spin)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
executor.add_callback_group(
|
||||
this->callback_group,
|
||||
this->node->get_node_base_interface());
|
||||
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
bool first_check_passed = false;
|
||||
@@ -151,8 +165,8 @@ TYPED_TEST(TestBusyWaiting, test_spin)
|
||||
});
|
||||
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
std::thread t([this]() {
|
||||
this->executor->spin();
|
||||
std::thread t([&executor]() {
|
||||
executor.spin();
|
||||
});
|
||||
|
||||
// wait until thread has started (first execute of waitable)
|
||||
@@ -172,7 +186,7 @@ TYPED_TEST(TestBusyWaiting, test_spin)
|
||||
}
|
||||
EXPECT_EQ(this->waitable->get_count(), 2u);
|
||||
|
||||
this->executor->cancel();
|
||||
executor.cancel();
|
||||
t.join();
|
||||
|
||||
this->check_for_busy_waits(start_time);
|
||||
|
||||
@@ -282,12 +282,20 @@ public:
|
||||
T executor;
|
||||
};
|
||||
|
||||
#if !defined(_WIN32)
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
# endif
|
||||
#endif
|
||||
using MainExecutorTypes =
|
||||
::testing::Types<
|
||||
rclcpp::executors::SingleThreadedExecutor,
|
||||
rclcpp::executors::MultiThreadedExecutor,
|
||||
rclcpp::executors::StaticSingleThreadedExecutor>;
|
||||
|
||||
DeprecatedStaticSingleThreadedExecutor>;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
// TODO(@fujitatomoya): this test excludes EventExecutor because it does not
|
||||
// support simulation time used for this test to relax the racy condition.
|
||||
// See more details for https://github.com/ros2/rclcpp/issues/2457.
|
||||
|
||||
244
rclcpp/test/rclcpp/executors/test_executors_warmup.cpp
Normal file
244
rclcpp/test/rclcpp/executors/test_executors_warmup.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
/**
|
||||
* This test checks all implementations of rclcpp::executor to check they pass they basic API
|
||||
* tests. Anything specific to any executor in particular should go in a separate test file.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
|
||||
#include "./executor_types.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
template<typename T>
|
||||
class TestExecutorsWarmup : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE(TestExecutorsWarmup, ExecutorTypes, ExecutorTypeNames);
|
||||
|
||||
// This test verifies that spin_all is correctly collecting work multiple times
|
||||
// even when one of the items of work is a notifier waitable event and thus results in
|
||||
// rebuilding the entities collection.
|
||||
// When spin_all goes back to collect more work, it should see the ready items from
|
||||
// the new added entities
|
||||
TYPED_TEST(TestExecutorsWarmup, spin_all_doesnt_require_warmup)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
|
||||
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
|
||||
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
|
||||
|
||||
// Add node to the executor before creating the entities
|
||||
executor.add_node(node);
|
||||
|
||||
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
|
||||
// the entities collection
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
|
||||
size_t callback_count = 0;
|
||||
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
|
||||
auto subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>(
|
||||
"test_topic", rclcpp::QoS(10), std::move(callback));
|
||||
|
||||
ASSERT_EQ(callback_count, 0u);
|
||||
|
||||
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
|
||||
publisher->publish(test_msgs::msg::Empty());
|
||||
|
||||
// We need to select a duration that is greater than
|
||||
// the time taken to refresh the entities collection and rebuild the waitset.
|
||||
// spin-all is expected to process the notifier waitable event, rebuild the collection,
|
||||
// and then collect more work, finding the subscription message event.
|
||||
// This duration has been selected empirically.
|
||||
executor.spin_all(std::chrono::milliseconds(500));
|
||||
|
||||
// Verify that the callback is called as part of the spin above
|
||||
EXPECT_EQ(callback_count, 1u);
|
||||
}
|
||||
|
||||
// Same test as `spin_all_doesnt_require_warmup`, but uses a callback group
|
||||
// This test reproduces the bug reported by https://github.com/ros2/rclcpp/issues/2589
|
||||
TYPED_TEST(TestExecutorsWarmup, spin_all_doesnt_require_warmup_with_cbgroup)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
|
||||
// TODO(alsora): Enable when https://github.com/ros2/rclcpp/pull/2595 gets merged
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::executors::SingleThreadedExecutor>() ||
|
||||
std::is_same<ExecutorType, rclcpp::executors::MultiThreadedExecutor>())
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
|
||||
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
|
||||
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
|
||||
|
||||
auto callback_group = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive,
|
||||
false);
|
||||
|
||||
// Add callback group to the executor before creating the entities
|
||||
executor.add_callback_group(callback_group, node->get_node_base_interface());
|
||||
|
||||
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
|
||||
// the entities collection
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
|
||||
size_t callback_count = 0;
|
||||
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
|
||||
rclcpp::SubscriptionOptions sub_options;
|
||||
sub_options.callback_group = callback_group;
|
||||
auto subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>(
|
||||
"test_topic", rclcpp::QoS(10), std::move(callback), sub_options);
|
||||
|
||||
ASSERT_EQ(callback_count, 0u);
|
||||
|
||||
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
|
||||
publisher->publish(test_msgs::msg::Empty());
|
||||
|
||||
// We need to select a duration that is greater than
|
||||
// the time taken to refresh the entities collection and rebuild the waitset.
|
||||
// spin-all is expected to process the notifier waitable event, rebuild the collection,
|
||||
// and then collect more work, finding the subscription message event.
|
||||
// This duration has been selected empirically.
|
||||
executor.spin_all(std::chrono::milliseconds(500));
|
||||
|
||||
// Verify that the callback is called as part of the spin above
|
||||
EXPECT_EQ(callback_count, 1u);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutorsWarmup, spin_some_doesnt_require_warmup)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
|
||||
// TODO(alsora): currently only the events-executor passes this test.
|
||||
// Enable single-threaded and multi-threaded executors
|
||||
// when https://github.com/ros2/rclcpp/pull/2595 gets merged
|
||||
if (
|
||||
!std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>())
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
|
||||
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
|
||||
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
|
||||
|
||||
// Add node to the executor before creating the entities
|
||||
executor.add_node(node);
|
||||
|
||||
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
|
||||
// the entities collection
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
|
||||
size_t callback_count = 0;
|
||||
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
|
||||
auto subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>(
|
||||
"test_topic", rclcpp::QoS(10), std::move(callback));
|
||||
|
||||
ASSERT_EQ(callback_count, 0u);
|
||||
|
||||
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
|
||||
publisher->publish(test_msgs::msg::Empty());
|
||||
|
||||
// NOTE: intra-process communication is enabled, so the subscription will immediately see
|
||||
// the new message, no risk of race conditions where spin_some gets called before the
|
||||
// message has been delivered.
|
||||
executor.spin_some();
|
||||
|
||||
// Verify that the callback is called as part of the spin above
|
||||
EXPECT_EQ(callback_count, 1u);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutorsWarmup, spin_some_doesnt_require_warmup_with_cbgroup)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
|
||||
// TODO(alsora): currently only the events-executor passes this test.
|
||||
// Enable single-threaded and multi-threaded executors
|
||||
// when https://github.com/ros2/rclcpp/pull/2595 gets merged
|
||||
if (
|
||||
!std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>())
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
|
||||
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
|
||||
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
|
||||
|
||||
auto callback_group = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive,
|
||||
false);
|
||||
|
||||
// Add callback group to the executor before creating the entities
|
||||
executor.add_callback_group(callback_group, node->get_node_base_interface());
|
||||
|
||||
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
|
||||
// the entities collection
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
|
||||
size_t callback_count = 0;
|
||||
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
|
||||
rclcpp::SubscriptionOptions sub_options;
|
||||
sub_options.callback_group = callback_group;
|
||||
auto subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>(
|
||||
"test_topic", rclcpp::QoS(10), std::move(callback), sub_options);
|
||||
|
||||
ASSERT_EQ(callback_count, 0u);
|
||||
|
||||
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
|
||||
publisher->publish(test_msgs::msg::Empty());
|
||||
|
||||
// NOTE: intra-process communication is enabled, so the subscription will immediately see
|
||||
// the new message, no risk of race conditions where spin_some gets called before the
|
||||
// message has been delivered.
|
||||
executor.spin_some();
|
||||
|
||||
// Verify that the callback is called as part of the spin above
|
||||
EXPECT_EQ(callback_count, 1u);
|
||||
}
|
||||
@@ -32,6 +32,11 @@ protected:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
constexpr std::chrono::milliseconds PERIOD_MS = 1000ms;
|
||||
|
||||
88
rclcpp/test/rclcpp/executors/test_reinitialized_timers.cpp
Normal file
88
rclcpp/test/rclcpp/executors/test_reinitialized_timers.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2024 iRobot Corporation.
|
||||
//
|
||||
// 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 <chrono>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "./executor_types.hpp"
|
||||
|
||||
template<typename ExecutorType>
|
||||
class TestTimersLifecycle : public testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp() override {rclcpp::init(0, nullptr);}
|
||||
|
||||
void TearDown() override {rclcpp::shutdown();}
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE(TestTimersLifecycle, ExecutorTypes, ExecutorTypeNames);
|
||||
|
||||
TYPED_TEST(TestTimersLifecycle, timers_lifecycle_reinitialized_object)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
|
||||
auto timers_period = std::chrono::milliseconds(50);
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node");
|
||||
|
||||
executor.add_node(node);
|
||||
|
||||
size_t count_1 = 0;
|
||||
auto timer_1 = rclcpp::create_timer(
|
||||
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_1]() {count_1++;});
|
||||
|
||||
size_t count_2 = 0;
|
||||
auto timer_2 = rclcpp::create_timer(
|
||||
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_2]() {count_2++;});
|
||||
|
||||
{
|
||||
std::thread executor_thread([&executor]() {executor.spin();});
|
||||
|
||||
while (count_2 < 10u) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
executor.cancel();
|
||||
executor_thread.join();
|
||||
|
||||
EXPECT_GE(count_2, 10u);
|
||||
EXPECT_LE(count_2 - count_1, 1u);
|
||||
}
|
||||
|
||||
count_1 = 0;
|
||||
timer_1 = rclcpp::create_timer(
|
||||
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_1]() {count_1++;});
|
||||
|
||||
count_2 = 0;
|
||||
timer_2 = rclcpp::create_timer(
|
||||
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_2]() {count_2++;});
|
||||
|
||||
{
|
||||
std::thread executor_thread([&executor]() {executor.spin();});
|
||||
|
||||
while (count_2 < 10u) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
executor.cancel();
|
||||
executor_thread.join();
|
||||
|
||||
EXPECT_GE(count_2, 10u);
|
||||
EXPECT_LE(count_2 - count_1, 1u);
|
||||
}
|
||||
}
|
||||
@@ -20,12 +20,13 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/executors.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "rclcpp/executors.hpp"
|
||||
|
||||
#include "test_msgs/srv/empty.hpp"
|
||||
|
||||
#include "./executor_types.hpp"
|
||||
#include "../../mocking_utils/patch.hpp"
|
||||
#include "../../utils/rclcpp_gtest_macros.hpp"
|
||||
|
||||
@@ -46,7 +47,15 @@ public:
|
||||
};
|
||||
|
||||
TEST_F(TestStaticSingleThreadedExecutor, add_callback_group_trigger_guard_failed) {
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
DeprecatedStaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
@@ -61,7 +70,15 @@ TEST_F(TestStaticSingleThreadedExecutor, add_callback_group_trigger_guard_failed
|
||||
}
|
||||
|
||||
TEST_F(TestStaticSingleThreadedExecutor, add_node_trigger_guard_failed) {
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
DeprecatedStaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
|
||||
{
|
||||
@@ -74,7 +91,15 @@ TEST_F(TestStaticSingleThreadedExecutor, add_node_trigger_guard_failed) {
|
||||
}
|
||||
|
||||
TEST_F(TestStaticSingleThreadedExecutor, remove_callback_group_trigger_guard_failed) {
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
DeprecatedStaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
@@ -92,7 +117,15 @@ TEST_F(TestStaticSingleThreadedExecutor, remove_callback_group_trigger_guard_fai
|
||||
}
|
||||
|
||||
TEST_F(TestStaticSingleThreadedExecutor, remove_node_failed) {
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
DeprecatedStaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
|
||||
{
|
||||
@@ -105,7 +138,15 @@ TEST_F(TestStaticSingleThreadedExecutor, remove_node_failed) {
|
||||
}
|
||||
|
||||
TEST_F(TestStaticSingleThreadedExecutor, remove_node_trigger_guard_failed) {
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
DeprecatedStaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
|
||||
executor.add_node(node);
|
||||
@@ -120,7 +161,15 @@ TEST_F(TestStaticSingleThreadedExecutor, remove_node_trigger_guard_failed) {
|
||||
}
|
||||
|
||||
TEST_F(TestStaticSingleThreadedExecutor, execute_service) {
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
DeprecatedStaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
executor.add_node(node);
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ protected:
|
||||
{
|
||||
node.reset();
|
||||
wrapped_node.reset();
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
static rclcpp::Node::SharedPtr node;
|
||||
|
||||
@@ -47,9 +47,13 @@ constexpr char absolute_namespace[] = "/ns";
|
||||
class TestNodeGraph : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp()
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
node_ = std::make_shared<rclcpp::Node>(node_name, node_namespace);
|
||||
|
||||
// This dynamic cast is not necessary for the unittests, but instead is used to ensure
|
||||
@@ -59,7 +63,7 @@ public:
|
||||
ASSERT_NE(nullptr, node_graph_);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
@@ -31,13 +31,13 @@ public:
|
||||
void add_to_wait_set(rcl_wait_set_t &) override {}
|
||||
bool is_ready(const rcl_wait_set_t &) override {return false;}
|
||||
|
||||
std::shared_ptr<void>
|
||||
take_data() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> take_data() override {return nullptr;}
|
||||
void execute(const std::shared_ptr<void> &) override {}
|
||||
|
||||
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
|
||||
void clear_on_ready_callback() override {}
|
||||
|
||||
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
|
||||
};
|
||||
|
||||
class TestNodeWaitables : public ::testing::Test
|
||||
|
||||
@@ -51,13 +51,13 @@ public:
|
||||
return test_waitable_result;
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
take_data() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> take_data() override {return nullptr;}
|
||||
void execute(const std::shared_ptr<void> &) override {}
|
||||
|
||||
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
|
||||
void clear_on_ready_callback() override {}
|
||||
|
||||
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
|
||||
};
|
||||
|
||||
struct RclWaitSetSizes
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#include "rclcpp/executor.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "./executors/executor_types.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
template<typename T>
|
||||
@@ -48,48 +50,8 @@ public:
|
||||
template<typename T>
|
||||
class TestAddCallbackGroupsToExecutorStable : public TestAddCallbackGroupsToExecutor<T> {};
|
||||
|
||||
using ExecutorTypes =
|
||||
::testing::Types<
|
||||
rclcpp::executors::SingleThreadedExecutor,
|
||||
rclcpp::executors::MultiThreadedExecutor,
|
||||
rclcpp::executors::StaticSingleThreadedExecutor,
|
||||
rclcpp::experimental::executors::EventsExecutor>;
|
||||
|
||||
class ExecutorTypeNames
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
static std::string GetName(int idx)
|
||||
{
|
||||
(void)idx;
|
||||
if (std::is_same<T, rclcpp::executors::SingleThreadedExecutor>()) {
|
||||
return "SingleThreadedExecutor";
|
||||
}
|
||||
|
||||
if (std::is_same<T, rclcpp::executors::MultiThreadedExecutor>()) {
|
||||
return "MultiThreadedExecutor";
|
||||
}
|
||||
|
||||
if (std::is_same<T, rclcpp::executors::StaticSingleThreadedExecutor>()) {
|
||||
return "StaticSingleThreadedExecutor";
|
||||
}
|
||||
|
||||
if (std::is_same<T, rclcpp::experimental::executors::EventsExecutor>()) {
|
||||
return "EventsExecutor";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutor, ExecutorTypes, ExecutorTypeNames);
|
||||
|
||||
// StaticSingleThreadedExecutor is not included in these tests for now
|
||||
using StandardExecutors =
|
||||
::testing::Types<
|
||||
rclcpp::executors::SingleThreadedExecutor,
|
||||
rclcpp::executors::MultiThreadedExecutor,
|
||||
rclcpp::experimental::executors::EventsExecutor>;
|
||||
TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutorStable, StandardExecutors, ExecutorTypeNames);
|
||||
|
||||
/*
|
||||
|
||||
@@ -34,6 +34,11 @@ protected:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
|
||||
@@ -27,6 +27,11 @@ protected:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestFindWeakNodes, allocator_strategy_with_weak_nodes) {
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
@@ -28,6 +30,7 @@
|
||||
#include "../mocking_utils/patch.hpp"
|
||||
|
||||
#include "test_msgs/srv/empty.hpp"
|
||||
#include "test_msgs/srv/basic_types.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
@@ -105,7 +108,7 @@ TEST_F(TestGenericClient, construction_and_destruction) {
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto client = node->create_generic_client("test_service", "test_msgs/srv/InvalidType");
|
||||
}, std::runtime_error);
|
||||
}, rclcpp::exceptions::InvalidServiceTypeError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,3 +231,69 @@ TEST_F(TestGenericClientSub, construction_and_destruction) {
|
||||
}, rclcpp::exceptions::InvalidServiceNameError);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestGenericClientSub, async_send_request_with_request) {
|
||||
const std::string service_name = "test_service";
|
||||
int64_t expected_change = 1111;
|
||||
|
||||
auto client = node->create_generic_client(service_name, "test_msgs/srv/BasicTypes");
|
||||
|
||||
auto callback = [&expected_change](
|
||||
const test_msgs::srv::BasicTypes::Request::SharedPtr request,
|
||||
test_msgs::srv::BasicTypes::Response::SharedPtr response) {
|
||||
response->int64_value = request->int64_value + expected_change;
|
||||
};
|
||||
|
||||
auto service =
|
||||
node->create_service<test_msgs::srv::BasicTypes>(service_name, std::move(callback));
|
||||
|
||||
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(5)));
|
||||
ASSERT_TRUE(client->service_is_ready());
|
||||
|
||||
test_msgs::srv::BasicTypes::Request request;
|
||||
request.int64_value = 12345678;
|
||||
|
||||
auto future = client->async_send_request(static_cast<void *>(&request));
|
||||
rclcpp::spin_until_future_complete(
|
||||
node->get_node_base_interface(), future, std::chrono::seconds(5));
|
||||
ASSERT_TRUE(future.valid());
|
||||
auto get_untyped_response = future.get();
|
||||
auto typed_response =
|
||||
static_cast<test_msgs::srv::BasicTypes::Response *>(get_untyped_response.get());
|
||||
EXPECT_EQ(typed_response->int64_value, (request.int64_value + expected_change));
|
||||
}
|
||||
|
||||
TEST_F(TestGenericClientSub, async_send_request_with_request_and_callback) {
|
||||
const std::string service_name = "test_service";
|
||||
int64_t expected_change = 2222;
|
||||
|
||||
auto client = node->create_generic_client(service_name, "test_msgs/srv/BasicTypes");
|
||||
|
||||
auto server_callback = [&expected_change](
|
||||
const test_msgs::srv::BasicTypes::Request::SharedPtr request,
|
||||
test_msgs::srv::BasicTypes::Response::SharedPtr response) {
|
||||
response->int64_value = request->int64_value + expected_change;
|
||||
};
|
||||
|
||||
auto service =
|
||||
node->create_service<test_msgs::srv::BasicTypes>(service_name, std::move(server_callback));
|
||||
|
||||
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(5)));
|
||||
ASSERT_TRUE(client->service_is_ready());
|
||||
|
||||
test_msgs::srv::BasicTypes::Request request;
|
||||
request.int64_value = 12345678;
|
||||
|
||||
auto client_callback = [&request, &expected_change](
|
||||
rclcpp::GenericClient::SharedFuture future) {
|
||||
auto untyped_response = future.get();
|
||||
auto typed_response =
|
||||
static_cast<test_msgs::srv::BasicTypes::Response *>(untyped_response.get());
|
||||
EXPECT_EQ(typed_response->int64_value, (request.int64_value + expected_change));
|
||||
};
|
||||
|
||||
auto future =
|
||||
client->async_send_request(static_cast<void *>(&request), client_callback);
|
||||
rclcpp::spin_until_future_complete(
|
||||
node->get_node_base_interface(), future, std::chrono::seconds(5));
|
||||
}
|
||||
|
||||
422
rclcpp/test/rclcpp/test_generic_service.cpp
Normal file
422
rclcpp/test/rclcpp/test_generic_service.cpp
Normal file
@@ -0,0 +1,422 @@
|
||||
// Copyright 2024 Sony Group Corporation.
|
||||
//
|
||||
// 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 <chrono>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "../mocking_utils/patch.hpp"
|
||||
#include "../utils/rclcpp_gtest_macros.hpp"
|
||||
|
||||
#include "rcl_interfaces/srv/list_parameters.hpp"
|
||||
#include "test_msgs/srv/empty.hpp"
|
||||
#include "test_msgs/srv/basic_types.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
class TestGenericService : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("test_node", "/ns");
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
class TestGenericServiceSub : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("test_node", "/ns");
|
||||
subnode = node->create_sub_node("sub_ns");
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
rclcpp::Node::SharedPtr subnode;
|
||||
};
|
||||
|
||||
/*
|
||||
Testing service construction and destruction.
|
||||
*/
|
||||
TEST_F(TestGenericService, construction_and_destruction) {
|
||||
auto callback = [](
|
||||
rclcpp::GenericService::SharedRequest,
|
||||
rclcpp::GenericService::SharedResponse) {};
|
||||
{
|
||||
auto generic_service = node->create_generic_service(
|
||||
"test_generic_service", "rcl_interfaces/srv/ListParameters", callback);
|
||||
EXPECT_NE(nullptr, generic_service->get_service_handle());
|
||||
const rclcpp::ServiceBase * const_service_base = generic_service.get();
|
||||
EXPECT_NE(nullptr, const_service_base->get_service_handle());
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto generic_service = node->create_generic_service(
|
||||
"invalid_service?", "test_msgs/srv/Empty", callback);
|
||||
}, rclcpp::exceptions::InvalidServiceNameError);
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto generic_service = node->create_generic_service(
|
||||
"test_generic_service", "test_msgs/srv/NotExist", callback);
|
||||
}, rclcpp::exceptions::InvalidServiceTypeError);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing service construction and destruction for subnodes.
|
||||
*/
|
||||
TEST_F(TestGenericServiceSub, construction_and_destruction) {
|
||||
auto callback = [](
|
||||
rclcpp::GenericService::SharedRequest,
|
||||
rclcpp::GenericService::SharedResponse) {};
|
||||
{
|
||||
auto generic_service = subnode->create_generic_service(
|
||||
"test_generic_service", "rcl_interfaces/srv/ListParameters", callback);
|
||||
EXPECT_STREQ(generic_service->get_service_name(), "/ns/sub_ns/test_generic_service");
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto generic_service = subnode->create_generic_service(
|
||||
"invalid_service?", "test_msgs/srv/Empty", callback);
|
||||
}, rclcpp::exceptions::InvalidServiceNameError);
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto generic_service = subnode->create_generic_service(
|
||||
"test_generic_service", "test_msgs/srv/NotExist", callback);
|
||||
}, rclcpp::exceptions::InvalidServiceTypeError);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, construction_and_destruction_rcl_errors) {
|
||||
auto callback = [](
|
||||
rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
|
||||
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_service_init, RCL_RET_ERROR);
|
||||
// reset() isn't necessary for this exception, it just avoids unused return value warning
|
||||
EXPECT_THROW(
|
||||
node->create_generic_service("service", "test_msgs/srv/Empty", callback).reset(),
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
{
|
||||
// reset() is required for this one
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_service_fini, RCL_RET_ERROR);
|
||||
EXPECT_NO_THROW(
|
||||
node->create_generic_service("service", "test_msgs/srv/Empty", callback).reset());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, generic_service_take_request) {
|
||||
auto callback = [](
|
||||
rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
|
||||
auto generic_service =
|
||||
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
|
||||
{
|
||||
auto request_id = generic_service->create_request_header();
|
||||
auto request = generic_service->create_request();
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_take_request, RCL_RET_OK);
|
||||
EXPECT_TRUE(generic_service->take_request(request, *request_id.get()));
|
||||
}
|
||||
{
|
||||
auto request_id = generic_service->create_request_header();
|
||||
auto request = generic_service->create_request();
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_take_request, RCL_RET_SERVICE_TAKE_FAILED);
|
||||
EXPECT_FALSE(generic_service->take_request(request, *request_id.get()));
|
||||
}
|
||||
{
|
||||
auto request_id = generic_service->create_request_header();
|
||||
auto request = generic_service->create_request();
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_take_request, RCL_RET_ERROR);
|
||||
EXPECT_THROW(
|
||||
generic_service->take_request(request, *request_id.get()), rclcpp::exceptions::RCLError);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, generic_service_send_response) {
|
||||
auto callback = [](
|
||||
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
|
||||
auto generic_service =
|
||||
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
|
||||
|
||||
{
|
||||
auto request_id = generic_service->create_request_header();
|
||||
auto response = generic_service->create_response();
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_send_response, RCL_RET_OK);
|
||||
EXPECT_NO_THROW(generic_service->send_response(*request_id.get(), response));
|
||||
}
|
||||
|
||||
{
|
||||
auto request_id = generic_service->create_request_header();
|
||||
auto response = generic_service->create_response();
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_send_response, RCL_RET_ERROR);
|
||||
EXPECT_THROW(
|
||||
generic_service->send_response(*request_id.get(), response),
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing on_new_request callbacks.
|
||||
*/
|
||||
TEST_F(TestGenericService, generic_service_on_new_request_callback) {
|
||||
auto server_callback = [](
|
||||
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {FAIL();};
|
||||
rclcpp::ServicesQoS service_qos;
|
||||
service_qos.keep_last(3);
|
||||
auto generic_service = node->create_generic_service(
|
||||
"~/test_service", "test_msgs/srv/Empty", server_callback, service_qos);
|
||||
|
||||
std::atomic<size_t> c1 {0};
|
||||
auto increase_c1_cb = [&c1](size_t count_msgs) {c1 += count_msgs;};
|
||||
generic_service->set_on_new_request_callback(increase_c1_cb);
|
||||
|
||||
auto client = node->create_client<test_msgs::srv::Empty>(
|
||||
"~/test_service", service_qos);
|
||||
{
|
||||
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
|
||||
client->async_send_request(request);
|
||||
}
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
do {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
} while (c1 == 0 && std::chrono::steady_clock::now() - start < 10s);
|
||||
|
||||
EXPECT_EQ(c1.load(), 1u);
|
||||
|
||||
std::atomic<size_t> c2 {0};
|
||||
auto increase_c2_cb = [&c2](size_t count_msgs) {c2 += count_msgs;};
|
||||
generic_service->set_on_new_request_callback(increase_c2_cb);
|
||||
|
||||
{
|
||||
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
|
||||
client->async_send_request(request);
|
||||
}
|
||||
|
||||
start = std::chrono::steady_clock::now();
|
||||
do {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
} while (c2 == 0 && std::chrono::steady_clock::now() - start < 10s);
|
||||
|
||||
EXPECT_EQ(c1.load(), 1u);
|
||||
EXPECT_EQ(c2.load(), 1u);
|
||||
|
||||
generic_service->clear_on_new_request_callback();
|
||||
|
||||
{
|
||||
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
|
||||
client->async_send_request(request);
|
||||
client->async_send_request(request);
|
||||
client->async_send_request(request);
|
||||
}
|
||||
|
||||
std::atomic<size_t> c3 {0};
|
||||
auto increase_c3_cb = [&c3](size_t count_msgs) {c3 += count_msgs;};
|
||||
generic_service->set_on_new_request_callback(increase_c3_cb);
|
||||
|
||||
start = std::chrono::steady_clock::now();
|
||||
do {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
} while (c3 < 3 && std::chrono::steady_clock::now() - start < 10s);
|
||||
|
||||
EXPECT_EQ(c1.load(), 1u);
|
||||
EXPECT_EQ(c2.load(), 1u);
|
||||
EXPECT_EQ(c3.load(), 3u);
|
||||
|
||||
std::function<void(size_t)> invalid_cb = nullptr;
|
||||
EXPECT_THROW(generic_service->set_on_new_request_callback(invalid_cb), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, rcl_service_response_publisher_get_actual_qos_error) {
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_service_response_publisher_get_actual_qos, nullptr);
|
||||
auto callback = [](
|
||||
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
|
||||
auto generic_service =
|
||||
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
generic_service->get_response_publisher_actual_qos(),
|
||||
std::runtime_error("failed to get service's response publisher qos settings: error not set"));
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, rcl_service_request_subscription_get_actual_qos_error) {
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_service_request_subscription_get_actual_qos, nullptr);
|
||||
auto callback = [](
|
||||
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
|
||||
auto generic_service =
|
||||
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
generic_service->get_request_subscription_actual_qos(),
|
||||
std::runtime_error("failed to get service's request subscription qos settings: error not set"));
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, generic_service_qos) {
|
||||
rclcpp::ServicesQoS qos_profile;
|
||||
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
|
||||
rclcpp::Duration duration(std::chrono::nanoseconds(1));
|
||||
qos_profile.deadline(duration);
|
||||
qos_profile.lifespan(duration);
|
||||
qos_profile.liveliness_lease_duration(duration);
|
||||
|
||||
auto callback = [](
|
||||
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
|
||||
auto generic_service =
|
||||
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback, qos_profile);
|
||||
|
||||
auto rs_qos = generic_service->get_request_subscription_actual_qos();
|
||||
auto rp_qos = generic_service->get_response_publisher_actual_qos();
|
||||
|
||||
EXPECT_EQ(qos_profile, rp_qos);
|
||||
// Lifespan has no meaning for subscription/readers
|
||||
rs_qos.lifespan(qos_profile.lifespan());
|
||||
EXPECT_EQ(qos_profile, rs_qos);
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, generic_service_qos_depth) {
|
||||
uint64_t server_cb_count_ = 0;
|
||||
auto server_callback = [&](
|
||||
const rclcpp::GenericService::SharedRequest,
|
||||
rclcpp::GenericService::SharedResponse) {server_cb_count_++;};
|
||||
|
||||
auto server_node = std::make_shared<rclcpp::Node>("server_node", "/ns");
|
||||
|
||||
rclcpp::QoS server_qos_profile(2);
|
||||
|
||||
auto generic_service = server_node->create_generic_service(
|
||||
"test_qos_depth", "test_msgs/srv/Empty", std::move(server_callback), server_qos_profile);
|
||||
|
||||
rclcpp::QoS client_qos_profile(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_default));
|
||||
auto client = node->create_client<test_msgs::srv::Empty>("test_qos_depth", client_qos_profile);
|
||||
|
||||
::testing::AssertionResult request_result = ::testing::AssertionSuccess();
|
||||
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
|
||||
|
||||
auto client_callback = [&request_result](
|
||||
rclcpp::Client<test_msgs::srv::Empty>::SharedFuture future_response) {
|
||||
if (nullptr == future_response.get()) {
|
||||
request_result = ::testing::AssertionFailure() << "Future response was null";
|
||||
}
|
||||
};
|
||||
|
||||
uint64_t client_requests = 5;
|
||||
for (uint64_t i = 0; i < client_requests; i++) {
|
||||
client->async_send_request(request, client_callback);
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while ((server_cb_count_ < server_qos_profile.depth()) &&
|
||||
(std::chrono::steady_clock::now() - start) < 1s)
|
||||
{
|
||||
rclcpp::spin_some(server_node);
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
// Spin an extra time to check if server QoS depth has been ignored,
|
||||
// so more server responses might be processed than expected.
|
||||
rclcpp::spin_some(server_node);
|
||||
|
||||
EXPECT_EQ(server_cb_count_, server_qos_profile.depth());
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, generic_service_and_client) {
|
||||
const std::string service_name = "test_service";
|
||||
const std::string service_type = "test_msgs/srv/BasicTypes";
|
||||
int64_t expected_change = 87654321;
|
||||
|
||||
auto callback = [&expected_change](
|
||||
const rclcpp::GenericService::SharedRequest request,
|
||||
rclcpp::GenericService::SharedResponse response) {
|
||||
auto typed_request = static_cast<test_msgs::srv::BasicTypes_Request *>(request.get());
|
||||
auto typed_response = static_cast<test_msgs::srv::BasicTypes_Response *>(response.get());
|
||||
|
||||
typed_response->int64_value = typed_request->int64_value + expected_change;
|
||||
};
|
||||
auto generic_service = node->create_generic_service(service_name, service_type, callback);
|
||||
|
||||
auto client = node->create_client<test_msgs::srv::BasicTypes>(service_name);
|
||||
|
||||
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(5)));
|
||||
ASSERT_TRUE(client->service_is_ready());
|
||||
|
||||
auto request = std::make_shared<test_msgs::srv::BasicTypes::Request>();
|
||||
request->int64_value = 12345678;
|
||||
|
||||
auto generic_client_callback = [&request, &expected_change](
|
||||
std::shared_future<test_msgs::srv::BasicTypes_Response::SharedPtr> future) {
|
||||
auto response = future.get();
|
||||
EXPECT_EQ(response->int64_value, (request->int64_value + expected_change));
|
||||
};
|
||||
|
||||
auto future =
|
||||
client->async_send_request(request, generic_client_callback);
|
||||
rclcpp::spin_until_future_complete(
|
||||
node->get_node_base_interface(), future, std::chrono::seconds(5));
|
||||
}
|
||||
@@ -27,6 +27,11 @@ protected:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -123,6 +123,7 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
// need to store the messages somewhere otherwise the memory address will be reused
|
||||
ConstMessageSharedPtr shared_msg;
|
||||
MessageUniquePtr unique_msg;
|
||||
@@ -158,9 +159,9 @@ class PublisherBase
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
|
||||
|
||||
explicit PublisherBase(rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: qos_profile(qos),
|
||||
topic_name("topic")
|
||||
explicit PublisherBase(const std::string & topic, const rclcpp::QoS & qos)
|
||||
: topic_name(topic),
|
||||
qos_profile(qos)
|
||||
{}
|
||||
|
||||
virtual ~PublisherBase()
|
||||
@@ -205,10 +206,12 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
rclcpp::QoS qos_profile;
|
||||
std::string topic_name;
|
||||
uint64_t intra_process_publisher_id_;
|
||||
IntraProcessManagerWeakPtr weak_ipm_;
|
||||
|
||||
private:
|
||||
std::string topic_name;
|
||||
rclcpp::QoS qos_profile;
|
||||
};
|
||||
|
||||
template<typename T, typename Alloc = std::allocator<void>>
|
||||
@@ -223,8 +226,8 @@ public:
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<T, Alloc>)
|
||||
|
||||
explicit Publisher(rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: PublisherBase(qos)
|
||||
explicit Publisher(const std::string & topic, const rclcpp::QoS & qos)
|
||||
: PublisherBase(topic, qos)
|
||||
{
|
||||
auto allocator = std::make_shared<Alloc>();
|
||||
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
|
||||
@@ -258,9 +261,9 @@ public:
|
||||
|
||||
explicit SubscriptionIntraProcessBase(
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::string & topic = "topic",
|
||||
rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: qos_profile(qos), topic_name(topic)
|
||||
const std::string & topic,
|
||||
const rclcpp::QoS & qos)
|
||||
: topic_name(topic), qos_profile(qos)
|
||||
{
|
||||
(void)context;
|
||||
}
|
||||
@@ -292,8 +295,8 @@ public:
|
||||
size_t
|
||||
available_capacity() const = 0;
|
||||
|
||||
rclcpp::QoS qos_profile;
|
||||
std::string topic_name;
|
||||
rclcpp::QoS qos_profile;
|
||||
};
|
||||
|
||||
template<
|
||||
@@ -307,8 +310,8 @@ class SubscriptionIntraProcessBuffer : public SubscriptionIntraProcessBase
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcessBuffer)
|
||||
|
||||
explicit SubscriptionIntraProcessBuffer(rclcpp::QoS qos)
|
||||
: SubscriptionIntraProcessBase(nullptr, "topic", qos), take_shared_method(false)
|
||||
explicit SubscriptionIntraProcessBuffer(const std::string & topic, const rclcpp::QoS & qos)
|
||||
: SubscriptionIntraProcessBase(nullptr, topic, qos), take_shared_method(false)
|
||||
{
|
||||
buffer = std::make_unique<rclcpp::experimental::buffers::mock::IntraProcessBuffer<MessageT>>();
|
||||
}
|
||||
@@ -375,8 +378,8 @@ class SubscriptionIntraProcess : public SubscriptionIntraProcessBuffer<
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
|
||||
|
||||
explicit SubscriptionIntraProcess(rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(qos)
|
||||
explicit SubscriptionIntraProcess(const std::string & topic, const rclcpp::QoS & qos)
|
||||
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(topic, qos)
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -466,12 +469,11 @@ TEST(TestIntraProcessManager, add_pub_sub) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
|
||||
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10).best_effort());
|
||||
|
||||
auto p2 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
|
||||
p2->topic_name = "different_topic_name";
|
||||
auto p2 = std::make_shared<PublisherT>("different_topic_name", rclcpp::QoS(10).best_effort());
|
||||
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).best_effort());
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10).best_effort());
|
||||
|
||||
auto p1_id = ipm->add_publisher(p1);
|
||||
auto p2_id = ipm->add_publisher(p2);
|
||||
@@ -480,24 +482,42 @@ TEST(TestIntraProcessManager, add_pub_sub) {
|
||||
bool unique_ids = p1_id != p2_id && p2_id != s1_id;
|
||||
ASSERT_TRUE(unique_ids);
|
||||
|
||||
// p1 has 1 subcription, s1
|
||||
size_t p1_subs = ipm->get_subscription_count(p1_id);
|
||||
// p2 has 0 subscriptions
|
||||
size_t p2_subs = ipm->get_subscription_count(p2_id);
|
||||
// Non-existent publisher_id has 0 subscriptions
|
||||
size_t non_existing_pub_subs = ipm->get_subscription_count(42);
|
||||
ASSERT_EQ(1u, p1_subs);
|
||||
ASSERT_EQ(0u, p2_subs);
|
||||
ASSERT_EQ(0u, non_existing_pub_subs);
|
||||
|
||||
auto p3 = std::make_shared<PublisherT>(rclcpp::QoS(10).reliable());
|
||||
auto p3 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10).reliable());
|
||||
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).reliable());
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10).reliable());
|
||||
|
||||
// s2 may be able to communicate with p1 depending on the RMW
|
||||
auto s2_id = ipm->template add_subscription<MessageT>(s2);
|
||||
// p3 can definitely communicate with s2, may be able to communicate with s1 depending on the RMW
|
||||
auto p3_id = ipm->add_publisher(p3);
|
||||
|
||||
// p1 definitely matches subscription s1, since the topic name and QoS match exactly.
|
||||
// If the RMW can match best-effort publishers to reliable subscriptions (like Zenoh can),
|
||||
// then p1 will also match s2.
|
||||
p1_subs = ipm->get_subscription_count(p1_id);
|
||||
// No subscriptions with a topic name of "different_topic_name" were added.
|
||||
p2_subs = ipm->get_subscription_count(p2_id);
|
||||
// On all current RMWs (DDS and Zenoh), a reliable publisher like p3 can communicate with both
|
||||
// reliable and best-effort subscriptions (s1 and s2).
|
||||
size_t p3_subs = ipm->get_subscription_count(p3_id);
|
||||
ASSERT_EQ(1u, p1_subs);
|
||||
|
||||
rclcpp::QoSCheckCompatibleResult qos_compatible =
|
||||
rclcpp::qos_check_compatible(p1->get_actual_qos(), s2->get_actual_qos());
|
||||
if (qos_compatible.compatibility == rclcpp::QoSCompatibility::Error) {
|
||||
ASSERT_EQ(1u, p1_subs);
|
||||
} else {
|
||||
ASSERT_EQ(2u, p1_subs);
|
||||
}
|
||||
ASSERT_EQ(0u, p2_subs);
|
||||
ASSERT_EQ(2u, p3_subs);
|
||||
|
||||
@@ -528,11 +548,11 @@ TEST(TestIntraProcessManager, single_subscription) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>();
|
||||
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10));
|
||||
auto p1_id = ipm->add_publisher(p1);
|
||||
p1->set_intra_process_manager(p1_id, ipm);
|
||||
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s1->take_shared_method = false;
|
||||
auto s1_id = ipm->template add_subscription<MessageT>(s1);
|
||||
|
||||
@@ -543,7 +563,7 @@ TEST(TestIntraProcessManager, single_subscription) {
|
||||
ASSERT_EQ(original_message_pointer, received_message_pointer_1);
|
||||
|
||||
ipm->remove_subscription(s1_id);
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s2->take_shared_method = true;
|
||||
auto s2_id = ipm->template add_subscription<MessageT>(s2);
|
||||
(void)s2_id;
|
||||
@@ -582,15 +602,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>();
|
||||
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10));
|
||||
auto p1_id = ipm->add_publisher(p1);
|
||||
p1->set_intra_process_manager(p1_id, ipm);
|
||||
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s1->take_shared_method = false;
|
||||
auto s1_id = ipm->template add_subscription<MessageT>(s1);
|
||||
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s2->take_shared_method = false;
|
||||
auto s2_id = ipm->template add_subscription<MessageT>(s2);
|
||||
|
||||
@@ -606,11 +626,11 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
|
||||
ipm->remove_subscription(s1_id);
|
||||
ipm->remove_subscription(s2_id);
|
||||
|
||||
auto s3 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s3 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s3->take_shared_method = true;
|
||||
auto s3_id = ipm->template add_subscription<MessageT>(s3);
|
||||
|
||||
auto s4 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s4 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s4->take_shared_method = true;
|
||||
auto s4_id = ipm->template add_subscription<MessageT>(s4);
|
||||
|
||||
@@ -625,11 +645,11 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
|
||||
ipm->remove_subscription(s3_id);
|
||||
ipm->remove_subscription(s4_id);
|
||||
|
||||
auto s5 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s5 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s5->take_shared_method = false;
|
||||
auto s5_id = ipm->template add_subscription<MessageT>(s5);
|
||||
|
||||
auto s6 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s6 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s6->take_shared_method = false;
|
||||
auto s6_id = ipm->template add_subscription<MessageT>(s6);
|
||||
|
||||
@@ -645,12 +665,12 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
|
||||
ipm->remove_subscription(s5_id);
|
||||
ipm->remove_subscription(s6_id);
|
||||
|
||||
auto s7 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s7 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s7->take_shared_method = true;
|
||||
auto s7_id = ipm->template add_subscription<MessageT>(s7);
|
||||
(void)s7_id;
|
||||
|
||||
auto s8 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s8 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s8->take_shared_method = true;
|
||||
auto s8_id = ipm->template add_subscription<MessageT>(s8);
|
||||
(void)s8_id;
|
||||
@@ -688,15 +708,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>();
|
||||
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10));
|
||||
auto p1_id = ipm->add_publisher(p1);
|
||||
p1->set_intra_process_manager(p1_id, ipm);
|
||||
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s1->take_shared_method = true;
|
||||
auto s1_id = ipm->template add_subscription<MessageT>(s1);
|
||||
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s2->take_shared_method = false;
|
||||
auto s2_id = ipm->template add_subscription<MessageT>(s2);
|
||||
|
||||
@@ -711,15 +731,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
|
||||
ipm->remove_subscription(s1_id);
|
||||
ipm->remove_subscription(s2_id);
|
||||
|
||||
auto s3 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s3 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s3->take_shared_method = false;
|
||||
auto s3_id = ipm->template add_subscription<MessageT>(s3);
|
||||
|
||||
auto s4 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s4 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s4->take_shared_method = false;
|
||||
auto s4_id = ipm->template add_subscription<MessageT>(s4);
|
||||
|
||||
auto s5 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s5 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s5->take_shared_method = true;
|
||||
auto s5_id = ipm->template add_subscription<MessageT>(s5);
|
||||
|
||||
@@ -743,19 +763,19 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
|
||||
ipm->remove_subscription(s4_id);
|
||||
ipm->remove_subscription(s5_id);
|
||||
|
||||
auto s6 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s6 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s6->take_shared_method = true;
|
||||
auto s6_id = ipm->template add_subscription<MessageT>(s6);
|
||||
|
||||
auto s7 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s7 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s7->take_shared_method = true;
|
||||
auto s7_id = ipm->template add_subscription<MessageT>(s7);
|
||||
|
||||
auto s8 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s8 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s8->take_shared_method = false;
|
||||
auto s8_id = ipm->template add_subscription<MessageT>(s8);
|
||||
|
||||
auto s9 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s9 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s9->take_shared_method = false;
|
||||
auto s9_id = ipm->template add_subscription<MessageT>(s9);
|
||||
|
||||
@@ -781,12 +801,12 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
|
||||
ipm->remove_subscription(s8_id);
|
||||
ipm->remove_subscription(s9_id);
|
||||
|
||||
auto s10 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s10 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s10->take_shared_method = false;
|
||||
auto s10_id = ipm->template add_subscription<MessageT>(s10);
|
||||
(void)s10_id;
|
||||
|
||||
auto s11 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s11 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s11->take_shared_method = true;
|
||||
auto s11_id = ipm->template add_subscription<MessageT>(s11);
|
||||
(void)s11_id;
|
||||
@@ -831,10 +851,12 @@ TEST(TestIntraProcessManager, lowest_available_capacity) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).best_effort());
|
||||
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(history_depth).best_effort());
|
||||
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).best_effort());
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).best_effort());
|
||||
auto s1 =
|
||||
std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(history_depth).best_effort());
|
||||
auto s2 =
|
||||
std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(history_depth).best_effort());
|
||||
|
||||
auto p1_id = ipm->add_publisher(p1);
|
||||
p1->set_intra_process_manager(p1_id, ipm);
|
||||
@@ -902,7 +924,7 @@ TEST(TestIntraProcessManager, transient_local_invalid_buffer) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).transient_local());
|
||||
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(history_depth).transient_local());
|
||||
|
||||
ASSERT_THROW(
|
||||
{
|
||||
@@ -926,14 +948,14 @@ TEST(TestIntraProcessManager, transient_local) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).transient_local());
|
||||
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(history_depth).transient_local());
|
||||
|
||||
auto s1 =
|
||||
std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).transient_local());
|
||||
auto s2 =
|
||||
std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).transient_local());
|
||||
auto s3 =
|
||||
std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).transient_local());
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>(
|
||||
"topic", rclcpp::QoS(history_depth).transient_local());
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>(
|
||||
"topic", rclcpp::QoS(history_depth).transient_local());
|
||||
auto s3 = std::make_shared<SubscriptionIntraProcessT>(
|
||||
"topic", rclcpp::QoS(history_depth).transient_local());
|
||||
|
||||
s1->take_shared_method = false;
|
||||
s2->take_shared_method = true;
|
||||
|
||||
@@ -40,6 +40,11 @@ public:
|
||||
|
||||
std::shared_ptr<void> take_data() override {return nullptr;}
|
||||
void execute(const std::shared_ptr<void> &) override {}
|
||||
|
||||
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
|
||||
void clear_on_ready_callback() override {}
|
||||
|
||||
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
|
||||
};
|
||||
|
||||
class TestMemoryStrategy : public ::testing::Test
|
||||
|
||||
@@ -32,6 +32,11 @@ protected:
|
||||
const int argc = sizeof(args) / sizeof(const char *);
|
||||
rclcpp::init(argc, args);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestNodeWithGlobalArgs, local_arguments_before_global) {
|
||||
|
||||
@@ -200,6 +200,57 @@ TEST(TestNodeOptions, copy) {
|
||||
rcl_arguments_get_count_unparsed(&other_rcl_options->arguments),
|
||||
rcl_arguments_get_count_unparsed(&rcl_options->arguments));
|
||||
}
|
||||
|
||||
{
|
||||
// The following scope test is missing:
|
||||
// "arguments" because it is already tested in the above scopes
|
||||
// "parameter_event_publisher_options" because it can not be directly compared with EXPECT_EQ
|
||||
// "allocator" because it can not be directly compared with EXPECT_EQ
|
||||
|
||||
// We separate attribute modification from variable initialisation (copy assignment operator)
|
||||
// to be sure the "non_default_options"'s properties are correctly set before testing the
|
||||
// assignment operator.
|
||||
auto non_default_options = rclcpp::NodeOptions();
|
||||
non_default_options
|
||||
.parameter_overrides({rclcpp::Parameter("foo", 0), rclcpp::Parameter("bar", "1")})
|
||||
.use_global_arguments(false)
|
||||
.enable_rosout(false)
|
||||
.use_intra_process_comms(true)
|
||||
.enable_topic_statistics(true)
|
||||
.start_parameter_services(false)
|
||||
.enable_logger_service(true)
|
||||
.start_parameter_event_publisher(false)
|
||||
.clock_type(RCL_SYSTEM_TIME)
|
||||
.clock_qos(rclcpp::SensorDataQoS())
|
||||
.use_clock_thread(false)
|
||||
.parameter_event_qos(rclcpp::ClockQoS())
|
||||
.rosout_qos(rclcpp::ParameterEventsQoS())
|
||||
.allow_undeclared_parameters(true)
|
||||
.automatically_declare_parameters_from_overrides(true);
|
||||
|
||||
auto copied_options = non_default_options;
|
||||
EXPECT_EQ(non_default_options.parameter_overrides(), copied_options.parameter_overrides());
|
||||
EXPECT_EQ(non_default_options.use_global_arguments(), copied_options.use_global_arguments());
|
||||
EXPECT_EQ(non_default_options.enable_rosout(), copied_options.enable_rosout());
|
||||
EXPECT_EQ(non_default_options.use_intra_process_comms(),
|
||||
copied_options.use_intra_process_comms());
|
||||
EXPECT_EQ(non_default_options.enable_topic_statistics(),
|
||||
copied_options.enable_topic_statistics());
|
||||
EXPECT_EQ(non_default_options.start_parameter_services(),
|
||||
copied_options.start_parameter_services());
|
||||
EXPECT_EQ(non_default_options.enable_logger_service(), copied_options.enable_logger_service());
|
||||
EXPECT_EQ(non_default_options.start_parameter_event_publisher(),
|
||||
copied_options.start_parameter_event_publisher());
|
||||
EXPECT_EQ(non_default_options.clock_type(), copied_options.clock_type());
|
||||
EXPECT_EQ(non_default_options.clock_qos(), copied_options.clock_qos());
|
||||
EXPECT_EQ(non_default_options.use_clock_thread(), copied_options.use_clock_thread());
|
||||
EXPECT_EQ(non_default_options.parameter_event_qos(), copied_options.parameter_event_qos());
|
||||
EXPECT_EQ(non_default_options.rosout_qos(), copied_options.rosout_qos());
|
||||
EXPECT_EQ(non_default_options.allow_undeclared_parameters(),
|
||||
copied_options.allow_undeclared_parameters());
|
||||
EXPECT_EQ(non_default_options.automatically_declare_parameters_from_overrides(),
|
||||
copied_options.automatically_declare_parameters_from_overrides());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TestNodeOptions, append_parameter_override) {
|
||||
|
||||
@@ -31,6 +31,11 @@ protected:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestParameter, construct_destruct) {
|
||||
|
||||
@@ -60,6 +60,11 @@ protected:
|
||||
node_with_option.reset();
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
// "start_type_description_service" and "use_sim_time"
|
||||
const uint64_t builtin_param_count = 2;
|
||||
rclcpp::Node::SharedPtr node;
|
||||
|
||||
@@ -107,6 +107,11 @@ protected:
|
||||
param_handler.reset();
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::ParameterEvent::SharedPtr same_node_int;
|
||||
rcl_interfaces::msg::ParameterEvent::SharedPtr same_node_double;
|
||||
rcl_interfaces::msg::ParameterEvent::SharedPtr diff_node_int;
|
||||
|
||||
@@ -38,9 +38,7 @@ class TestPublisher : public ::testing::Test
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
if (!rclcpp::ok()) {
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -54,6 +52,11 @@ protected:
|
||||
node.reset();
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
@@ -81,6 +84,7 @@ class TestPublisherSub : public ::testing::Test
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
@@ -94,6 +98,11 @@ protected:
|
||||
node.reset();
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
rclcpp::Node::SharedPtr subnode;
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
#include "rmw/types.h"
|
||||
|
||||
@@ -241,13 +242,20 @@ TEST(TestQoS, qos_check_compatible)
|
||||
// TODO(jacobperron): programmatically check if current RMW is one of the officially
|
||||
// supported DDS middlewares before running the following tests
|
||||
|
||||
// If the RMW implementation is rmw_zenoh_cpp, we do not expect any QoS incompatibilities.
|
||||
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
|
||||
// Incompatible
|
||||
{
|
||||
rclcpp::QoS pub_qos = rclcpp::QoS(1).best_effort();
|
||||
rclcpp::QoS sub_qos = rclcpp::QoS(1).reliable();
|
||||
rclcpp::QoSCheckCompatibleResult ret = rclcpp::qos_check_compatible(pub_qos, sub_qos);
|
||||
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Error);
|
||||
EXPECT_FALSE(ret.reason.empty());
|
||||
if (rmw_implementation_str == "rmw_zenoh_cpp") {
|
||||
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Ok);
|
||||
EXPECT_TRUE(ret.reason.empty());
|
||||
} else {
|
||||
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Error);
|
||||
EXPECT_FALSE(ret.reason.empty());
|
||||
}
|
||||
}
|
||||
|
||||
// Warn of possible incompatibility
|
||||
@@ -255,7 +263,12 @@ TEST(TestQoS, qos_check_compatible)
|
||||
rclcpp::SystemDefaultsQoS pub_qos;
|
||||
rclcpp::QoS sub_qos = rclcpp::QoS(1).reliable();
|
||||
rclcpp::QoSCheckCompatibleResult ret = rclcpp::qos_check_compatible(pub_qos, sub_qos);
|
||||
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Warning);
|
||||
EXPECT_FALSE(ret.reason.empty());
|
||||
if (rmw_implementation_str == "rmw_zenoh_cpp") {
|
||||
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Ok);
|
||||
EXPECT_TRUE(ret.reason.empty());
|
||||
} else {
|
||||
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Warning);
|
||||
EXPECT_FALSE(ret.reason.empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,13 +33,14 @@ using namespace std::chrono_literals;
|
||||
class TestQosEvent : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
// We initialize and shutdown the context (and hence also the rmw_context),
|
||||
// for each test case to reset the ROS graph for each test case.
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
rmw_implementation_str = std::string(rmw_get_implementation_identifier());
|
||||
|
||||
node = std::make_shared<rclcpp::Node>("test_qos_event", "/ns");
|
||||
|
||||
message_callback = [node = node.get()](test_msgs::msg::Empty::ConstSharedPtr /*msg*/) {
|
||||
@@ -50,8 +51,10 @@ protected:
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
std::string rmw_implementation_str;
|
||||
static constexpr char topic_name[] = "test_topic";
|
||||
rclcpp::Node::SharedPtr node;
|
||||
std::function<void(test_msgs::msg::Empty::ConstSharedPtr)> message_callback;
|
||||
@@ -70,28 +73,29 @@ TEST_F(TestQosEvent, test_publisher_constructor)
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>(
|
||||
topic_name, 10, options);
|
||||
|
||||
// options arg with one of the callbacks
|
||||
options.event_callbacks.deadline_callback =
|
||||
[node = node.get()](rclcpp::QOSDeadlineOfferedInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Offered deadline missed - total %d (delta %d)",
|
||||
event.total_count, event.total_count_change);
|
||||
};
|
||||
publisher = node->create_publisher<test_msgs::msg::Empty>(
|
||||
topic_name, 10, options);
|
||||
|
||||
// options arg with two of the callbacks
|
||||
options.event_callbacks.liveliness_callback =
|
||||
[node = node.get()](rclcpp::QOSLivelinessLostInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Liveliness lost - total %d (delta %d)",
|
||||
event.total_count, event.total_count_change);
|
||||
};
|
||||
publisher = node->create_publisher<test_msgs::msg::Empty>(
|
||||
topic_name, 10, options);
|
||||
if (rmw_implementation_str != "rmw_zenoh_cpp") {
|
||||
// options arg with one of the callbacks
|
||||
options.event_callbacks.deadline_callback =
|
||||
[node = node.get()](rclcpp::QOSDeadlineOfferedInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Offered deadline missed - total %d (delta %d)",
|
||||
event.total_count, event.total_count_change);
|
||||
};
|
||||
publisher = node->create_publisher<test_msgs::msg::Empty>(
|
||||
topic_name, 10, options);
|
||||
|
||||
// options arg with two of the callbacks
|
||||
options.event_callbacks.liveliness_callback =
|
||||
[node = node.get()](rclcpp::QOSLivelinessLostInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Liveliness lost - total %d (delta %d)",
|
||||
event.total_count, event.total_count_change);
|
||||
};
|
||||
publisher = node->create_publisher<test_msgs::msg::Empty>(
|
||||
topic_name, 10, options);
|
||||
}
|
||||
// options arg with three of the callbacks
|
||||
options.event_callbacks.incompatible_qos_callback =
|
||||
[node = node.get()](rclcpp::QOSOfferedIncompatibleQoSInfo & event) {
|
||||
@@ -109,35 +113,38 @@ TEST_F(TestQosEvent, test_publisher_constructor)
|
||||
*/
|
||||
TEST_F(TestQosEvent, test_subscription_constructor)
|
||||
{
|
||||
// While rmw_zenoh does not support Deadline/LivelinessChanged events,
|
||||
// it does support IncompatibleQoS
|
||||
rclcpp::SubscriptionOptions options;
|
||||
|
||||
// options arg with no callbacks
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, options);
|
||||
|
||||
// options arg with one of the callbacks
|
||||
options.event_callbacks.deadline_callback =
|
||||
[node = node.get()](rclcpp::QOSDeadlineRequestedInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Requested deadline missed - total %d (delta %d)",
|
||||
event.total_count, event.total_count_change);
|
||||
};
|
||||
subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, options);
|
||||
|
||||
// options arg with two of the callbacks
|
||||
options.event_callbacks.liveliness_callback =
|
||||
[node = node.get()](rclcpp::QOSLivelinessChangedInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Liveliness changed - alive %d (delta %d), not alive %d (delta %d)",
|
||||
event.alive_count, event.alive_count_change,
|
||||
event.not_alive_count, event.not_alive_count_change);
|
||||
};
|
||||
subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, options);
|
||||
if (rmw_implementation_str != "rmw_zenoh_cpp") {
|
||||
// options arg with one of the callbacks
|
||||
options.event_callbacks.deadline_callback =
|
||||
[node = node.get()](rclcpp::QOSDeadlineRequestedInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Requested deadline missed - total %d (delta %d)",
|
||||
event.total_count, event.total_count_change);
|
||||
};
|
||||
subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, options);
|
||||
|
||||
// options arg with two of the callbacks
|
||||
options.event_callbacks.liveliness_callback =
|
||||
[node = node.get()](rclcpp::QOSLivelinessChangedInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Liveliness changed - alive %d (delta %d), not alive %d (delta %d)",
|
||||
event.alive_count, event.alive_count_change,
|
||||
event.not_alive_count, event.not_alive_count_change);
|
||||
};
|
||||
subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, options);
|
||||
}
|
||||
// options arg with three of the callbacks
|
||||
options.event_callbacks.incompatible_qos_callback =
|
||||
[node = node.get()](rclcpp::QOSRequestedIncompatibleQoSInfo & event) {
|
||||
@@ -204,14 +211,19 @@ TEST_F(TestQosEvent, test_default_incompatible_qos_callbacks)
|
||||
const auto timeout = std::chrono::seconds(10);
|
||||
ex.spin_until_future_complete(log_msgs_future, timeout);
|
||||
|
||||
EXPECT_EQ(
|
||||
"New subscription discovered on topic '/ns/test_topic', requesting incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
pub_log_msg);
|
||||
EXPECT_EQ(
|
||||
"New publisher discovered on topic '/ns/test_topic', offering incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
sub_log_msg);
|
||||
if (rmw_implementation_str == "rmw_zenoh_cpp") {
|
||||
EXPECT_EQ(rclcpp::QoSCompatibility::Ok,
|
||||
qos_check_compatible(qos_profile_publisher, qos_profile_subscription).compatibility);
|
||||
} else {
|
||||
EXPECT_EQ(
|
||||
"New subscription discovered on topic '/ns/test_topic', requesting incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
pub_log_msg);
|
||||
EXPECT_EQ(
|
||||
"New publisher discovered on topic '/ns/test_topic', offering incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
sub_log_msg);
|
||||
}
|
||||
|
||||
rcutils_logging_set_output_handler(original_output_handler);
|
||||
}
|
||||
@@ -223,7 +235,8 @@ TEST_F(TestQosEvent, construct_destruct_rcl_error) {
|
||||
|
||||
// This callback requires some type of parameter, but it could be anything
|
||||
auto callback = [](int) {};
|
||||
const rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
|
||||
const rcl_publisher_event_type_t event_type = rmw_implementation_str == "rmw_zenoh_cpp" ?
|
||||
RCL_PUBLISHER_MATCHED : RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
|
||||
|
||||
{
|
||||
// Logs error and returns
|
||||
@@ -260,13 +273,16 @@ TEST_F(TestQosEvent, construct_destruct_rcl_error) {
|
||||
}
|
||||
|
||||
TEST_F(TestQosEvent, execute) {
|
||||
if (rmw_implementation_str == "rmw_zenoh_cpp") {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
|
||||
auto rcl_handle = publisher->get_publisher_handle();
|
||||
|
||||
bool handler_callback_executed = false;
|
||||
// This callback requires some type of parameter, but it could be anything
|
||||
auto callback = [&handler_callback_executed](int) {handler_callback_executed = true;};
|
||||
rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
|
||||
const rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
|
||||
|
||||
rclcpp::EventHandler<decltype(callback), decltype(rcl_handle)> handler(
|
||||
callback, rcl_publisher_event_init, rcl_handle, event_type);
|
||||
@@ -292,8 +308,9 @@ TEST_F(TestQosEvent, add_to_wait_set) {
|
||||
// This callback requires some type of parameter, but it could be anything
|
||||
auto callback = [](int) {};
|
||||
|
||||
rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
|
||||
rclcpp::EventHandler<decltype(callback), decltype(rcl_handle)> handler(
|
||||
const rcl_publisher_event_type_t event_type = rmw_implementation_str == "rmw_zenoh_cpp" ?
|
||||
RCL_PUBLISHER_MATCHED : RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
|
||||
rclcpp::EventHandler<decltype(callback), decltype(rcl_handle)> handler(
|
||||
callback, rcl_publisher_event_init, rcl_handle, event_type);
|
||||
|
||||
EXPECT_EQ(1u, handler.get_number_of_ready_events());
|
||||
@@ -314,6 +331,10 @@ TEST_F(TestQosEvent, add_to_wait_set) {
|
||||
|
||||
TEST_F(TestQosEvent, test_on_new_event_callback)
|
||||
{
|
||||
if (rmw_implementation_str == "rmw_zenoh_cpp") {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto offered_deadline = rclcpp::Duration(std::chrono::milliseconds(1));
|
||||
auto requested_deadline = rclcpp::Duration(std::chrono::milliseconds(2));
|
||||
|
||||
@@ -359,18 +380,19 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>(topic_name, 10, message_callback);
|
||||
auto dummy_cb = [](size_t count_events) {(void)count_events;};
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
|
||||
if (rmw_implementation_str != "rmw_zenoh_cpp") {
|
||||
EXPECT_NO_THROW(
|
||||
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
|
||||
EXPECT_NO_THROW(
|
||||
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_LIVELINESS_LOST));
|
||||
EXPECT_NO_THROW(
|
||||
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_LIVELINESS_LOST));
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
EXPECT_NO_THROW(
|
||||
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_LIVELINESS_LOST));
|
||||
|
||||
}
|
||||
EXPECT_NO_THROW(
|
||||
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS));
|
||||
|
||||
@@ -383,18 +405,19 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
|
||||
EXPECT_NO_THROW(
|
||||
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_MATCHED));
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
|
||||
if (rmw_implementation_str == "rmw_zenoh_cpp") {
|
||||
EXPECT_NO_THROW(
|
||||
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
|
||||
EXPECT_NO_THROW(
|
||||
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_LIVELINESS_CHANGED));
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_LIVELINESS_CHANGED));
|
||||
EXPECT_NO_THROW(
|
||||
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_LIVELINESS_CHANGED));
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_LIVELINESS_CHANGED));
|
||||
}
|
||||
EXPECT_NO_THROW(
|
||||
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS));
|
||||
|
||||
@@ -407,24 +430,26 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
|
||||
EXPECT_NO_THROW(
|
||||
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_MATCHED));
|
||||
|
||||
std::function<void(size_t)> invalid_cb;
|
||||
if (rmw_implementation_str != "rmw_zenoh_cpp") {
|
||||
std::function<void(size_t)> invalid_cb;
|
||||
|
||||
rclcpp::SubscriptionOptions sub_options;
|
||||
sub_options.event_callbacks.deadline_callback = [](auto) {};
|
||||
sub = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, sub_options);
|
||||
rclcpp::SubscriptionOptions sub_options;
|
||||
sub_options.event_callbacks.deadline_callback = [](auto) {};
|
||||
sub = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, sub_options);
|
||||
|
||||
EXPECT_THROW(
|
||||
sub->set_on_new_qos_event_callback(invalid_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED),
|
||||
std::invalid_argument);
|
||||
EXPECT_THROW(
|
||||
sub->set_on_new_qos_event_callback(invalid_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED),
|
||||
std::invalid_argument);
|
||||
|
||||
rclcpp::PublisherOptions pub_options;
|
||||
pub_options.event_callbacks.deadline_callback = [](auto) {};
|
||||
pub = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10, pub_options);
|
||||
rclcpp::PublisherOptions pub_options;
|
||||
pub_options.event_callbacks.deadline_callback = [](auto) {};
|
||||
pub = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10, pub_options);
|
||||
|
||||
EXPECT_THROW(
|
||||
pub->set_on_new_qos_event_callback(invalid_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED),
|
||||
std::invalid_argument);
|
||||
EXPECT_THROW(
|
||||
pub->set_on_new_qos_event_callback(invalid_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED),
|
||||
std::invalid_argument);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestQosEvent, test_pub_matched_event_by_set_event_callback)
|
||||
|
||||
@@ -123,7 +123,7 @@ TEST_F(TestServiceSub, construction_and_destruction) {
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto service = node->create_service<rcl_interfaces::srv::ListParameters>(
|
||||
auto service = subnode->create_service<rcl_interfaces::srv::ListParameters>(
|
||||
"invalid_service?", callback);
|
||||
}, rclcpp::exceptions::InvalidServiceNameError);
|
||||
}
|
||||
|
||||
@@ -124,6 +124,20 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
|
||||
ASSERT_THAT(
|
||||
client_gid_arr,
|
||||
testing::Eq(event_map[ServiceEventInfo::REQUEST_SENT]->info.client_gid));
|
||||
// TODO(@fujitatomoya): Remove this if statement once rmw implementations support test.
|
||||
// rmw_cyclonedds_cpp does not pass this test requirement for now.
|
||||
// See more details for https://github.com/ros2/rmw/issues/357
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_cyclonedds_cpp") != 0) {
|
||||
ASSERT_THAT(
|
||||
client_gid_arr,
|
||||
testing::Eq(event_map[ServiceEventInfo::REQUEST_RECEIVED]->info.client_gid));
|
||||
ASSERT_THAT(
|
||||
client_gid_arr,
|
||||
testing::Eq(event_map[ServiceEventInfo::RESPONSE_SENT]->info.client_gid));
|
||||
}
|
||||
ASSERT_THAT(
|
||||
client_gid_arr,
|
||||
testing::Eq(event_map[ServiceEventInfo::RESPONSE_RECEIVED]->info.client_gid));
|
||||
|
||||
ASSERT_EQ(
|
||||
event_map[ServiceEventInfo::REQUEST_SENT]->info.sequence_number,
|
||||
|
||||
@@ -30,14 +30,7 @@
|
||||
|
||||
#include "test_msgs/msg/basic_types.hpp"
|
||||
|
||||
#ifdef RMW_IMPLEMENTATION
|
||||
# define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX
|
||||
# define CLASSNAME(NAME, SUFFIX) CLASSNAME_(NAME, SUFFIX)
|
||||
#else
|
||||
# define CLASSNAME(NAME, SUFFIX) NAME
|
||||
#endif
|
||||
|
||||
class CLASSNAME (TestContentFilterSubscription, RMW_IMPLEMENTATION) : public ::testing::Test
|
||||
class TestContentFilterSubscription : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
@@ -113,7 +106,8 @@ bool operator==(const test_msgs::msg::BasicTypes & m1, const test_msgs::msg::Bas
|
||||
m1.uint64_value == m2.uint64_value;
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), is_cft_enabled) {
|
||||
TEST_F(TestContentFilterSubscription, is_cft_enabled)
|
||||
{
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_subscription_is_cft_enabled, false);
|
||||
@@ -127,7 +121,8 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), is_cft_enab
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), get_content_filter_error) {
|
||||
TEST_F(TestContentFilterSubscription, get_content_filter_error)
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_subscription_get_content_filter, RCL_RET_ERROR);
|
||||
|
||||
@@ -137,7 +132,8 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), get_content
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), set_content_filter_error) {
|
||||
TEST_F(TestContentFilterSubscription, set_content_filter_error)
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_subscription_set_content_filter, RCL_RET_ERROR);
|
||||
|
||||
@@ -148,7 +144,8 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), set_content
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), get_content_filter) {
|
||||
TEST_F(TestContentFilterSubscription, get_content_filter)
|
||||
{
|
||||
rclcpp::ContentFilterOptions options;
|
||||
|
||||
if (sub->is_cft_enabled()) {
|
||||
@@ -164,7 +161,8 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), get_content
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), set_content_filter) {
|
||||
TEST_F(TestContentFilterSubscription, set_content_filter)
|
||||
{
|
||||
if (sub->is_cft_enabled()) {
|
||||
EXPECT_NO_THROW(
|
||||
sub->set_content_filter(filter_expression_init, expression_parameters_2));
|
||||
@@ -175,7 +173,13 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), set_content
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_filter_get_begin) {
|
||||
TEST_F(TestContentFilterSubscription, content_filter_get_begin)
|
||||
{
|
||||
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
|
||||
if (rmw_implementation_str == "rmw_zenoh_cpp") {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
{
|
||||
test_msgs::msg::BasicTypes msg;
|
||||
@@ -217,7 +221,13 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_fil
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_filter_get_later) {
|
||||
TEST_F(TestContentFilterSubscription, content_filter_get_later)
|
||||
{
|
||||
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
|
||||
if (rmw_implementation_str == "rmw_zenoh_cpp") {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
{
|
||||
test_msgs::msg::BasicTypes msg;
|
||||
@@ -264,7 +274,13 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_fil
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_filter_reset) {
|
||||
TEST_F(TestContentFilterSubscription, content_filter_reset)
|
||||
{
|
||||
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
|
||||
if (rmw_implementation_str == "rmw_zenoh_cpp") {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
{
|
||||
test_msgs::msg::BasicTypes msg;
|
||||
@@ -311,11 +327,8 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_fil
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
CLASSNAME(
|
||||
TestContentFilterSubscription,
|
||||
RMW_IMPLEMENTATION), create_two_content_filters_with_same_topic_name_and_destroy) {
|
||||
|
||||
TEST_F(TestContentFilterSubscription, create_two_content_filters_with_same_topic_name_and_destroy)
|
||||
{
|
||||
// Create another content filter
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
|
||||
|
||||
@@ -51,6 +51,11 @@ protected:
|
||||
node.reset();
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
@@ -60,7 +65,7 @@ TEST_F(TestSubscriptionOptions, topic_statistics_options_default_and_set) {
|
||||
EXPECT_EQ(options.topic_stats_options.state, rclcpp::TopicStatisticsState::NodeDefault);
|
||||
EXPECT_EQ(options.topic_stats_options.publish_topic, defaultPublishTopic);
|
||||
EXPECT_EQ(options.topic_stats_options.publish_period, 1s);
|
||||
EXPECT_EQ(options.topic_stats_options.qos, rclcpp::SystemDefaultsQoS());
|
||||
EXPECT_EQ(options.topic_stats_options.qos, rclcpp::SystemDefaultsQoS().keep_last(10));
|
||||
|
||||
options.topic_stats_options.state = rclcpp::TopicStatisticsState::Enable;
|
||||
options.topic_stats_options.publish_topic = "topic_statistics";
|
||||
|
||||
@@ -178,11 +178,11 @@ MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, <)
|
||||
|
||||
TEST(TestUtilities, test_context_init_shutdown_fails) {
|
||||
{
|
||||
auto context = std::make_shared<rclcpp::contexts::DefaultContext>();
|
||||
auto context_fail_init = std::make_shared<rclcpp::contexts::DefaultContext>();
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_init, RCL_RET_ERROR);
|
||||
EXPECT_THROW(context_fail_init->init(0, nullptr), rclcpp::exceptions::RCLError);
|
||||
EXPECT_FALSE(context_fail_init->is_valid());
|
||||
}
|
||||
|
||||
{
|
||||
@@ -190,6 +190,7 @@ TEST(TestUtilities, test_context_init_shutdown_fails) {
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_logging_configure_with_output_handler, RCL_RET_ERROR);
|
||||
EXPECT_THROW(context_fail_init->init(0, nullptr), rclcpp::exceptions::RCLError);
|
||||
EXPECT_FALSE(context_fail_init->is_valid());
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -31,6 +31,11 @@ protected:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -343,7 +343,6 @@ TEST_F(TestSubscriptionTopicStatisticsFixture, test_receive_stats_for_message_no
|
||||
uint64_t message_age_count{0};
|
||||
uint64_t message_period_count{0};
|
||||
|
||||
std::set<std::string> received_metrics;
|
||||
for (const auto & msg : received_messages) {
|
||||
if (msg.metrics_source == kMessageAgeSourceLabel) {
|
||||
message_age_count++;
|
||||
|
||||
@@ -52,16 +52,18 @@ public:
|
||||
: is_ready_(false) {}
|
||||
|
||||
void add_to_wait_set(rcl_wait_set_t &) override {}
|
||||
|
||||
bool is_ready(const rcl_wait_set_t &) override {return is_ready_;}
|
||||
|
||||
std::shared_ptr<void> take_data() override {return nullptr;}
|
||||
|
||||
void
|
||||
execute(const std::shared_ptr<void> &) override {}
|
||||
void execute(const std::shared_ptr<void> &) override {}
|
||||
|
||||
void set_is_ready(bool value) {is_ready_ = value;}
|
||||
|
||||
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
|
||||
void clear_on_ready_callback() override {}
|
||||
|
||||
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
|
||||
|
||||
private:
|
||||
bool is_ready_;
|
||||
};
|
||||
|
||||
@@ -52,16 +52,18 @@ public:
|
||||
: is_ready_(false) {}
|
||||
|
||||
void add_to_wait_set(rcl_wait_set_t &) override {}
|
||||
|
||||
bool is_ready(const rcl_wait_set_t &) override {return is_ready_;}
|
||||
|
||||
std::shared_ptr<void> take_data() override {return nullptr;}
|
||||
|
||||
void
|
||||
execute(const std::shared_ptr<void> &) override {}
|
||||
void execute(const std::shared_ptr<void> &) override {}
|
||||
|
||||
void set_is_ready(bool value) {is_ready_ = value;}
|
||||
|
||||
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
|
||||
void clear_on_ready_callback() override {}
|
||||
|
||||
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
|
||||
|
||||
private:
|
||||
bool is_ready_;
|
||||
};
|
||||
|
||||
@@ -61,14 +61,17 @@ public:
|
||||
bool is_ready(const rcl_wait_set_t &) override {return is_ready_;}
|
||||
|
||||
std::shared_ptr<void> take_data() override {return nullptr;}
|
||||
|
||||
void
|
||||
execute(const std::shared_ptr<void> & data) override {(void)data;}
|
||||
void execute(const std::shared_ptr<void> &) override {}
|
||||
|
||||
void set_is_ready(bool value) {is_ready_ = value;}
|
||||
|
||||
void set_add_to_wait_set(bool value) {add_to_wait_set_ = value;}
|
||||
|
||||
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
|
||||
void clear_on_ready_callback() override {}
|
||||
|
||||
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
|
||||
|
||||
private:
|
||||
bool is_ready_;
|
||||
bool add_to_wait_set_;
|
||||
|
||||
@@ -52,16 +52,18 @@ public:
|
||||
: is_ready_(false) {}
|
||||
|
||||
void add_to_wait_set(rcl_wait_set_t &) override {}
|
||||
|
||||
bool is_ready(const rcl_wait_set_t &) override {return is_ready_;}
|
||||
|
||||
std::shared_ptr<void> take_data() override {return nullptr;}
|
||||
|
||||
void
|
||||
execute(const std::shared_ptr<void> &) override {}
|
||||
void execute(const std::shared_ptr<void> &) override {}
|
||||
|
||||
void set_is_ready(bool value) {is_ready_ = value;}
|
||||
|
||||
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
|
||||
void clear_on_ready_callback() override {}
|
||||
|
||||
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
|
||||
|
||||
private:
|
||||
bool is_ready_;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,23 @@ Changelog for package rclcpp_action
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
29.2.0 (2024-11-25)
|
||||
-------------------
|
||||
|
||||
29.1.0 (2024-11-20)
|
||||
-------------------
|
||||
* Fix documentation typo in server_goal_handle.hpp (`#2669 <https://github.com/ros2/rclcpp/issues/2669>`_)
|
||||
* Contributors: YR
|
||||
|
||||
29.0.0 (2024-10-03)
|
||||
-------------------
|
||||
* Increase the timeout for the cppcheck on rclcpp_action. (`#2640 <https://github.com/ros2/rclcpp/issues/2640>`_)
|
||||
* add smart pointer macros definitions to action server and client base classes (`#2631 <https://github.com/ros2/rclcpp/issues/2631>`_)
|
||||
* Contributors: Alberto Soragna, Chris Lalancette
|
||||
|
||||
28.3.3 (2024-07-29)
|
||||
-------------------
|
||||
|
||||
28.3.2 (2024-07-24)
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -135,3 +135,8 @@ if(BUILD_TESTING)
|
||||
endif()
|
||||
|
||||
ament_package()
|
||||
|
||||
if(TEST cppcheck)
|
||||
# must set the property after ament_package()
|
||||
set_tests_properties(cppcheck PROPERTIES TIMEOUT 600)
|
||||
endif()
|
||||
|
||||
@@ -62,6 +62,8 @@ class ClientBaseImpl;
|
||||
class ClientBase : public rclcpp::Waitable
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ClientBase)
|
||||
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
virtual ~ClientBase();
|
||||
|
||||
|
||||
@@ -72,6 +72,8 @@ enum class CancelResponse : int8_t
|
||||
class ServerBase : public rclcpp::Waitable
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ServerBase)
|
||||
|
||||
/// Enum to identify entities belonging to the action server
|
||||
enum class EntityType : std::size_t
|
||||
{
|
||||
|
||||
@@ -43,7 +43,7 @@ class ServerGoalHandleBase
|
||||
{
|
||||
public:
|
||||
/// Indicate if client has requested this goal be cancelled.
|
||||
/// \return true if a cancelation request has been accepted for this goal.
|
||||
/// \return true if a cancellation request has been accepted for this goal.
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
bool
|
||||
is_canceling() const;
|
||||
|
||||
@@ -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>28.3.2</version>
|
||||
<version>29.2.0</version>
|
||||
<description>Adds action APIs for C++.</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
|
||||
@@ -2,6 +2,21 @@
|
||||
Changelog for package rclcpp_components
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
29.2.0 (2024-11-25)
|
||||
-------------------
|
||||
|
||||
29.1.0 (2024-11-20)
|
||||
-------------------
|
||||
|
||||
29.0.0 (2024-10-03)
|
||||
-------------------
|
||||
* Shutdown the context before context's destructor is invoked in tests (`#2633 <https://github.com/ros2/rclcpp/issues/2633>`_)
|
||||
* Fix typo in rclcpp_components benchmark_components (`#2602 <https://github.com/ros2/rclcpp/issues/2602>`_)
|
||||
* Contributors: Alejandro Hernández Cordero, Christophe Bedard
|
||||
|
||||
28.3.3 (2024-07-29)
|
||||
-------------------
|
||||
|
||||
28.3.2 (2024-07-24)
|
||||
-------------------
|
||||
* Updated rcpputils path API (`#2579 <https://github.com/ros2/rclcpp/issues/2579>`_)
|
||||
|
||||
@@ -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>28.3.2</version>
|
||||
<version>29.2.0</version>
|
||||
<description>Package containing tools for dynamically loadable components</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
|
||||
@@ -109,7 +109,7 @@ BENCHMARK_F(ComponentTest, create_node_instance)(benchmark::State & state)
|
||||
}
|
||||
|
||||
// Choosing resource 0 - the other two test components were shown empirically to yield
|
||||
// the same performance charactarisitics, so they shouldn't need their own benchmarks.
|
||||
// the same performance characteristics, so they shouldn't need their own benchmarks.
|
||||
const std::shared_ptr<rclcpp_components::NodeFactory> factory =
|
||||
manager->create_component_factory(resources[0]);
|
||||
|
||||
|
||||
@@ -28,6 +28,11 @@ protected:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestComponentManager, get_component_resources_invalid)
|
||||
|
||||
@@ -33,6 +33,11 @@ protected:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
// TODO(hidmic): split up tests once Node bring up/tear down races
|
||||
|
||||
@@ -3,6 +3,24 @@ Changelog for package rclcpp_lifecycle
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
29.2.0 (2024-11-25)
|
||||
-------------------
|
||||
|
||||
29.1.0 (2024-11-20)
|
||||
-------------------
|
||||
* Fix error message in rclcpp_lifecycle::State::reset() (`#2647 <https://github.com/ros2/rclcpp/issues/2647>`_)
|
||||
* Contributors: Christophe Bedard
|
||||
|
||||
29.0.0 (2024-10-03)
|
||||
-------------------
|
||||
* Shutdown the context before context's destructor is invoked in tests (`#2633 <https://github.com/ros2/rclcpp/issues/2633>`_)
|
||||
* LifecycleNode bugfix and add test cases (`#2562 <https://github.com/ros2/rclcpp/issues/2562>`_)
|
||||
* Properly test get_service_names_and_types_by_node in rclcpp_lifecycle (`#2599 <https://github.com/ros2/rclcpp/issues/2599>`_)
|
||||
* Contributors: Alejandro Hernández Cordero, Christophe Bedard, Tomoya Fujita
|
||||
|
||||
28.3.3 (2024-07-29)
|
||||
-------------------
|
||||
|
||||
28.3.2 (2024-07-24)
|
||||
-------------------
|
||||
* Removed deprecated methods and classes (`#2575 <https://github.com/ros2/rclcpp/issues/2575>`_)
|
||||
|
||||
@@ -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>28.3.2</version>
|
||||
<version>29.2.0</version>
|
||||
<description>Package containing a prototype for lifecycle implementation</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
|
||||
@@ -152,8 +152,19 @@ LifecycleNode::LifecycleNode(
|
||||
|
||||
LifecycleNode::~LifecycleNode()
|
||||
{
|
||||
auto current_state = LifecycleNode::get_current_state().id();
|
||||
if (current_state != lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED) {
|
||||
// This might be leaving sensors and devices without shutting down unintentionally.
|
||||
// It is user's responsibility to call shutdown to avoid leaving them unknow states.
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp_lifecycle"),
|
||||
"LifecycleNode is not shut down: Node still in state(%u) in destructor",
|
||||
current_state);
|
||||
}
|
||||
|
||||
// release sub-interfaces in an order that allows them to consult with node_base during tear-down
|
||||
node_waitables_.reset();
|
||||
node_type_descriptions_.reset();
|
||||
node_time_source_.reset();
|
||||
node_parameters_.reset();
|
||||
node_clock_.reset();
|
||||
@@ -162,6 +173,7 @@ LifecycleNode::~LifecycleNode()
|
||||
node_timers_.reset();
|
||||
node_logging_.reset();
|
||||
node_graph_.reset();
|
||||
node_base_.reset();
|
||||
}
|
||||
|
||||
const char *
|
||||
|
||||
@@ -167,7 +167,7 @@ State::reset() noexcept
|
||||
if (ret != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp_lifecycle"),
|
||||
"rcl_lifecycle_transition_fini did not complete successfully, leaking memory");
|
||||
"rcl_lifecycle_state_fini did not complete successfully, leaking memory");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,11 @@ protected:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
class PositiveCallbackExceptionNode : public rclcpp_lifecycle::LifecycleNode
|
||||
|
||||
@@ -431,6 +431,71 @@ TEST_F(TestDefaultStateMachine, bad_mood) {
|
||||
EXPECT_EQ(1u, test_node->number_of_callbacks);
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, shutdown_from_each_primary_state) {
|
||||
auto success = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
|
||||
auto reset_key = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR;
|
||||
|
||||
// PRIMARY_STATE_UNCONFIGURED to shutdown
|
||||
{
|
||||
auto ret = reset_key;
|
||||
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
|
||||
auto finalized = test_node->shutdown(ret);
|
||||
EXPECT_EQ(success, ret);
|
||||
EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED);
|
||||
}
|
||||
|
||||
// PRIMARY_STATE_INACTIVE to shutdown
|
||||
{
|
||||
auto ret = reset_key;
|
||||
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
|
||||
auto configured = test_node->configure(ret);
|
||||
EXPECT_EQ(success, ret);
|
||||
EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE);
|
||||
ret = reset_key;
|
||||
auto finalized = test_node->shutdown(ret);
|
||||
EXPECT_EQ(success, ret);
|
||||
EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED);
|
||||
}
|
||||
|
||||
// PRIMARY_STATE_ACTIVE to shutdown
|
||||
{
|
||||
auto ret = reset_key;
|
||||
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
|
||||
auto configured = test_node->configure(ret);
|
||||
EXPECT_EQ(success, ret);
|
||||
EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE);
|
||||
ret = reset_key;
|
||||
auto activated = test_node->activate(ret);
|
||||
EXPECT_EQ(success, ret);
|
||||
EXPECT_EQ(activated.id(), State::PRIMARY_STATE_ACTIVE);
|
||||
ret = reset_key;
|
||||
auto finalized = test_node->shutdown(ret);
|
||||
EXPECT_EQ(success, ret);
|
||||
EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED);
|
||||
}
|
||||
|
||||
// PRIMARY_STATE_FINALIZED to shutdown
|
||||
{
|
||||
auto ret = reset_key;
|
||||
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
|
||||
auto configured = test_node->configure(ret);
|
||||
EXPECT_EQ(success, ret);
|
||||
EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE);
|
||||
ret = reset_key;
|
||||
auto activated = test_node->activate(ret);
|
||||
EXPECT_EQ(success, ret);
|
||||
EXPECT_EQ(activated.id(), State::PRIMARY_STATE_ACTIVE);
|
||||
ret = reset_key;
|
||||
auto finalized = test_node->shutdown(ret);
|
||||
EXPECT_EQ(success, ret);
|
||||
EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED);
|
||||
ret = reset_key;
|
||||
auto finalized_again = test_node->shutdown(ret);
|
||||
EXPECT_EQ(reset_key, ret);
|
||||
EXPECT_EQ(finalized_again.id(), State::PRIMARY_STATE_FINALIZED);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, lifecycle_subscriber) {
|
||||
auto test_node = std::make_shared<MoodyLifecycleNode<GoodMood>>("testnode");
|
||||
|
||||
|
||||
@@ -18,11 +18,14 @@
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "lifecycle_msgs/msg/state.hpp"
|
||||
@@ -368,26 +371,34 @@ TEST_F(TestLifecycleServiceClient, lifecycle_transitions) {
|
||||
|
||||
TEST_F(TestLifecycleServiceClient, get_service_names_and_types_by_node)
|
||||
{
|
||||
auto node1 = std::make_shared<LifecycleServiceClient>("client1");
|
||||
auto node2 = std::make_shared<LifecycleServiceClient>("client2");
|
||||
|
||||
auto node_graph = node1->get_node_graph_interface();
|
||||
ASSERT_NE(nullptr, node_graph);
|
||||
|
||||
EXPECT_THROW(
|
||||
node_graph->get_service_names_and_types_by_node("not_a_node", "not_absolute_namespace"),
|
||||
lifecycle_node()->get_service_names_and_types_by_node("not_a_node", "not_absolute_namespace"),
|
||||
std::runtime_error);
|
||||
auto service_names_and_types1 = node_graph->get_service_names_and_types_by_node("client1", "/");
|
||||
auto service_names_and_types2 = node_graph->get_service_names_and_types_by_node("client2", "/");
|
||||
auto service_names_and_types =
|
||||
lifecycle_node()->get_service_names_and_types_by_node(lifecycle_node_name, "/");
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (0 == service_names_and_types1.size() ||
|
||||
service_names_and_types1.size() != service_names_and_types2.size() ||
|
||||
while (0 == service_names_and_types.size() ||
|
||||
(std::chrono::steady_clock::now() - start) < std::chrono::seconds(1))
|
||||
{
|
||||
service_names_and_types1 = node_graph->get_service_names_and_types_by_node("client1", "/");
|
||||
service_names_and_types2 = node_graph->get_service_names_and_types_by_node("client2", "/");
|
||||
service_names_and_types =
|
||||
lifecycle_node()->get_service_names_and_types_by_node(lifecycle_node_name, "/");
|
||||
}
|
||||
const std::array services = {
|
||||
std::make_pair(node_get_state_topic, "lifecycle_msgs/srv/GetState"),
|
||||
std::make_pair(node_change_state_topic, "lifecycle_msgs/srv/ChangeState"),
|
||||
std::make_pair(node_get_available_states_topic, "lifecycle_msgs/srv/GetAvailableStates"),
|
||||
std::make_pair(
|
||||
node_get_available_transitions_topic, "lifecycle_msgs/srv/GetAvailableTransitions"),
|
||||
std::make_pair(node_get_transition_graph_topic, "lifecycle_msgs/srv/GetAvailableTransitions"),
|
||||
};
|
||||
for (const auto & [service_name, service_type] : services) {
|
||||
ASSERT_TRUE(service_names_and_types.find(service_name) != service_names_and_types.end())
|
||||
<< service_name;
|
||||
const auto service_types = service_names_and_types.at(service_name);
|
||||
EXPECT_TRUE(
|
||||
std::find(service_types.cbegin(), service_types.cend(), service_type) != service_types.cend())
|
||||
<< service_name;
|
||||
}
|
||||
EXPECT_EQ(service_names_and_types1.size(), service_names_and_types2.size());
|
||||
}
|
||||
|
||||
TEST_F(TestLifecycleServiceClient, declare_parameter_with_no_initial_values)
|
||||
|
||||
@@ -34,6 +34,11 @@ protected:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
class CustomLifecycleNode : public rclcpp_lifecycle::LifecycleNode
|
||||
|
||||
@@ -34,6 +34,11 @@ protected:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestStateMachineInfo, available_states) {
|
||||
|
||||
Reference in New Issue
Block a user