Compare commits

...

28 Commits

Author SHA1 Message Date
Chris Lalancette
eaf6edd6b2 20.0.0 2023-04-13 19:48:07 +00:00
Chris Lalancette
d478525778 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-04-13 19:48:00 +00:00
ymski
82a693e028 applied tracepoints for ring_buffer (#2091)
applied tracepoints for intra_publish
add tracepoints for linking buffer and subscription

Signed-off-by: Kodai Yamasaki <114902604+ymski@users.noreply.github.com>
2023-04-13 15:44:02 -04:00
methylDragon
b8173e28c6 Dynamic Subscription (REP-2011 Subset): Stubs for rclcpp (#2165)
* Implement subscription base changes

* Add stubbed out classes

Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-13 10:32:33 -04:00
Emerson Knapp
3088b536cc Add type_hash to cpp TopicEndpointInfo (#2137)
* Add type_hash to cpp TopicEndpointInfo

Signed-off-by: Emerson Knapp <emerson.b.knapp@gmail.com>
2023-04-12 08:57:57 -04:00
Michael Carroll
5f9695afb0 Trigger the intraprocess guard condition with data (#2164)
If the intraprocess buffer still has data after taking, re-trigger the
guard condition to ensure that the executor will continue to service it,
even if incoming publications stop.


Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 19:20:21 -05:00
Yadu
71a06985af Minor grammar fix (#2149)
Signed-off-by: Yadunund <yadunund@openrobotics.org>
2023-04-03 09:53:49 -07:00
Christopher Wecht
73d555b402 Fix unnecessary allocations in executor.cpp (#2135)
Signed-off-by: Christopher Wecht <cwecht@mailbox.org>
2023-04-03 09:01:39 -05:00
Tomoya Fujita
a5368e6fe4 add Logger::get_effective_level(). (#2141)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-03-29 21:50:17 -07:00
Emerson Knapp
20e9cd17f6 Remove deprecated header (#2139)
Signed-off-by: Emerson Knapp <emerson.b.knapp@gmail.com>
2023-03-26 08:45:36 -04:00
Barry Xu
cb08c79a0a Implement matched event (#2105)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2023-03-22 08:36:47 -05:00
Tomoya Fujita
bff59925de extract the result response before the callback is issued. (#2132)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-03-21 14:11:48 -07:00
Tomoya Fujita
1a796b5515 use allocator via init_options argument. (#2129)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-03-17 12:35:34 -07:00
Chris Lalancette
cbd48c0eb4 Fixes to silence some clang warnings. (#2127)
This does 2 separate things:

* Adds (void)unused_variable things where needed.
* Stops doing some checks on moved-from items in tests.

With this in place, most of the remaining clang static analysis
warnings are gone.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2023-03-14 15:38:20 -04:00
Michael Carroll
18dd05fba5 Documentation improvements on the executor (#2125)
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-03-13 17:23:43 -05:00
Barry Xu
232262c02a Avoid losing waitable handles while using MultiThreadedExecutor (#2109)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2023-03-13 10:10:24 -07:00
Chris Lalancette
6c4afb3a70 Hook up the incompatible type event inside of rclcpp (#2069)
* Rename QOSEventHandler* to EventHandler.

We are going to be using it for more than just QOS events, so
rename it to just EventHandler and EventHandlerBase for now.
Leave the old names in place but deprecated.

* Rename qos_event.hpp -> event_handler.hpp
* Revamp incompatible_qos callback setting.
* Add in incompatible type implementation.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2023-03-13 09:33:30 -04:00
Chris Lalancette
b6a803f48c Update all rclcpp packages to C++17. (#2121)
The main reason to do this is so that we can compile rclcpp
with the clang static analyzer.  As of clang++-14 (what is in
Ubuntu 22.04), the default still seems to be C++14, so we need
to specify C++17 so that new things in the rclcpp headers work
properly.

Further, due to reasons I don't fully understand, I needed to
set CMAKE_CXX_STANDARD_REQUIRED in order for clang to really use
that version.  So set this as well.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2023-03-07 14:43:58 -05:00
Chris Lalancette
dbe555a3c3 Use the correct macro for LifecycleNode::get_fully_qualified_name (#2117)
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-03-06 12:45:24 -05:00
mauropasse
1a9b117d53 Fix clang warning: bugprone-use-after-move (#2116)
Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
Co-authored-by: Mauro Passerino <mpasserino@irobot.com>
2023-03-05 12:53:14 -05:00
Steve Macenski
11778f5048 add get_fully_qualified_name to rclcpp_lifecycle (#2115)
Signed-off-by: stevemacenski <stevenmacenski@gmail.com>
2023-03-04 16:13:56 -05:00
Nathan Wiebe Neufeldt
399f4df739 Fix the GoalUUID to_string representation (#1999)
* Fix expected results of the goal_uuid_to_string test

Signed-off-by: Nathan Wiebe Neufeldt <nwiebeneufeldt@clearpath.ai>
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
2023-03-02 07:45:22 -05:00
Chris Lalancette
e7890b7c62 19.3.0 2023-03-01 14:27:21 +00:00
Chris Lalancette
b589b490c3 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-03-01 14:27:01 +00:00
Christophe Bedard
72c05ecee0 Fix memory leak in tracetools::get_symbol() (#2104)
Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2023-02-28 16:42:39 -06:00
Brian
968ce0a03f Service introspection (#1985)
* Implementation of service introspection.

To do this, we add a new method on the Client and
Service classes that allows the user to change the
introspection method at runtime.  These end up calling
into the rcl layer to do the actual configuration,
at which point service introspection messages will be
sent as configured.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
Signed-off-by: Brian Chen <brian.chen@openrobotics.org>
2023-02-28 13:43:39 -05:00
Miguel Company
3062dec77e Allow publishing borrowed messages with intra-process enabled (#2108)
Signed-off-by: Miguel Company <MiguelCompany@eprosima.com>
2023-02-24 14:48:23 -08:00
Chen Lihui
9ea55ba620 to fix flaky test about TestTimeSource.callbacks (#2111)
Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2023-02-24 15:16:02 -05:00
80 changed files with 2709 additions and 566 deletions

View File

@@ -2,6 +2,34 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20.0.0 (2023-04-13)
-------------------
* applied tracepoints for ring_buffer (`#2091 <https://github.com/ros2/rclcpp/issues/2091>`_)
* Dynamic Subscription (REP-2011 Subset): Stubs for rclcpp (`#2165 <https://github.com/ros2/rclcpp/issues/2165>`_)
* Add type_hash to cpp TopicEndpointInfo (`#2137 <https://github.com/ros2/rclcpp/issues/2137>`_)
* Trigger the intraprocess guard condition with data (`#2164 <https://github.com/ros2/rclcpp/issues/2164>`_)
* Minor grammar fix (`#2149 <https://github.com/ros2/rclcpp/issues/2149>`_)
* Fix unnecessary allocations in executor.cpp (`#2135 <https://github.com/ros2/rclcpp/issues/2135>`_)
* add Logger::get_effective_level(). (`#2141 <https://github.com/ros2/rclcpp/issues/2141>`_)
* Remove deprecated header (`#2139 <https://github.com/ros2/rclcpp/issues/2139>`_)
* Implement matched event (`#2105 <https://github.com/ros2/rclcpp/issues/2105>`_)
* use allocator via init_options argument. (`#2129 <https://github.com/ros2/rclcpp/issues/2129>`_)
* Fixes to silence some clang warnings. (`#2127 <https://github.com/ros2/rclcpp/issues/2127>`_)
* Documentation improvements on the executor (`#2125 <https://github.com/ros2/rclcpp/issues/2125>`_)
* Avoid losing waitable handles while using MultiThreadedExecutor (`#2109 <https://github.com/ros2/rclcpp/issues/2109>`_)
* Hook up the incompatible type event inside of rclcpp (`#2069 <https://github.com/ros2/rclcpp/issues/2069>`_)
* Update all rclcpp packages to C++17. (`#2121 <https://github.com/ros2/rclcpp/issues/2121>`_)
* Fix clang warning: bugprone-use-after-move (`#2116 <https://github.com/ros2/rclcpp/issues/2116>`_)
* Contributors: Barry Xu, Chris Lalancette, Christopher Wecht, Emerson Knapp, Michael Carroll, Tomoya Fujita, Yadu, mauropasse, methylDragon, ymski
19.3.0 (2023-03-01)
-------------------
* Fix memory leak in tracetools::get_symbol() (`#2104 <https://github.com/ros2/rclcpp/issues/2104>`_)
* Service introspection (`#1985 <https://github.com/ros2/rclcpp/issues/1985>`_)
* Allow publishing borrowed messages with intra-process enabled (`#2108 <https://github.com/ros2/rclcpp/issues/2108>`_)
* to fix flaky test about TestTimeSource.callbacks (`#2111 <https://github.com/ros2/rclcpp/issues/2111>`_)
* Contributors: Brian, Chen Lihui, Christophe Bedard, Miguel Company
19.2.0 (2023-02-24)
-------------------
* to create a sublogger while getting child of Logger (`#1717 <https://github.com/ros2/rclcpp/issues/1717>`_)

View File

@@ -25,6 +25,7 @@ find_package(tracetools REQUIRED)
# Default to C++17
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# About -Wno-sign-conversion: With Clang, -Wconversion implies -Wsign-conversion. There are a number of
@@ -48,6 +49,11 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/detail/rmw_implementation_specific_subscription_payload.cpp
src/rclcpp/detail/utilities.cpp
src/rclcpp/duration.cpp
src/rclcpp/dynamic_typesupport/dynamic_message.cpp
src/rclcpp/dynamic_typesupport/dynamic_message_type.cpp
src/rclcpp/dynamic_typesupport/dynamic_message_type_builder.cpp
src/rclcpp/dynamic_typesupport/dynamic_message_type_support.cpp
src/rclcpp/dynamic_typesupport/dynamic_serialization_support.cpp
src/rclcpp/event.cpp
src/rclcpp/exceptions/exceptions.cpp
src/rclcpp/executable_list.cpp
@@ -92,7 +98,7 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/parameter_value.cpp
src/rclcpp/publisher_base.cpp
src/rclcpp/qos.cpp
src/rclcpp/qos_event.cpp
src/rclcpp/event_handler.cpp
src/rclcpp/qos_overriding_options.cpp
src/rclcpp/serialization.cpp
src/rclcpp/serialized_message.cpp

View File

@@ -191,10 +191,14 @@ public:
#ifndef TRACETOOLS_DISABLED
std::visit(
[this](auto && arg) {
TRACEPOINT(
rclcpp_callback_register,
static_cast<const void *>(this),
tracetools::get_symbol(arg));
if (TRACEPOINT_ENABLED(rclcpp_callback_register)) {
char * symbol = tracetools::get_symbol(arg);
DO_TRACEPOINT(
rclcpp_callback_register,
static_cast<const void *>(this),
symbol);
std::free(symbol);
}
}, callback_);
#endif // TRACETOOLS_DISABLED
}

View File

@@ -965,10 +965,14 @@ public:
#ifndef TRACETOOLS_DISABLED
std::visit(
[this](auto && callback) {
TRACEPOINT(
rclcpp_callback_register,
static_cast<const void *>(this),
tracetools::get_symbol(callback));
if (TRACEPOINT_ENABLED(rclcpp_callback_register)) {
char * symbol = tracetools::get_symbol(callback);
DO_TRACEPOINT(
rclcpp_callback_register,
static_cast<const void *>(this),
symbol);
std::free(symbol);
}
}, callback_variant_);
#endif // TRACETOOLS_DISABLED
}

View File

@@ -16,14 +16,15 @@
#define RCLCPP__CLIENT_HPP_
#include <atomic>
#include <functional>
#include <future>
#include <unordered_map>
#include <memory>
#include <mutex>
#include <optional> // NOLINT, cpplint doesn't think this is a cpp std header
#include <sstream>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <variant> // NOLINT
#include <vector>
@@ -31,8 +32,10 @@
#include "rcl/client.h"
#include "rcl/error_handling.h"
#include "rcl/event_callback.h"
#include "rcl/service_introspection.h"
#include "rcl/wait.h"
#include "rclcpp/clock.hpp"
#include "rclcpp/detail/cpp_callback_trampoline.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
@@ -467,15 +470,13 @@ public:
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
const std::string & service_name,
rcl_client_options_t & client_options)
: ClientBase(node_base, node_graph)
: ClientBase(node_base, node_graph),
srv_type_support_handle_(rosidl_typesupport_cpp::get_service_type_support_handle<ServiceT>())
{
using rosidl_typesupport_cpp::get_service_type_support_handle;
auto service_type_support_handle =
get_service_type_support_handle<ServiceT>();
rcl_ret_t ret = rcl_client_init(
this->get_client_handle().get(),
this->get_rcl_node_handle(),
service_type_support_handle,
srv_type_support_handle_,
service_name.c_str(),
&client_options);
if (ret != RCL_RET_OK) {
@@ -781,6 +782,33 @@ public:
return old_size - pending_requests_.size();
}
/// Configure client introspection.
/**
* \param[in] clock clock to use to generate introspection timestamps
* \param[in] qos_service_event_pub QoS settings to use when creating the introspection publisher
* \param[in] introspection_state the state to set introspection to
*/
void
configure_introspection(
Clock::SharedPtr clock, const QoS & qos_service_event_pub,
rcl_service_introspection_state_t introspection_state)
{
rcl_publisher_options_t pub_opts = rcl_publisher_get_default_options();
pub_opts.qos = qos_service_event_pub.get_rmw_qos_profile();
rcl_ret_t ret = rcl_client_configure_service_introspection(
client_handle_.get(),
node_handle_.get(),
clock->get_clock_handle(),
srv_type_support_handle_,
pub_opts,
introspection_state);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to configure client introspection");
}
}
protected:
using CallbackTypeValueVariant = std::tuple<CallbackType, SharedFuture, Promise>;
using CallbackWithRequestTypeValueVariant = std::tuple<
@@ -831,6 +859,9 @@ protected:
CallbackInfoVariant>>
pending_requests_;
std::mutex pending_requests_mutex_;
private:
const rosidl_service_type_support_t * srv_type_support_handle_;
};
} // namespace rclcpp

View File

@@ -0,0 +1,70 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_HPP_
#define RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_HPP_
#include <rcl/allocator.h>
#include <rcl/types.h>
#include <rosidl_dynamic_typesupport/types.h>
#include <memory>
#include <string>
#include "rclcpp/dynamic_typesupport/dynamic_message_type.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace dynamic_typesupport
{
/// Utility wrapper class for rosidl_dynamic_typesupport_dynamic_data_t
/// STUBBED OUT
class DynamicMessage : public std::enable_shared_from_this<DynamicMessage>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessage)
RCLCPP_PUBLIC
virtual ~DynamicMessage();
protected:
// NOTE(methylDragon):
// This is just here to extend the lifetime of the serialization support
// It isn't actually used by the builder since the builder should compose its own support
//
// ... Though ideally it should be the exact same support as the one stored in the
// DynamicSerializationSupport
DynamicSerializationSupport::SharedPtr serialization_support_;
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data_;
bool is_loaned_;
// Used for returning the loaned value, and lifetime management
DynamicMessage::SharedPtr parent_data_;
private:
RCLCPP_DISABLE_COPY(DynamicMessage)
RCLCPP_PUBLIC
DynamicMessage();
};
} // namespace dynamic_typesupport
} // namespace rclcpp
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_HPP_

View File

@@ -0,0 +1,64 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_HPP_
#define RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_HPP_
#include <rcl/allocator.h>
#include <rosidl_dynamic_typesupport/types.h>
#include <memory>
#include <string>
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace dynamic_typesupport
{
/// Utility wrapper class for `rosidl_dynamic_typesupport_dynamic_type_t`
/// STUBBED OUT
class DynamicMessageType : public std::enable_shared_from_this<DynamicMessageType>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessageType)
RCLCPP_PUBLIC
virtual ~DynamicMessageType();
protected:
// NOTE(methylDragon):
// This is just here to extend the lifetime of the serialization support
// It isn't actually used by the builder since the builder should compose its own support
//
// ... Though ideally it should be the exact same support as the one stored in the
// `DynamicSerializationSupport`
DynamicSerializationSupport::SharedPtr serialization_support_;
rosidl_dynamic_typesupport_dynamic_type_t rosidl_dynamic_type_;
private:
RCLCPP_DISABLE_COPY(DynamicMessageType)
RCLCPP_PUBLIC
DynamicMessageType();
};
} // namespace dynamic_typesupport
} // namespace rclcpp
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_HPP_

View File

@@ -0,0 +1,65 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_BUILDER_HPP_
#define RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_BUILDER_HPP_
#include <rcl/allocator.h>
#include <rosidl_dynamic_typesupport/types.h>
#include <memory>
#include <string>
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace dynamic_typesupport
{
/// Utility wrapper class for `rosidl_dynamic_typesupport_dynamic_type_builder_t *`
/// STUBBED OUT
class DynamicMessageTypeBuilder : public std::enable_shared_from_this<DynamicMessageTypeBuilder>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessageTypeBuilder)
RCLCPP_PUBLIC
virtual ~DynamicMessageTypeBuilder();
protected:
// NOTE(methylDragon):
// This is just here to extend the lifetime of the serialization support
// It isn't actually used by the builder since the builder should compose its own support
//
// ... Though ideally it should be the exact same support as the one stored in the
// `DynamicSerializationSupport`
DynamicSerializationSupport::SharedPtr serialization_support_;
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder_;
private:
RCLCPP_DISABLE_COPY(DynamicMessageTypeBuilder)
RCLCPP_PUBLIC
DynamicMessageTypeBuilder();
};
} // namespace dynamic_typesupport
} // namespace rclcpp
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_BUILDER_HPP_

View File

@@ -0,0 +1,67 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_SUPPORT_HPP_
#define RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_SUPPORT_HPP_
#include <rcl/allocator.h>
#include <rosidl_dynamic_typesupport/dynamic_message_type_support_struct.h>
#include <rosidl_dynamic_typesupport/types.h>
#include <rosidl_runtime_c/message_type_support_struct.h>
#include <rosidl_runtime_c/type_description/type_description__struct.h>
#include <memory>
#include <string>
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message_type.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace dynamic_typesupport
{
/// Utility wrapper class for `rosidl_message_type_support_t` containing managed
/// STUBBED OUT
class DynamicMessageTypeSupport : public std::enable_shared_from_this<DynamicMessageTypeSupport>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessageTypeSupport)
RCLCPP_PUBLIC
virtual ~DynamicMessageTypeSupport();
protected:
DynamicSerializationSupport::SharedPtr serialization_support_;
DynamicMessageType::SharedPtr dynamic_message_type_;
DynamicMessage::SharedPtr dynamic_message_;
rosidl_message_type_support_t rosidl_message_type_support_;
private:
RCLCPP_DISABLE_COPY(DynamicMessageTypeSupport)
RCLCPP_PUBLIC
DynamicMessageTypeSupport();
};
} // namespace dynamic_typesupport
} // namespace rclcpp
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_SUPPORT_HPP_

View File

@@ -0,0 +1,60 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_SERIALIZATION_SUPPORT_HPP_
#define RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_SERIALIZATION_SUPPORT_HPP_
#include <rcl/allocator.h>
#include <rosidl_dynamic_typesupport/api/serialization_support.h>
#include <rosidl_dynamic_typesupport/types.h>
#include <memory>
#include <string>
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace dynamic_typesupport
{
/// Utility wrapper class for rosidl_dynamic_typesupport_serialization_support_t
class DynamicSerializationSupport : public std::enable_shared_from_this<DynamicSerializationSupport>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicSerializationSupport)
RCLCPP_PUBLIC
explicit DynamicSerializationSupport(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicSerializationSupport(
const std::string & serialization_library_name,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
virtual ~DynamicSerializationSupport();
protected:
rosidl_dynamic_typesupport_serialization_support_t rosidl_serialization_support_;
private:
RCLCPP_DISABLE_COPY(DynamicSerializationSupport)
};
} // namespace dynamic_typesupport
} // namespace rclcpp
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_SERIALIZATION_SUPPORT_HPP_

View File

@@ -0,0 +1,311 @@
// Copyright 2019 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.
#ifndef RCLCPP__EVENT_HANDLER_HPP_
#define RCLCPP__EVENT_HANDLER_HPP_
#include <functional>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <string>
#include "rcl/error_handling.h"
#include "rcl/event_callback.h"
#include "rmw/impl/cpp/demangle.hpp"
#include "rmw/incompatible_qos_events_statuses.h"
#include "rmw/events_statuses/incompatible_type.h"
#include "rcutils/logging_macros.h"
#include "rclcpp/detail/cpp_callback_trampoline.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/function_traits.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/waitable.hpp"
namespace rclcpp
{
using QOSDeadlineRequestedInfo = rmw_requested_deadline_missed_status_t;
using QOSDeadlineOfferedInfo = rmw_offered_deadline_missed_status_t;
using QOSLivelinessChangedInfo = rmw_liveliness_changed_status_t;
using QOSLivelinessLostInfo = rmw_liveliness_lost_status_t;
using QOSMessageLostInfo = rmw_message_lost_status_t;
using QOSOfferedIncompatibleQoSInfo = rmw_offered_qos_incompatible_event_status_t;
using QOSRequestedIncompatibleQoSInfo = rmw_requested_qos_incompatible_event_status_t;
using IncompatibleTypeInfo = rmw_incompatible_type_status_t;
using MatchedInfo = rmw_matched_status_t;
using QOSDeadlineRequestedCallbackType = std::function<void (QOSDeadlineRequestedInfo &)>;
using QOSDeadlineOfferedCallbackType = std::function<void (QOSDeadlineOfferedInfo &)>;
using QOSLivelinessChangedCallbackType = std::function<void (QOSLivelinessChangedInfo &)>;
using QOSLivelinessLostCallbackType = std::function<void (QOSLivelinessLostInfo &)>;
using QOSMessageLostCallbackType = std::function<void (QOSMessageLostInfo &)>;
using QOSOfferedIncompatibleQoSCallbackType = std::function<void (QOSOfferedIncompatibleQoSInfo &)>;
using QOSRequestedIncompatibleQoSCallbackType =
std::function<void (QOSRequestedIncompatibleQoSInfo &)>;
using IncompatibleTypeCallbackType = std::function<void (IncompatibleTypeInfo &)>;
using PublisherMatchedCallbackType = std::function<void (MatchedInfo &)>;
using SubscriptionMatchedCallbackType = std::function<void (MatchedInfo &)>;
/// Contains callbacks for various types of events a Publisher can receive from the middleware.
struct PublisherEventCallbacks
{
QOSDeadlineOfferedCallbackType deadline_callback;
QOSLivelinessLostCallbackType liveliness_callback;
QOSOfferedIncompatibleQoSCallbackType incompatible_qos_callback;
IncompatibleTypeCallbackType incompatible_type_callback;
PublisherMatchedCallbackType matched_callback;
};
/// Contains callbacks for non-message events that a Subscription can receive from the middleware.
struct SubscriptionEventCallbacks
{
QOSDeadlineRequestedCallbackType deadline_callback;
QOSLivelinessChangedCallbackType liveliness_callback;
QOSRequestedIncompatibleQoSCallbackType incompatible_qos_callback;
QOSMessageLostCallbackType message_lost_callback;
IncompatibleTypeCallbackType incompatible_type_callback;
SubscriptionMatchedCallbackType matched_callback;
};
class UnsupportedEventTypeException : public exceptions::RCLErrorBase, public std::runtime_error
{
public:
RCLCPP_PUBLIC
UnsupportedEventTypeException(
rcl_ret_t ret,
const rcl_error_state_t * error_state,
const std::string & prefix);
RCLCPP_PUBLIC
UnsupportedEventTypeException(
const exceptions::RCLErrorBase & base_exc,
const std::string & prefix);
};
class EventHandlerBase : public Waitable
{
public:
enum class EntityType : std::size_t
{
Event,
};
RCLCPP_PUBLIC
virtual ~EventHandlerBase();
/// Get the number of ready events
RCLCPP_PUBLIC
size_t
get_number_of_ready_events() override;
/// Add the Waitable to a wait set.
RCLCPP_PUBLIC
void
add_to_wait_set(rcl_wait_set_t * wait_set) override;
/// Check if the Waitable is ready.
RCLCPP_PUBLIC
bool
is_ready(rcl_wait_set_t * wait_set) override;
/// Set a callback to be called when each new event instance occurs.
/**
* The callback receives a size_t which is the number of events that occurred
* since the last time this callback was called.
* Normally this is 1, but can be > 1 if events occurred before any
* callback was set.
*
* The callback also receives an int identifier argument.
* This is needed because a Waitable may be composed of several distinct entities,
* such as subscriptions, services, etc.
* The application should provide a generic callback function that will be then
* forwarded by the waitable to all of its entities.
* Before forwarding, a different value for the identifier argument will be
* bond to the function.
* This implies that the provided callback can use the identifier to behave
* differently depending on which entity triggered the waitable to become ready.
*
* Since this callback is called from the middleware, you should aim to make
* it fast and not blocking.
* If you need to do a lot of work or wait for some other event, you should
* spin it off to another thread, otherwise you risk blocking the middleware.
*
* Calling it again will clear any previously set callback.
*
* An exception will be thrown if the callback is not callable.
*
* This function is thread-safe.
*
* If you want more information available in the callback, like the qos event
* or other information, you may use a lambda with captures or std::bind.
*
* \sa rmw_event_set_callback
* \sa rcl_event_set_callback
*
* \param[in] callback functor to be called when a new event occurs
*/
void
set_on_ready_callback(std::function<void(size_t, int)> callback) override
{
if (!callback) {
throw std::invalid_argument(
"The callback passed to set_on_ready_callback "
"is not callable.");
}
// Note: we bind the int identifier argument to this waitable's entity types
auto new_callback =
[callback, this](size_t number_of_events) {
try {
callback(number_of_events, static_cast<int>(EntityType::Event));
} catch (const std::exception & exception) {
RCLCPP_ERROR_STREAM(
// TODO(wjwwood): get this class access to the node logger it is associated with
rclcpp::get_logger("rclcpp"),
"rclcpp::EventHandlerBase@" << this <<
" caught " << rmw::impl::cpp::demangle(exception) <<
" exception in user-provided callback for the 'on ready' callback: " <<
exception.what());
} catch (...) {
RCLCPP_ERROR_STREAM(
rclcpp::get_logger("rclcpp"),
"rclcpp::EventHandlerBase@" << this <<
" caught unhandled exception in user-provided callback " <<
"for the 'on ready' callback");
}
};
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
// Set it temporarily to the new callback, while we replace the old one.
// This two-step setting, prevents a gap where the old std::function has
// been replaced but the middleware hasn't been told about the new one yet.
set_on_new_event_callback(
rclcpp::detail::cpp_callback_trampoline<decltype(new_callback), const void *, size_t>,
static_cast<const void *>(&new_callback));
// Store the std::function to keep it in scope, also overwrites the existing one.
on_new_event_callback_ = new_callback;
// Set it again, now using the permanent storage.
set_on_new_event_callback(
rclcpp::detail::cpp_callback_trampoline<
decltype(on_new_event_callback_), const void *, size_t>,
static_cast<const void *>(&on_new_event_callback_));
}
/// Unset the callback registered for new events, if any.
void
clear_on_ready_callback() override
{
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
if (on_new_event_callback_) {
set_on_new_event_callback(nullptr, nullptr);
on_new_event_callback_ = nullptr;
}
}
protected:
RCLCPP_PUBLIC
void
set_on_new_event_callback(rcl_event_callback_t callback, const void * user_data);
std::recursive_mutex callback_mutex_;
std::function<void(size_t)> on_new_event_callback_{nullptr};
rcl_event_t event_handle_;
size_t wait_set_event_index_;
};
using QOSEventHandlerBase [[deprecated("Use rclcpp::EventHandlerBase")]] = EventHandlerBase;
template<typename EventCallbackT, typename ParentHandleT>
class EventHandler : public EventHandlerBase
{
public:
template<typename InitFuncT, typename EventTypeEnum>
EventHandler(
const EventCallbackT & callback,
InitFuncT init_func,
ParentHandleT parent_handle,
EventTypeEnum event_type)
: parent_handle_(parent_handle), event_callback_(callback)
{
event_handle_ = rcl_get_zero_initialized_event();
rcl_ret_t ret = init_func(&event_handle_, parent_handle.get(), event_type);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_UNSUPPORTED) {
UnsupportedEventTypeException exc(ret, rcl_get_error_state(), "Failed to initialize event");
rcl_reset_error();
throw exc;
} else {
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to initialize event");
}
}
}
/// Take data so that the callback cannot be scheduled again
std::shared_ptr<void>
take_data() override
{
EventCallbackInfoT callback_info;
rcl_ret_t ret = rcl_take_event(&event_handle_, &callback_info);
if (ret != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't take event info: %s", rcl_get_error_string().str);
return nullptr;
}
return std::static_pointer_cast<void>(std::make_shared<EventCallbackInfoT>(callback_info));
}
std::shared_ptr<void>
take_data_by_entity_id(size_t id) override
{
(void)id;
return take_data();
}
/// Execute any entities of the Waitable that are ready.
void
execute(std::shared_ptr<void> & data) override
{
if (!data) {
throw std::runtime_error("'data' is empty");
}
auto callback_ptr = std::static_pointer_cast<EventCallbackInfoT>(data);
event_callback_(*callback_ptr);
callback_ptr.reset();
}
private:
using EventCallbackInfoT = typename std::remove_reference<typename
rclcpp::function_traits::function_traits<EventCallbackT>::template argument_type<0>>::type;
ParentHandleT parent_handle_;
EventCallbackT event_callback_;
};
template<typename EventCallbackT, typename ParentHandleT>
using QOSEventHandler [[deprecated("Use rclcpp::EventHandler")]] = EventHandler<EventCallbackT,
ParentHandleT>;
} // namespace rclcpp
#endif // RCLCPP__EVENT_HANDLER_HPP_

View File

@@ -315,6 +315,16 @@ public:
virtual void
spin_all(std::chrono::nanoseconds max_duration);
/// Collect work once and execute the next available work, optionally within a duration.
/**
* This function can be overridden. The default implementation is suitable for
* a single-thread model of execution.
* Adding subscriptions, timers, services, etc. with blocking callbacks will cause this function
* to block (which may have unintended consequences).
* \param[in] timeout The maximum amount of time to spend waiting for work.
* `-1` is potentially block forever waiting for work.
*/
RCLCPP_PUBLIC
virtual void
spin_once(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
@@ -413,12 +423,29 @@ public:
is_spinning();
protected:
/// Add a node to executor, execute the next available unit of work, and remove the node.
/**
* Implementation of spin_node_once using std::chrono::nanoseconds
* \param[in] node Shared pointer to the node to add.
* \param[in] timeout How long to wait for work to become available. Negative values cause
* spin_node_once to block indefinitely (the default behavior). A timeout of 0 causes this
* function to be non-blocking.
*/
RCLCPP_PUBLIC
void
spin_node_once_nanoseconds(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
std::chrono::nanoseconds timeout);
/// Collect work and execute available work, optionally within a duration.
/**
* Implementation of spin_some and spin_all.
* The exhaustive flag controls if the function will re-collect available work within the duration.
*
* \param[in] max_duration The maximum amount of time to spend executing work, or 0 for no limit.
* \param[in] exhaustive when set to true, continue to collect work and execute (spin_all)
* when set to false, return when all collected work is executed (spin_some)
*/
RCLCPP_PUBLIC
void
spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive);
@@ -433,30 +460,60 @@ protected:
void
execute_any_executable(AnyExecutable & any_exec);
/// Run subscription executable.
/**
* Do necessary setup and tear-down as well as executing the subscription.
* \param[in] subscription Subscription to execute
*/
RCLCPP_PUBLIC
static void
execute_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription);
/// Run timer executable.
/**
* Do necessary setup and tear-down as well as executing the timer callback.
* \param[in] timer Timer to execute
*/
RCLCPP_PUBLIC
static void
execute_timer(rclcpp::TimerBase::SharedPtr timer);
/// Run service server executable.
/**
* Do necessary setup and tear-down as well as executing the service server callback.
* \param[in] service Service to execute
*/
RCLCPP_PUBLIC
static void
execute_service(rclcpp::ServiceBase::SharedPtr service);
/// Run service client executable.
/**
* Do necessary setup and tear-down as well as executing the service client callback.
* \param[in] service Service to execute
*/
RCLCPP_PUBLIC
static void
execute_client(rclcpp::ClientBase::SharedPtr client);
/// Block until more work becomes avilable or timeout is reached.
/**
* Builds a set of waitable entities, which are passed to the middleware.
* After building wait set, waits on middleware to notify.
* \param[in] timeout duration to wait for new work to become available.
* \throws std::runtime_error if the wait set can be cleared
*/
RCLCPP_PUBLIC
void
wait_for_work(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
/// Find node associated with a callback group
/**
* \param[in] weak_groups_to_nodes map of callback groups to nodes
* \param[in] group callback group to find assocatiated node
* \return Pointer to associated node if found, else nullptr
*/
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_by_group(
@@ -475,6 +532,11 @@ protected:
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) const;
/// Find the callback group associated with a timer
/**
* \param[in] timer Timer to find associated callback group
* \return Pointer to callback group node if found, else nullptr
*/
RCLCPP_PUBLIC
rclcpp::CallbackGroup::SharedPtr
get_group_by_timer(rclcpp::TimerBase::SharedPtr timer);
@@ -502,16 +564,54 @@ protected:
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
bool notify = true) RCPPUTILS_TSA_REQUIRES(mutex_);
/// Check for executable in ready state and populate union structure.
/**
* \param[out] any_executable populated union structure of ready executable
* \return true if an executable was ready and any_executable was populated,
* otherwise false
*/
RCLCPP_PUBLIC
bool
get_next_ready_executable(AnyExecutable & any_executable);
/// Check for executable in ready state and populate union structure.
/**
* This is the implementation of get_next_ready_executable that takes into
* account the current state of callback groups' association with nodes and
* executors.
*
* This checks in a particular order for available work:
* * Timers
* * Subscriptions
* * Services
* * Clients
* * Waitable
*
* If the next executable is not associated with this executor/node pair,
* then this method will return false.
*
* \param[out] any_executable populated union structure of ready executable
* \param[in] weak_groups_to_nodes mapping of callback groups to nodes
* \return true if an executable was ready and any_executable was populated,
* otherwise false
*/
RCLCPP_PUBLIC
bool
get_next_ready_executable_from_map(
AnyExecutable & any_executable,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
/// Wait for executable in ready state and populate union structure.
/**
* If an executable is ready, it will return immediately, otherwise
* block based on the timeout for work to become ready.
*
* \param[out] any_executable populated union structure of ready executable
* \param[in] timeout duration of time to wait for work, a negative value
* (the defualt behavior), will make this function block indefinitely
* \return true if an executable was ready and any_executable was populated,
* otherwise false
*/
RCLCPP_PUBLIC
bool
get_next_executable(

View File

@@ -24,6 +24,7 @@
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/experimental/buffers/buffer_implementation_base.hpp"
#include "rclcpp/macros.hpp"
#include "tracetools/tracetools.h"
namespace rclcpp
{
@@ -94,6 +95,10 @@ public:
buffer_ = std::move(buffer_impl);
TRACEPOINT(
rclcpp_buffer_to_ipb,
static_cast<const void *>(buffer_.get()),
static_cast<const void *>(this));
if (!allocator) {
message_allocator_ = std::make_shared<MessageAlloc>();
} else {

View File

@@ -25,6 +25,7 @@
#include "rclcpp/logging.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
#include "tracetools/tracetools.h"
namespace rclcpp
{
@@ -51,6 +52,7 @@ public:
if (capacity == 0) {
throw std::invalid_argument("capacity must be a positive, non-zero value");
}
TRACEPOINT(rclcpp_construct_ring_buffer, static_cast<const void *>(this), capacity_);
}
virtual ~RingBufferImplementation() {}
@@ -67,6 +69,12 @@ public:
write_index_ = next_(write_index_);
ring_buffer_[write_index_] = std::move(request);
TRACEPOINT(
rclcpp_ring_buffer_enqueue,
static_cast<const void *>(this),
write_index_,
size_ + 1,
is_full_());
if (is_full_()) {
read_index_ = next_(read_index_);
@@ -90,6 +98,11 @@ public:
}
auto request = std::move(ring_buffer_[read_index_]);
TRACEPOINT(
rclcpp_ring_buffer_dequeue,
static_cast<const void *>(this),
read_index_,
size_ - 1);
read_index_ = next_(read_index_);
size_--;
@@ -135,7 +148,10 @@ public:
return is_full_();
}
void clear() {}
void clear()
{
TRACEPOINT(rclcpp_ring_buffer_clear, static_cast<const void *>(this));
}
private:
/// Get the next index value for the ring buffer

View File

@@ -454,6 +454,8 @@ private:
if (std::next(it) == subscription_ids.end()) {
// If this is the last subscription, give up ownership
subscription->provide_intra_process_data(std::move(message));
// Last message delivered, break from for loop
break;
} else {
// Copy the message since we have additional subscriptions to serve
Deleter deleter = message.get_deleter();
@@ -493,6 +495,8 @@ private:
if (std::next(it) == subscription_ids.end()) {
// If this is the last subscription, give up ownership
ros_message_subscription->provide_intra_process_message(std::move(message));
// Last message delivered, break from for loop
break;
} else {
// Copy the message since we have additional subscriptions to serve
Deleter deleter = message.get_deleter();

View File

@@ -118,6 +118,13 @@ public:
return nullptr;
}
}
if (this->buffer_->has_data()) {
// If there is data still to be processed, indicate to the
// executor or waitset by triggering the guard condition.
this->trigger_guard_condition();
}
return std::static_pointer_cast<void>(
std::make_shared<std::pair<ConstMessageSharedPtr, MessageUniquePtr>>(
std::pair<ConstMessageSharedPtr, MessageUniquePtr>(

View File

@@ -31,6 +31,8 @@
#include "rclcpp/qos.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "tracetools/tracetools.h"
namespace rclcpp
{
namespace experimental
@@ -91,6 +93,10 @@ public:
buffer_type,
qos_profile,
std::make_shared<Alloc>(subscribed_type_allocator_));
TRACEPOINT(
rclcpp_ipb_to_subscription,
static_cast<const void *>(buffer_.get()),
static_cast<const void *>(this));
}
bool

View File

@@ -84,7 +84,7 @@ public:
options.to_rcl_subscription_options(qos),
options.event_callbacks,
options.use_default_callbacks,
true),
SubscriptionType::SERIALIZED_MESSAGE),
callback_(callback),
ts_lib_(ts_lib)
{}
@@ -123,6 +123,31 @@ public:
RCLCPP_PUBLIC
void return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> & message) override;
// DYNAMIC TYPE ==================================================================================
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr get_shared_dynamic_message_type()
override;
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr get_shared_dynamic_message() override;
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support() override;
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr create_dynamic_message() override;
RCLCPP_PUBLIC
void return_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message) override;
RCLCPP_PUBLIC
void handle_dynamic_message(
const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
const rclcpp::MessageInfo & message_info) override;
private:
RCLCPP_DISABLE_COPY(GenericSubscription)

View File

@@ -170,6 +170,24 @@ public:
RCLCPP_PUBLIC
void
set_level(Level level);
/// Get effective level for current logger.
/**
* The effective level is determined as the severity level of
* the logger if it is set, otherwise it is the first specified severity
* level of the logger's ancestors, starting with its closest ancestor.
* The ancestor hierarchy is signified by logger names being separated by dots:
* a logger named `x` is an ancestor of `x.y`, and both `x` and `x.y` are
* ancestors of `x.y.z`, etc.
* If the level has not been set for the logger nor any of its
* ancestors, the default level is used.
*
* \throws rclcpp::exceptions::RCLError if any error happens.
* \return Level for the current logger.
*/
RCLCPP_PUBLIC
Level
get_effective_level() const;
};
} // namespace rclcpp

View File

@@ -57,7 +57,8 @@ public:
node_namespace_(info.node_namespace),
topic_type_(info.topic_type),
endpoint_type_(static_cast<rclcpp::EndpointType>(info.endpoint_type)),
qos_profile_({info.qos_profile.history, info.qos_profile.depth}, info.qos_profile)
qos_profile_({info.qos_profile.history, info.qos_profile.depth}, info.qos_profile),
topic_type_hash_(info.topic_type_hash)
{
std::copy(info.endpoint_gid, info.endpoint_gid + RMW_GID_STORAGE_SIZE, endpoint_gid_.begin());
}
@@ -122,6 +123,16 @@ public:
const rclcpp::QoS &
qos_profile() const;
/// Get a mutable reference to the type hash of the topic endpoint.
RCLCPP_PUBLIC
rosidl_type_hash_t &
topic_type_hash();
/// Get a const reference to the type hash of the topic endpoint.
RCLCPP_PUBLIC
const rosidl_type_hash_t &
topic_type_hash() const;
private:
std::string node_name_;
std::string node_namespace_;
@@ -129,6 +140,7 @@ private:
rclcpp::EndpointType endpoint_type_;
std::array<uint8_t, RMW_GID_STORAGE_SIZE> endpoint_gid_;
rclcpp::QoS qos_profile_;
rosidl_type_hash_t topic_type_hash_;
};
namespace node_interfaces

View File

@@ -381,10 +381,6 @@ public:
if (!loaned_msg.is_valid()) {
throw std::runtime_error("loaned message is not valid");
}
if (intra_process_is_enabled_) {
// TODO(Karsten1987): support loaned message passed by intraprocess
throw std::runtime_error("storing loaned messages in intra process is not supported yet");
}
// verify that publisher supports loaned messages
// TODO(Karsten1987): This case separation has to be done in rclcpp
@@ -398,7 +394,7 @@ public:
} else {
// we don't release the ownership, let the middleware copy the ros message
// and thus the destructor of rclcpp::LoanedMessage cleans up the memory.
this->do_inter_process_publish(loaned_msg.get());
this->publish(loaned_msg.get());
}
}
@@ -488,6 +484,10 @@ protected:
if (!msg) {
throw std::runtime_error("cannot publish msg which is a null pointer");
}
TRACEPOINT(
rclcpp_intra_publish,
static_cast<const void *>(publisher_handle_.get()),
msg.get());
ipm->template do_intra_process_publish<PublishedType, ROSMessageType, AllocatorT>(
intra_process_publisher_id_,
@@ -506,6 +506,10 @@ protected:
if (!msg) {
throw std::runtime_error("cannot publish msg which is a null pointer");
}
TRACEPOINT(
rclcpp_intra_publish,
static_cast<const void *>(publisher_handle_.get()),
msg.get());
ipm->template do_intra_process_publish<ROSMessageType, ROSMessageType, AllocatorT>(
intra_process_publisher_id_,
@@ -525,6 +529,10 @@ protected:
if (!msg) {
throw std::runtime_error("cannot publish msg which is a null pointer");
}
TRACEPOINT(
rclcpp_intra_publish,
static_cast<const void *>(publisher_handle_.get()),
msg.get());
return ipm->template do_intra_process_publish_and_return_shared<ROSMessageType, ROSMessageType,
AllocatorT>(

View File

@@ -33,7 +33,7 @@
#include "rclcpp/macros.hpp"
#include "rclcpp/network_flow_endpoint.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/event_handler.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rcpputils/time.hpp"
@@ -124,7 +124,7 @@ public:
/** \return The map of QoS event handlers. */
RCLCPP_PUBLIC
const
std::unordered_map<rcl_publisher_event_type_t, std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
std::unordered_map<rcl_publisher_event_type_t, std::shared_ptr<rclcpp::EventHandlerBase>> &
get_event_handlers() const;
/// Get subscription count
@@ -276,7 +276,7 @@ public:
* If you want more information available in the callback, like the qos event
* or other information, you may use a lambda with captures or std::bind.
*
* \sa rclcpp::QOSEventHandlerBase::set_on_ready_callback
* \sa rclcpp::EventHandlerBase::set_on_ready_callback
*
* \param[in] callback functor to be called when a new event occurs
* \param[in] event_type identifier for the qos event we want to attach the callback to
@@ -327,7 +327,7 @@ protected:
const EventCallbackT & callback,
const rcl_publisher_event_type_t event_type)
{
auto handler = std::make_shared<QOSEventHandler<EventCallbackT,
auto handler = std::make_shared<EventHandler<EventCallbackT,
std::shared_ptr<rcl_publisher_t>>>(
callback,
rcl_publisher_event_init,
@@ -339,12 +339,15 @@ protected:
RCLCPP_PUBLIC
void default_incompatible_qos_callback(QOSOfferedIncompatibleQoSInfo & info) const;
RCLCPP_PUBLIC
void default_incompatible_type_callback(IncompatibleTypeInfo & info) const;
std::shared_ptr<rcl_node_t> rcl_node_handle_;
std::shared_ptr<rcl_publisher_t> publisher_handle_;
std::unordered_map<rcl_publisher_event_type_t,
std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;
std::shared_ptr<rclcpp::EventHandlerBase>> event_handlers_;
using IntraProcessManagerWeakPtr =
std::weak_ptr<rclcpp::experimental::IntraProcessManager>;

View File

@@ -26,7 +26,7 @@
#include "rclcpp/detail/rmw_implementation_specific_publisher_payload.hpp"
#include "rclcpp/intra_process_setting.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/event_handler.hpp"
#include "rclcpp/qos_overriding_options.hpp"
namespace rclcpp

View File

@@ -15,279 +15,8 @@
#ifndef RCLCPP__QOS_EVENT_HPP_
#define RCLCPP__QOS_EVENT_HPP_
#include <functional>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <string>
#warning This header is obsolete, please include rclcpp/event_handler.hpp instead
#include "rcl/error_handling.h"
#include "rcl/event_callback.h"
#include "rmw/impl/cpp/demangle.hpp"
#include "rmw/incompatible_qos_events_statuses.h"
#include "rcutils/logging_macros.h"
#include "rclcpp/detail/cpp_callback_trampoline.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/function_traits.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/waitable.hpp"
namespace rclcpp
{
using QOSDeadlineRequestedInfo = rmw_requested_deadline_missed_status_t;
using QOSDeadlineOfferedInfo = rmw_offered_deadline_missed_status_t;
using QOSLivelinessChangedInfo = rmw_liveliness_changed_status_t;
using QOSLivelinessLostInfo = rmw_liveliness_lost_status_t;
using QOSMessageLostInfo = rmw_message_lost_status_t;
using QOSOfferedIncompatibleQoSInfo = rmw_offered_qos_incompatible_event_status_t;
using QOSRequestedIncompatibleQoSInfo = rmw_requested_qos_incompatible_event_status_t;
using QOSDeadlineRequestedCallbackType = std::function<void (QOSDeadlineRequestedInfo &)>;
using QOSDeadlineOfferedCallbackType = std::function<void (QOSDeadlineOfferedInfo &)>;
using QOSLivelinessChangedCallbackType = std::function<void (QOSLivelinessChangedInfo &)>;
using QOSLivelinessLostCallbackType = std::function<void (QOSLivelinessLostInfo &)>;
using QOSMessageLostCallbackType = std::function<void (QOSMessageLostInfo &)>;
using QOSOfferedIncompatibleQoSCallbackType = std::function<void (QOSOfferedIncompatibleQoSInfo &)>;
using QOSRequestedIncompatibleQoSCallbackType =
std::function<void (QOSRequestedIncompatibleQoSInfo &)>;
/// Contains callbacks for various types of events a Publisher can receive from the middleware.
struct PublisherEventCallbacks
{
QOSDeadlineOfferedCallbackType deadline_callback;
QOSLivelinessLostCallbackType liveliness_callback;
QOSOfferedIncompatibleQoSCallbackType incompatible_qos_callback;
};
/// Contains callbacks for non-message events that a Subscription can receive from the middleware.
struct SubscriptionEventCallbacks
{
QOSDeadlineRequestedCallbackType deadline_callback;
QOSLivelinessChangedCallbackType liveliness_callback;
QOSRequestedIncompatibleQoSCallbackType incompatible_qos_callback;
QOSMessageLostCallbackType message_lost_callback;
};
class UnsupportedEventTypeException : public exceptions::RCLErrorBase, public std::runtime_error
{
public:
RCLCPP_PUBLIC
UnsupportedEventTypeException(
rcl_ret_t ret,
const rcl_error_state_t * error_state,
const std::string & prefix);
RCLCPP_PUBLIC
UnsupportedEventTypeException(
const exceptions::RCLErrorBase & base_exc,
const std::string & prefix);
};
class QOSEventHandlerBase : public Waitable
{
public:
enum class EntityType : std::size_t
{
Event,
};
RCLCPP_PUBLIC
virtual ~QOSEventHandlerBase();
/// Get the number of ready events
RCLCPP_PUBLIC
size_t
get_number_of_ready_events() override;
/// Add the Waitable to a wait set.
RCLCPP_PUBLIC
void
add_to_wait_set(rcl_wait_set_t * wait_set) override;
/// Check if the Waitable is ready.
RCLCPP_PUBLIC
bool
is_ready(rcl_wait_set_t * wait_set) override;
/// Set a callback to be called when each new event instance occurs.
/**
* The callback receives a size_t which is the number of events that occurred
* since the last time this callback was called.
* Normally this is 1, but can be > 1 if events occurred before any
* callback was set.
*
* The callback also receives an int identifier argument.
* This is needed because a Waitable may be composed of several distinct entities,
* such as subscriptions, services, etc.
* The application should provide a generic callback function that will be then
* forwarded by the waitable to all of its entities.
* Before forwarding, a different value for the identifier argument will be
* bond to the function.
* This implies that the provided callback can use the identifier to behave
* differently depending on which entity triggered the waitable to become ready.
*
* Since this callback is called from the middleware, you should aim to make
* it fast and not blocking.
* If you need to do a lot of work or wait for some other event, you should
* spin it off to another thread, otherwise you risk blocking the middleware.
*
* Calling it again will clear any previously set callback.
*
* An exception will be thrown if the callback is not callable.
*
* This function is thread-safe.
*
* If you want more information available in the callback, like the qos event
* or other information, you may use a lambda with captures or std::bind.
*
* \sa rmw_event_set_callback
* \sa rcl_event_set_callback
*
* \param[in] callback functor to be called when a new event occurs
*/
void
set_on_ready_callback(std::function<void(size_t, int)> callback) override
{
if (!callback) {
throw std::invalid_argument(
"The callback passed to set_on_ready_callback "
"is not callable.");
}
// Note: we bind the int identifier argument to this waitable's entity types
auto new_callback =
[callback, this](size_t number_of_events) {
try {
callback(number_of_events, static_cast<int>(EntityType::Event));
} catch (const std::exception & exception) {
RCLCPP_ERROR_STREAM(
// TODO(wjwwood): get this class access to the node logger it is associated with
rclcpp::get_logger("rclcpp"),
"rclcpp::QOSEventHandlerBase@" << this <<
" caught " << rmw::impl::cpp::demangle(exception) <<
" exception in user-provided callback for the 'on ready' callback: " <<
exception.what());
} catch (...) {
RCLCPP_ERROR_STREAM(
rclcpp::get_logger("rclcpp"),
"rclcpp::QOSEventHandlerBase@" << this <<
" caught unhandled exception in user-provided callback " <<
"for the 'on ready' callback");
}
};
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
// Set it temporarily to the new callback, while we replace the old one.
// This two-step setting, prevents a gap where the old std::function has
// been replaced but the middleware hasn't been told about the new one yet.
set_on_new_event_callback(
rclcpp::detail::cpp_callback_trampoline<decltype(new_callback), const void *, size_t>,
static_cast<const void *>(&new_callback));
// Store the std::function to keep it in scope, also overwrites the existing one.
on_new_event_callback_ = new_callback;
// Set it again, now using the permanent storage.
set_on_new_event_callback(
rclcpp::detail::cpp_callback_trampoline<
decltype(on_new_event_callback_), const void *, size_t>,
static_cast<const void *>(&on_new_event_callback_));
}
/// Unset the callback registered for new events, if any.
void
clear_on_ready_callback() override
{
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
if (on_new_event_callback_) {
set_on_new_event_callback(nullptr, nullptr);
on_new_event_callback_ = nullptr;
}
}
protected:
RCLCPP_PUBLIC
void
set_on_new_event_callback(rcl_event_callback_t callback, const void * user_data);
std::recursive_mutex callback_mutex_;
std::function<void(size_t)> on_new_event_callback_{nullptr};
rcl_event_t event_handle_;
size_t wait_set_event_index_;
};
template<typename EventCallbackT, typename ParentHandleT>
class QOSEventHandler : public QOSEventHandlerBase
{
public:
template<typename InitFuncT, typename EventTypeEnum>
QOSEventHandler(
const EventCallbackT & callback,
InitFuncT init_func,
ParentHandleT parent_handle,
EventTypeEnum event_type)
: parent_handle_(parent_handle), event_callback_(callback)
{
event_handle_ = rcl_get_zero_initialized_event();
rcl_ret_t ret = init_func(&event_handle_, parent_handle.get(), event_type);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_UNSUPPORTED) {
UnsupportedEventTypeException exc(ret, rcl_get_error_state(), "Failed to initialize event");
rcl_reset_error();
throw exc;
} else {
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to initialize event");
}
}
}
/// Take data so that the callback cannot be scheduled again
std::shared_ptr<void>
take_data() override
{
EventCallbackInfoT callback_info;
rcl_ret_t ret = rcl_take_event(&event_handle_, &callback_info);
if (ret != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't take event info: %s", rcl_get_error_string().str);
return nullptr;
}
return std::static_pointer_cast<void>(std::make_shared<EventCallbackInfoT>(callback_info));
}
std::shared_ptr<void>
take_data_by_entity_id(size_t id) override
{
(void)id;
return take_data();
}
/// Execute any entities of the Waitable that are ready.
void
execute(std::shared_ptr<void> & data) override
{
if (!data) {
throw std::runtime_error("'data' is empty");
}
auto callback_ptr = std::static_pointer_cast<EventCallbackInfoT>(data);
event_callback_(*callback_ptr);
callback_ptr.reset();
}
private:
using EventCallbackInfoT = typename std::remove_reference<typename
rclcpp::function_traits::function_traits<EventCallbackT>::template argument_type<0>>::type;
ParentHandleT parent_handle_;
EventCallbackT event_callback_;
};
} // namespace rclcpp
#include "rclcpp/event_handler.hpp"
#endif // RCLCPP__QOS_EVENT_HPP_

View File

@@ -117,6 +117,18 @@
* - Allocator related items:
* - rclcpp/allocator/allocator_common.hpp
* - rclcpp/allocator/allocator_deleter.hpp
* - Dynamic typesupport wrappers
* - rclcpp::dynamic_typesupport::DynamicMessage
* - rclcpp::dynamic_typesupport::DynamicMessageType
* - rclcpp::dynamic_typesupport::DynamicMessageTypeBuilder
* - rclcpp::dynamic_typesupport::DynamicSerializationSupport
* - rclcpp/dynamic_typesupport/dynamic_message.hpp
* - rclcpp/dynamic_typesupport/dynamic_message_type.hpp
* - rclcpp/dynamic_typesupport/dynamic_message_type_builder.hpp
* - rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp
* - Dynamic typesupport
* - rclcpp::dynamic_typesupport::DynamicMessageTypeSupport
* - rclcpp/dynamic_typesupport/dynamic_message_type_support.hpp
* - Generic publisher
* - rclcpp::Node::create_generic_publisher()
* - rclcpp::GenericPublisher

View File

@@ -1,56 +0,0 @@
// Copyright 2015 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.
// Based on: http://the-witness.net/news/2012/11/scopeexit-in-c11/
// But I changed the lambda to include by reference rather than value, see:
// http://the-witness.net/news/2012/11/scopeexit-in-c11/comment-page-1/#comment-86873
#ifndef RCLCPP__SCOPE_EXIT_HPP_
#define RCLCPP__SCOPE_EXIT_HPP_
// TODO(christophebedard) remove this header completely in I-turtle
#warning rclcpp/scope_exit.hpp has been deprecated, please use rcpputils/scope_exit.hpp instead
#include <functional>
#include "rclcpp/macros.hpp"
namespace rclcpp
{
template<typename Callable>
struct ScopeExit
{
explicit ScopeExit(Callable callable)
: callable_(callable) {}
~ScopeExit() {callable_();}
private:
Callable callable_;
};
template<typename Callable>
ScopeExit<Callable>
make_scope_exit(Callable callable)
{
return ScopeExit<Callable>(callable);
}
} // namespace rclcpp
#define RCLCPP_SCOPE_EXIT(code) \
auto RCLCPP_STRING_JOIN(scope_exit_, __LINE__) = rclcpp::make_scope_exit([&]() {code;})
#endif // RCLCPP__SCOPE_EXIT_HPP_

View File

@@ -26,6 +26,7 @@
#include "rcl/error_handling.h"
#include "rcl/event_callback.h"
#include "rcl/service.h"
#include "rcl/service_introspection.h"
#include "rmw/error_handling.h"
#include "rmw/impl/cpp/demangle.hpp"
@@ -34,6 +35,7 @@
#include "tracetools/tracetools.h"
#include "rclcpp/any_service_callback.hpp"
#include "rclcpp/clock.hpp"
#include "rclcpp/detail/cpp_callback_trampoline.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
@@ -308,11 +310,9 @@ public:
const std::string & service_name,
AnyServiceCallback<ServiceT> any_callback,
rcl_service_options_t & service_options)
: ServiceBase(node_handle), any_callback_(any_callback)
: ServiceBase(node_handle), any_callback_(any_callback),
srv_type_support_handle_(rosidl_typesupport_cpp::get_service_type_support_handle<ServiceT>())
{
using rosidl_typesupport_cpp::get_service_type_support_handle;
auto service_type_support_handle = get_service_type_support_handle<ServiceT>();
// 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)
@@ -331,7 +331,7 @@ public:
rcl_ret_t ret = rcl_service_init(
service_handle_.get(),
node_handle.get(),
service_type_support_handle,
srv_type_support_handle_,
service_name.c_str(),
&service_options);
if (ret != RCL_RET_OK) {
@@ -371,8 +371,8 @@ public:
std::shared_ptr<rcl_node_t> node_handle,
std::shared_ptr<rcl_service_t> service_handle,
AnyServiceCallback<ServiceT> any_callback)
: ServiceBase(node_handle),
any_callback_(any_callback)
: ServiceBase(node_handle), any_callback_(any_callback),
srv_type_support_handle_(rosidl_typesupport_cpp::get_service_type_support_handle<ServiceT>())
{
// check if service handle was initialized
if (!rcl_service_is_valid(service_handle.get())) {
@@ -406,8 +406,8 @@ public:
std::shared_ptr<rcl_node_t> node_handle,
rcl_service_t * service_handle,
AnyServiceCallback<ServiceT> any_callback)
: ServiceBase(node_handle),
any_callback_(any_callback)
: ServiceBase(node_handle), any_callback_(any_callback),
srv_type_support_handle_(rosidl_typesupport_cpp::get_service_type_support_handle<ServiceT>())
{
// check if service handle was initialized
if (!rcl_service_is_valid(service_handle)) {
@@ -487,10 +487,39 @@ public:
}
}
/// Configure client introspection.
/**
* \param[in] clock clock to use to generate introspection timestamps
* \param[in] qos_service_event_pub QoS settings to use when creating the introspection publisher
* \param[in] introspection_state the state to set introspection to
*/
void
configure_introspection(
Clock::SharedPtr clock, const QoS & qos_service_event_pub,
rcl_service_introspection_state_t introspection_state)
{
rcl_publisher_options_t pub_opts = rcl_publisher_get_default_options();
pub_opts.qos = qos_service_event_pub.get_rmw_qos_profile();
rcl_ret_t ret = rcl_service_configure_service_introspection(
service_handle_.get(),
node_handle_.get(),
clock->get_clock_handle(),
srv_type_support_handle_,
pub_opts,
introspection_state);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to configure service introspection");
}
}
private:
RCLCPP_DISABLE_COPY(Service)
AnyServiceCallback<ServiceT> any_callback_;
const rosidl_service_type_support_t * srv_type_support_handle_;
};
} // namespace rclcpp

View File

@@ -17,6 +17,7 @@
#include <memory>
#include <vector>
#include <utility>
#include "rcl/allocator.h"
@@ -120,8 +121,8 @@ public:
}
}
for (size_t i = 0; i < waitable_handles_.size(); ++i) {
if (!waitable_handles_[i]->is_ready(wait_set)) {
waitable_handles_[i].reset();
if (waitable_handles_[i]->is_ready(wait_set)) {
waitable_triggered_handles_.emplace_back(std::move(waitable_handles_[i]));
}
}
@@ -145,10 +146,7 @@ public:
timer_handles_.end()
);
waitable_handles_.erase(
std::remove(waitable_handles_.begin(), waitable_handles_.end(), nullptr),
waitable_handles_.end()
);
waitable_handles_.clear();
}
bool collect_entities(const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
@@ -392,8 +390,9 @@ public:
rclcpp::AnyExecutable & any_exec,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
{
auto it = waitable_handles_.begin();
while (it != waitable_handles_.end()) {
auto & waitable_handles = waitable_triggered_handles_;
auto it = waitable_handles.begin();
while (it != waitable_handles.end()) {
std::shared_ptr<Waitable> & waitable = *it;
if (waitable) {
// Find the group for this handle and see if it can be serviced
@@ -401,7 +400,7 @@ public:
if (!group) {
// Group was not found, meaning the waitable is not valid...
// Remove it from the ready list and continue looking
it = waitable_handles_.erase(it);
it = waitable_handles.erase(it);
continue;
}
if (!group->can_be_taken_from().load()) {
@@ -414,11 +413,11 @@ public:
any_exec.waitable = waitable;
any_exec.callback_group = group;
any_exec.node_base = get_node_by_group(group, weak_groups_to_nodes);
waitable_handles_.erase(it);
waitable_handles.erase(it);
return;
}
// Else, the waitable is no longer valid, remove it and continue
it = waitable_handles_.erase(it);
it = waitable_handles.erase(it);
}
}
@@ -499,6 +498,8 @@ private:
VectorRebind<std::shared_ptr<const rcl_timer_t>> timer_handles_;
VectorRebind<std::shared_ptr<Waitable>> waitable_handles_;
VectorRebind<std::shared_ptr<Waitable>> waitable_triggered_handles_;
std::shared_ptr<VoidAlloc> allocator_;
};

View File

@@ -144,7 +144,7 @@ public:
// NOTE(methylDragon): Passing these args separately is necessary for event binding
options.event_callbacks,
options.use_default_callbacks,
callback.is_serialized_message_callback()),
callback.is_serialized_message_callback() ? SubscriptionType::SERIALIZED_MESSAGE : SubscriptionType::ROS_MESSAGE), // NOLINT
any_callback_(callback),
options_(options),
message_memory_strategy_(message_memory_strategy)
@@ -388,6 +388,57 @@ public:
return any_callback_.use_take_shared_method();
}
// DYNAMIC TYPE ==================================================================================
// TODO(methylDragon): Reorder later
// TODO(methylDragon): Implement later...
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr
get_shared_dynamic_message_type() override
{
throw rclcpp::exceptions::UnimplementedError(
"get_shared_dynamic_message_type is not implemented for Subscription");
}
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
get_shared_dynamic_message() override
{
throw rclcpp::exceptions::UnimplementedError(
"get_shared_dynamic_message is not implemented for Subscription");
}
rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support() override
{
throw rclcpp::exceptions::UnimplementedError(
"get_shared_dynamic_serialization_support is not implemented for Subscription");
}
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
create_dynamic_message() override
{
throw rclcpp::exceptions::UnimplementedError(
"create_dynamic_message is not implemented for Subscription");
}
void
return_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message) override
{
(void) message;
throw rclcpp::exceptions::UnimplementedError(
"return_dynamic_message is not implemented for Subscription");
}
void
handle_dynamic_message(
const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
const rclcpp::MessageInfo & message_info) override
{
(void) message;
(void) message_info;
throw rclcpp::exceptions::UnimplementedError(
"handle_dynamic_message is not implemented for Subscription");
}
private:
RCLCPP_DISABLE_COPY(Subscription)

View File

@@ -31,13 +31,16 @@
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/detail/cpp_callback_trampoline.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message_type.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
#include "rclcpp/experimental/intra_process_manager.hpp"
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/message_info.hpp"
#include "rclcpp/network_flow_endpoint.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/event_handler.hpp"
#include "rclcpp/serialized_message.hpp"
#include "rclcpp/subscription_content_filter_options.hpp"
#include "rclcpp/type_support_decl.hpp"
@@ -60,6 +63,15 @@ namespace experimental
class IntraProcessManager;
} // namespace experimental
enum class SubscriptionType : uint8_t
{
INVALID = 0, // The subscription type is most likely uninitialized
ROS_MESSAGE = 1, // take message as ROS message and handle as ROS message
SERIALIZED_MESSAGE = 2, // take message as serialized and handle as serialized
DYNAMIC_MESSAGE_DIRECT = 3, // take message as DynamicMessage and handle as DynamicMessage
DYNAMIC_MESSAGE_FROM_SERIALIZED = 4 // take message as serialized and handle as DynamicMessage
};
/// Virtual base class for subscriptions. This pattern allows us to iterate over different template
/// specializations of Subscription, among other things.
class SubscriptionBase : public std::enable_shared_from_this<SubscriptionBase>
@@ -76,7 +88,7 @@ public:
* \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
* \param[in] topic_name Name of the topic to subscribe to.
* \param[in] subscription_options Options for the subscription.
* \param[in] is_serialized is true if the message will be delivered still serialized
* \param[in] subscription_type Enum flag to change how the message will be received and delivered
*/
RCLCPP_PUBLIC
SubscriptionBase(
@@ -86,7 +98,7 @@ public:
const rcl_subscription_options_t & subscription_options,
const SubscriptionEventCallbacks & event_callbacks,
bool use_default_callbacks,
bool is_serialized = false);
SubscriptionType subscription_type = SubscriptionType::ROS_MESSAGE);
/// Destructor.
RCLCPP_PUBLIC
@@ -115,7 +127,7 @@ public:
/** \return The map of QoS event handlers. */
RCLCPP_PUBLIC
const
std::unordered_map<rcl_subscription_event_type_t, std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
std::unordered_map<rcl_subscription_event_type_t, std::shared_ptr<rclcpp::EventHandlerBase>> &
get_event_handlers() const;
/// Get the actual QoS settings, after the defaults have been determined.
@@ -235,6 +247,14 @@ public:
bool
is_serialized() const;
/// Return the type of the subscription.
/**
* \return `SubscriptionType`, which adjusts how messages are received and delivered.
*/
RCLCPP_PUBLIC
SubscriptionType
get_subscription_type() const;
/// Get matching publisher count.
/** \return The number of publishers on this topic. */
RCLCPP_PUBLIC
@@ -457,7 +477,7 @@ public:
* If you want more information available in the callback, like the qos event
* or other information, you may use a lambda with captures or std::bind.
*
* \sa rclcpp::QOSEventHandlerBase::set_on_ready_callback
* \sa rclcpp::EventHandlerBase::set_on_ready_callback
*
* \param[in] callback functor to be called when a new event occurs
* \param[in] event_type identifier for the qos event we want to attach the callback to
@@ -535,6 +555,49 @@ public:
rclcpp::ContentFilterOptions
get_content_filter() const;
// DYNAMIC TYPE ==================================================================================
// TODO(methylDragon): Reorder later
RCLCPP_PUBLIC
virtual
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr
get_shared_dynamic_message_type() = 0;
RCLCPP_PUBLIC
virtual
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
get_shared_dynamic_message() = 0;
RCLCPP_PUBLIC
virtual
rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support() = 0;
/// Borrow a new serialized message (this clones!)
/** \return Shared pointer to a rclcpp::dynamic_typesupport::DynamicMessage. */
RCLCPP_PUBLIC
virtual
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
create_dynamic_message() = 0;
RCLCPP_PUBLIC
virtual
void
return_dynamic_message(rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message) = 0;
RCLCPP_PUBLIC
virtual
void
handle_dynamic_message(
const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
const rclcpp::MessageInfo & message_info) = 0;
RCLCPP_PUBLIC
bool
take_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage & message_out,
rclcpp::MessageInfo & message_info_out);
// ===============================================================================================
protected:
template<typename EventCallbackT>
void
@@ -542,7 +605,7 @@ protected:
const EventCallbackT & callback,
const rcl_subscription_event_type_t event_type)
{
auto handler = std::make_shared<QOSEventHandler<EventCallbackT,
auto handler = std::make_shared<EventHandler<EventCallbackT,
std::shared_ptr<rcl_subscription_t>>>(
callback,
rcl_subscription_event_init,
@@ -555,6 +618,9 @@ protected:
RCLCPP_PUBLIC
void default_incompatible_qos_callback(QOSRequestedIncompatibleQoSInfo & info) const;
RCLCPP_PUBLIC
void default_incompatible_type_callback(IncompatibleTypeInfo & info) const;
RCLCPP_PUBLIC
bool
matches_any_intra_process_publishers(const rmw_gid_t * sender_gid) const;
@@ -571,7 +637,7 @@ protected:
rclcpp::Logger node_logger_;
std::unordered_map<rcl_subscription_event_type_t,
std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;
std::shared_ptr<rclcpp::EventHandlerBase>> event_handlers_;
bool use_intra_process_;
IntraProcessManagerWeakPtr weak_ipm_;
@@ -584,11 +650,11 @@ private:
RCLCPP_DISABLE_COPY(SubscriptionBase)
rosidl_message_type_support_t type_support_;
bool is_serialized_;
SubscriptionType subscription_type_;
std::atomic<bool> subscription_in_use_by_wait_set_{false};
std::atomic<bool> intra_process_subscription_waitable_in_use_by_wait_set_{false};
std::unordered_map<rclcpp::QOSEventHandlerBase *,
std::unordered_map<rclcpp::EventHandlerBase *,
std::atomic<bool>> qos_events_in_use_by_wait_set_;
std::recursive_mutex callback_mutex_;

View File

@@ -26,7 +26,7 @@
#include "rclcpp/intra_process_buffer_type.hpp"
#include "rclcpp/intra_process_setting.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/event_handler.hpp"
#include "rclcpp/qos_overriding_options.hpp"
#include "rclcpp/subscription_content_filter_options.hpp"
#include "rclcpp/topic_statistics_state.hpp"

View File

@@ -227,10 +227,16 @@ public:
rclcpp_timer_callback_added,
static_cast<const void *>(get_timer_handle().get()),
reinterpret_cast<const void *>(&callback_));
TRACEPOINT(
rclcpp_callback_register,
reinterpret_cast<const void *>(&callback_),
tracetools::get_symbol(callback_));
#ifndef TRACETOOLS_DISABLED
if (TRACEPOINT_ENABLED(rclcpp_callback_register)) {
char * symbol = tracetools::get_symbol(callback_);
DO_TRACEPOINT(
rclcpp_callback_register,
reinterpret_cast<const void *>(&callback_),
symbol);
std::free(symbol);
}
#endif
}
/// Default destructor.

View File

@@ -516,7 +516,7 @@ public:
* the waitable to be removed, but it will cause the associated entity pointer
* to be nullptr when introspecting this waitable after waiting.
*
* Note that rclcpp::QOSEventHandlerBase are just a special case of
* Note that rclcpp::EventHandlerBase is just a special case of
* rclcpp::Waitable and can be added with this function.
*
* \param[in] waitable Waitable to be added.

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp</name>
<version>19.2.0</version>
<version>20.0.0</version>
<description>The ROS client library in C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
@@ -39,6 +39,7 @@
<depend>rcpputils</depend>
<depend>rcutils</depend>
<depend>rmw</depend>
<depend>rosidl_dynamic_typesupport</depend>
<depend>statistics_msgs</depend>
<depend>tracetools</depend>

View File

@@ -23,9 +23,11 @@
#include "rcl/graph.h"
#include "rcl/node.h"
#include "rcl/wait.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/logging.hpp"
@@ -241,7 +243,6 @@ ClientBase::set_on_new_response_callback(rcl_event_callback_t callback, const vo
user_data);
if (RCL_RET_OK != ret) {
using rclcpp::exceptions::throw_from_rcl_error;
throw_from_rcl_error(ret, "failed to set the on new response callback for client");
}
}

View File

@@ -219,7 +219,7 @@ Context::init(
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()),
rcl_init_options_get_allocator(init_options.get_rcl_init_options()),
rclcpp_logging_output_handler);
if (RCL_RET_OK != ret) {
rcl_context_.reset();

View File

@@ -0,0 +1,40 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <rosidl_dynamic_typesupport/api/dynamic_type.h>
#include <rosidl_dynamic_typesupport/api/dynamic_data.h>
#include <rosidl_dynamic_typesupport/api/serialization_support.h>
#include <rosidl_dynamic_typesupport/types.h>
#include <memory>
#include <string>
#include "rcl/allocator.h"
#include "rcl/types.h"
#include "rcutils/logging_macros.h"
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message_type.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message_type_builder.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
#include "rclcpp/exceptions.hpp"
using rclcpp::dynamic_typesupport::DynamicMessage;
using rclcpp::dynamic_typesupport::DynamicMessageType;
using rclcpp::dynamic_typesupport::DynamicMessageTypeBuilder;
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
DynamicMessage::~DynamicMessage()
{} // STUBBED

View File

@@ -0,0 +1,38 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <rosidl_dynamic_typesupport/api/dynamic_data.h>
#include <rosidl_dynamic_typesupport/api/dynamic_type.h>
#include <rosidl_dynamic_typesupport/api/serialization_support.h>
#include <rosidl_dynamic_typesupport/types.h>
#include <memory>
#include <string>
#include "rcutils/logging_macros.h"
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message_type.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message_type_builder.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
#include "rclcpp/exceptions.hpp"
using rclcpp::dynamic_typesupport::DynamicMessage;
using rclcpp::dynamic_typesupport::DynamicMessageType;
using rclcpp::dynamic_typesupport::DynamicMessageTypeBuilder;
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
DynamicMessageType::~DynamicMessageType()
{} // STUBBED

View File

@@ -0,0 +1,37 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <rosidl_dynamic_typesupport/api/dynamic_data.h>
#include <rosidl_dynamic_typesupport/api/dynamic_type.h>
#include <rosidl_dynamic_typesupport/api/serialization_support.h>
#include <rosidl_dynamic_typesupport/types.h>
#include <memory>
#include <string>
#include "rcutils/logging_macros.h"
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message_type.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message_type_builder.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
#include "rclcpp/exceptions.hpp"
using rclcpp::dynamic_typesupport::DynamicMessage;
using rclcpp::dynamic_typesupport::DynamicMessageType;
using rclcpp::dynamic_typesupport::DynamicMessageTypeBuilder;
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
DynamicMessageTypeBuilder::~DynamicMessageTypeBuilder()
{} // STUBBED

View File

@@ -0,0 +1,49 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <rosidl_dynamic_typesupport/identifier.h>
#include <rosidl_dynamic_typesupport/types.h>
#include <rosidl_runtime_c/message_type_support_struct.h>
#include <rosidl_runtime_c/type_description_utils.h>
#include <rosidl_runtime_c/type_description/type_description__functions.h>
#include <rosidl_runtime_c/type_description/type_description__struct.h>
#include <rosidl_runtime_c/type_description/type_source__functions.h>
#include <rosidl_runtime_c/type_description/type_source__struct.h>
#include <memory>
#include <string>
#include "rcl/allocator.h"
#include "rcl/dynamic_message_type_support.h"
#include "rcl/type_hash.h"
#include "rcl/types.h"
#include "rcutils/logging_macros.h"
#include "rcutils/types/rcutils_ret.h"
#include "rmw/dynamic_message_type_support.h"
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message_type.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message_type_support.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
using rclcpp::dynamic_typesupport::DynamicMessage;
using rclcpp::dynamic_typesupport::DynamicMessageType;
using rclcpp::dynamic_typesupport::DynamicMessageTypeSupport;
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
DynamicMessageTypeSupport::~DynamicMessageTypeSupport()
{} // STUBBED

View File

@@ -0,0 +1,46 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <rcl/allocator.h>
#include <rcutils/logging_macros.h>
#include <rmw/dynamic_message_type_support.h>
#include <rmw/ret_types.h>
#include <rosidl_dynamic_typesupport/api/serialization_support.h>
#include <memory>
#include <string>
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
#include "rclcpp/exceptions.hpp"
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
// CONSTRUCTION ====================================================================================
DynamicSerializationSupport::DynamicSerializationSupport(rcl_allocator_t allocator)
: DynamicSerializationSupport::DynamicSerializationSupport("", allocator)
{
throw std::runtime_error("Unimplemented");
}
DynamicSerializationSupport::DynamicSerializationSupport(
const std::string & /*serialization_library_name*/,
rcl_allocator_t /*allocator*/)
: rosidl_serialization_support_(
rosidl_dynamic_typesupport_get_zero_initialized_serialization_support())
{
throw std::runtime_error("Unimplemented");
}
DynamicSerializationSupport::~DynamicSerializationSupport()
{} // STUBBED

View File

@@ -12,9 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdexcept>
#include <string>
#include "rclcpp/qos_event.hpp"
#include "rcl/event.h"
#include "rclcpp/event_handler.hpp"
#include "rclcpp/exceptions/exceptions.hpp"
namespace rclcpp
{
@@ -33,7 +37,7 @@ UnsupportedEventTypeException::UnsupportedEventTypeException(
std::runtime_error(prefix + (prefix.empty() ? "" : ": ") + base_exc.formatted_message)
{}
QOSEventHandlerBase::~QOSEventHandlerBase()
EventHandlerBase::~EventHandlerBase()
{
// Since the rmw event listener holds a reference to
// this callback, we need to clear it on destruction of this class.
@@ -52,14 +56,14 @@ QOSEventHandlerBase::~QOSEventHandlerBase()
/// Get the number of ready events.
size_t
QOSEventHandlerBase::get_number_of_ready_events()
EventHandlerBase::get_number_of_ready_events()
{
return 1;
}
/// Add the Waitable to a wait set.
void
QOSEventHandlerBase::add_to_wait_set(rcl_wait_set_t * wait_set)
EventHandlerBase::add_to_wait_set(rcl_wait_set_t * wait_set)
{
rcl_ret_t ret = rcl_wait_set_add_event(wait_set, &event_handle_, &wait_set_event_index_);
if (RCL_RET_OK != ret) {
@@ -69,13 +73,13 @@ QOSEventHandlerBase::add_to_wait_set(rcl_wait_set_t * wait_set)
/// Check if the Waitable is ready.
bool
QOSEventHandlerBase::is_ready(rcl_wait_set_t * wait_set)
EventHandlerBase::is_ready(rcl_wait_set_t * wait_set)
{
return wait_set->events[wait_set_event_index_] == &event_handle_;
}
void
QOSEventHandlerBase::set_on_new_event_callback(
EventHandlerBase::set_on_new_event_callback(
rcl_event_callback_t callback,
const void * user_data)
{
@@ -86,7 +90,7 @@ QOSEventHandlerBase::set_on_new_event_callback(
if (RCL_RET_OK != ret) {
using rclcpp::exceptions::throw_from_rcl_error;
throw_from_rcl_error(ret, "failed to set the on new message callback for QOS Event");
throw_from_rcl_error(ret, "failed to set the on new message callback for Event");
}
}

View File

@@ -24,6 +24,7 @@
#include "rcl/error_handling.h"
#include "rcpputils/scope_exit.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/executor.hpp"
#include "rclcpp/guard_condition.hpp"
@@ -558,13 +559,14 @@ Executor::execute_any_executable(AnyExecutable & any_exec)
}
}
template<typename Taker, typename Handler>
static
void
take_and_do_error_handling(
const char * action_description,
const char * topic_or_service_name,
std::function<bool()> take_action,
std::function<void()> handle_action)
Taker take_action,
Handler handle_action)
{
bool taken = false;
try {
@@ -597,70 +599,104 @@ take_and_do_error_handling(
void
Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
{
using rclcpp::dynamic_typesupport::DynamicMessage;
rclcpp::MessageInfo message_info;
message_info.get_rmw_message_info().from_intra_process = false;
if (subscription->is_serialized()) {
// This is the case where a copy of the serialized message is taken from
// the middleware via inter-process communication.
std::shared_ptr<SerializedMessage> serialized_msg = subscription->create_serialized_message();
take_and_do_error_handling(
"taking a serialized message from topic",
subscription->get_topic_name(),
[&]() {return subscription->take_serialized(*serialized_msg.get(), message_info);},
[&]()
switch (subscription->get_subscription_type()) {
// Take ROS message
case rclcpp::SubscriptionType::ROS_MESSAGE:
{
subscription->handle_serialized_message(serialized_msg, message_info);
});
subscription->return_serialized_message(serialized_msg);
} else if (subscription->can_loan_messages()) {
// This is the case where a loaned message is taken from the middleware via
// inter-process communication, given to the user for their callback,
// and then returned.
void * loaned_msg = nullptr;
// TODO(wjwwood): refactor this into methods on subscription when LoanedMessage
// is extened to support subscriptions as well.
take_and_do_error_handling(
"taking a loaned message from topic",
subscription->get_topic_name(),
[&]()
{
rcl_ret_t ret = rcl_take_loaned_message(
subscription->get_subscription_handle().get(),
&loaned_msg,
&message_info.get_rmw_message_info(),
nullptr);
if (RCL_RET_SUBSCRIPTION_TAKE_FAILED == ret) {
return false;
} else if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
if (subscription->can_loan_messages()) {
// This is the case where a loaned message is taken from the middleware via
// inter-process communication, given to the user for their callback,
// and then returned.
void * loaned_msg = nullptr;
// TODO(wjwwood): refactor this into methods on subscription when LoanedMessage
// is extened to support subscriptions as well.
take_and_do_error_handling(
"taking a loaned message from topic",
subscription->get_topic_name(),
[&]()
{
rcl_ret_t ret = rcl_take_loaned_message(
subscription->get_subscription_handle().get(),
&loaned_msg,
&message_info.get_rmw_message_info(),
nullptr);
if (RCL_RET_SUBSCRIPTION_TAKE_FAILED == ret) {
return false;
} else if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
return true;
},
[&]() {subscription->handle_loaned_message(loaned_msg, message_info);});
if (nullptr != loaned_msg) {
rcl_ret_t ret = rcl_return_loaned_message_from_subscription(
subscription->get_subscription_handle().get(), loaned_msg);
if (RCL_RET_OK != ret) {
RCLCPP_ERROR(
rclcpp::get_logger("rclcpp"),
"rcl_return_loaned_message_from_subscription() failed for subscription on topic "
"'%s': %s",
subscription->get_topic_name(), rcl_get_error_string().str);
}
loaned_msg = nullptr;
}
} else {
// This case is taking a copy of the message data from the middleware via
// inter-process communication.
std::shared_ptr<void> message = subscription->create_message();
take_and_do_error_handling(
"taking a message from topic",
subscription->get_topic_name(),
[&]() {return subscription->take_type_erased(message.get(), message_info);},
[&]() {subscription->handle_message(message, message_info);});
subscription->return_message(message);
}
return true;
},
[&]() {subscription->handle_loaned_message(loaned_msg, message_info);});
if (nullptr != loaned_msg) {
rcl_ret_t ret = rcl_return_loaned_message_from_subscription(
subscription->get_subscription_handle().get(),
loaned_msg);
if (RCL_RET_OK != ret) {
RCLCPP_ERROR(
rclcpp::get_logger("rclcpp"),
"rcl_return_loaned_message_from_subscription() failed for subscription on topic '%s': %s",
subscription->get_topic_name(), rcl_get_error_string().str);
break;
}
// Take serialized message
case rclcpp::SubscriptionType::SERIALIZED_MESSAGE:
{
// This is the case where a copy of the serialized message is taken from
// the middleware via inter-process communication.
std::shared_ptr<SerializedMessage> serialized_msg =
subscription->create_serialized_message();
take_and_do_error_handling(
"taking a serialized message from topic",
subscription->get_topic_name(),
[&]() {return subscription->take_serialized(*serialized_msg.get(), message_info);},
[&]()
{
subscription->handle_serialized_message(serialized_msg, message_info);
});
subscription->return_serialized_message(serialized_msg);
break;
}
// DYNAMIC SUBSCRIPTION ========================================================================
// Take dynamic message directly from the middleware
case rclcpp::SubscriptionType::DYNAMIC_MESSAGE_DIRECT:
{
throw std::runtime_error("Unimplemented");
}
// Take serialized and then convert to dynamic message
case rclcpp::SubscriptionType::DYNAMIC_MESSAGE_FROM_SERIALIZED:
{
throw std::runtime_error("Unimplemented");
}
default:
{
throw std::runtime_error("Subscription type is not supported");
}
loaned_msg = nullptr;
}
} else {
// This case is taking a copy of the message data from the middleware via
// inter-process communication.
std::shared_ptr<void> message = subscription->create_message();
take_and_do_error_handling(
"taking a message from topic",
subscription->get_topic_name(),
[&]() {return subscription->take_type_erased(message.get(), message_info);},
[&]() {subscription->handle_message(message, message_info);});
subscription->return_message(message);
}
return;
}
void

View File

@@ -25,17 +25,20 @@
namespace rclcpp
{
std::shared_ptr<void> GenericSubscription::create_message()
std::shared_ptr<void>
GenericSubscription::create_message()
{
return create_serialized_message();
}
std::shared_ptr<rclcpp::SerializedMessage> GenericSubscription::create_serialized_message()
std::shared_ptr<rclcpp::SerializedMessage>
GenericSubscription::create_serialized_message()
{
return std::make_shared<rclcpp::SerializedMessage>(0);
}
void GenericSubscription::handle_message(
void
GenericSubscription::handle_message(
std::shared_ptr<void> &,
const rclcpp::MessageInfo &)
{
@@ -51,7 +54,8 @@ GenericSubscription::handle_serialized_message(
callback_(message);
}
void GenericSubscription::handle_loaned_message(
void
GenericSubscription::handle_loaned_message(
void * message, const rclcpp::MessageInfo & message_info)
{
(void) message;
@@ -60,16 +64,69 @@ void GenericSubscription::handle_loaned_message(
"handle_loaned_message is not implemented for GenericSubscription");
}
void GenericSubscription::return_message(std::shared_ptr<void> & message)
void
GenericSubscription::return_message(std::shared_ptr<void> & message)
{
auto typed_message = std::static_pointer_cast<rclcpp::SerializedMessage>(message);
return_serialized_message(typed_message);
}
void GenericSubscription::return_serialized_message(
void
GenericSubscription::return_serialized_message(
std::shared_ptr<rclcpp::SerializedMessage> & message)
{
message.reset();
}
// DYNAMIC TYPE ====================================================================================
// TODO(methylDragon): Reorder later
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr
GenericSubscription::get_shared_dynamic_message_type()
{
throw rclcpp::exceptions::UnimplementedError(
"get_shared_dynamic_message_type is not implemented for GenericSubscription");
}
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
GenericSubscription::get_shared_dynamic_message()
{
throw rclcpp::exceptions::UnimplementedError(
"get_shared_dynamic_message is not implemented for GenericSubscription");
}
rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr
GenericSubscription::get_shared_dynamic_serialization_support()
{
throw rclcpp::exceptions::UnimplementedError(
"get_shared_dynamic_serialization_support is not implemented for GenericSubscription");
}
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
GenericSubscription::create_dynamic_message()
{
throw rclcpp::exceptions::UnimplementedError(
"create_dynamic_message is not implemented for GenericSubscription");
}
void
GenericSubscription::return_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message)
{
(void) message;
throw rclcpp::exceptions::UnimplementedError(
"return_dynamic_message is not implemented for GenericSubscription");
}
void
GenericSubscription::handle_dynamic_message(
const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
const rclcpp::MessageInfo & message_info)
{
(void) message;
(void) message_info;
throw rclcpp::exceptions::UnimplementedError(
"handle_dynamic_message is not implemented for GenericSubscription");
}
} // namespace rclcpp

View File

@@ -125,4 +125,18 @@ Logger::set_level(Level level)
}
}
Logger::Level
Logger::get_effective_level() const
{
int logger_level = rcutils_logging_get_logger_effective_level(get_name());
if (logger_level < 0) {
exceptions::throw_from_rcl_error(
RCL_RET_ERROR, "Couldn't get logger level",
rcutils_get_error_state(), rcutils_reset_error);
}
return static_cast<Level>(logger_level);
}
} // namespace rclcpp

View File

@@ -789,3 +789,15 @@ rclcpp::TopicEndpointInfo::qos_profile() const
{
return qos_profile_;
}
rosidl_type_hash_t &
rclcpp::TopicEndpointInfo::topic_type_hash()
{
return topic_type_hash_;
}
const rosidl_type_hash_t &
rclcpp::TopicEndpointInfo::topic_type_hash() const
{
return topic_type_hash_;
}

View File

@@ -651,7 +651,7 @@ NodeParameters::undeclare_parameter(const std::string & name)
}
if (!parameter_info->second.descriptor.dynamic_typing) {
throw rclcpp::exceptions::InvalidParameterTypeException{
name, "cannot undeclare an statically typed parameter"};
name, "cannot undeclare a statically typed parameter"};
}
parameters_.erase(parameter_info);
@@ -824,7 +824,7 @@ NodeParameters::set_parameters_atomically(const std::vector<rclcpp::Parameter> &
auto it = parameters_.find(parameter.get_name());
if (it != parameters_.end() && rclcpp::PARAMETER_NOT_SET != it->second.value.get_type()) {
if (!it->second.descriptor.dynamic_typing) {
result.reason = "cannot undeclare an statically typed parameter";
result.reason = "cannot undeclare a statically typed parameter";
result.successful = false;
return result;
}

View File

@@ -37,7 +37,7 @@
#include "rclcpp/macros.hpp"
#include "rclcpp/network_flow_endpoint.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/event_handler.hpp"
using rclcpp::PublisherBase;
@@ -145,21 +145,48 @@ PublisherBase::bind_event_callbacks(
event_callbacks.liveliness_callback,
RCL_PUBLISHER_LIVELINESS_LOST);
}
QOSOfferedIncompatibleQoSCallbackType incompatible_qos_cb;
if (event_callbacks.incompatible_qos_callback) {
this->add_event_handler(
event_callbacks.incompatible_qos_callback,
RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
incompatible_qos_cb = event_callbacks.incompatible_qos_callback;
} else if (use_default_callbacks) {
// Register default callback when not specified
try {
this->add_event_handler(
[this](QOSOfferedIncompatibleQoSInfo & info) {
this->default_incompatible_qos_callback(info);
},
RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
} catch (UnsupportedEventTypeException & /*exc*/) {
// pass
incompatible_qos_cb = [this](QOSOfferedIncompatibleQoSInfo & info) {
this->default_incompatible_qos_callback(info);
};
}
try {
if (incompatible_qos_cb) {
this->add_event_handler(incompatible_qos_cb, RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_DEBUG(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for incompatible qos; wrong callback type");
}
IncompatibleTypeCallbackType incompatible_type_cb;
if (event_callbacks.incompatible_type_callback) {
incompatible_type_cb = event_callbacks.incompatible_type_callback;
} else if (use_default_callbacks) {
// Register default callback when not specified
incompatible_type_cb = [this](IncompatibleTypeInfo & info) {
this->default_incompatible_type_callback(info);
};
}
try {
if (incompatible_type_cb) {
this->add_event_handler(incompatible_type_cb, RCL_PUBLISHER_INCOMPATIBLE_TYPE);
}
} catch (UnsupportedEventTypeException & /*exc*/) {
RCLCPP_DEBUG(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for incompatible type; wrong callback type");
}
if (event_callbacks.matched_callback) {
this->add_event_handler(
event_callbacks.matched_callback,
RCL_PUBLISHER_MATCHED);
}
}
@@ -195,7 +222,7 @@ PublisherBase::get_publisher_handle() const
}
const
std::unordered_map<rcl_publisher_event_type_t, std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
std::unordered_map<rcl_publisher_event_type_t, std::shared_ptr<rclcpp::EventHandlerBase>> &
PublisherBase::get_event_handlers() const
{
return event_handlers_;
@@ -265,7 +292,7 @@ PublisherBase::assert_liveliness() const
bool
PublisherBase::can_loan_messages() const
{
return rcl_publisher_can_loan_messages(publisher_handle_.get());
return !intra_process_is_enabled_ && rcl_publisher_can_loan_messages(publisher_handle_.get());
}
bool
@@ -311,6 +338,17 @@ PublisherBase::default_incompatible_qos_callback(
policy_name.c_str());
}
void
PublisherBase::default_incompatible_type_callback(
rclcpp::IncompatibleTypeInfo & event) const
{
(void)event;
RCLCPP_WARN(
rclcpp::get_logger(rcl_node_get_logger_name(rcl_node_handle_.get())),
"Incompatible type on topic '%s', no messages will be sent to it.", get_topic_name());
}
std::vector<rclcpp::NetworkFlowEndpoint> PublisherBase::get_network_flow_endpoints() const
{
rcutils_allocator_t allocator = rcutils_get_default_allocator();

View File

@@ -22,6 +22,7 @@
#include "rclcpp/any_service_callback.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/qos.hpp"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
@@ -131,7 +132,7 @@ ServiceBase::set_on_new_request_callback(rcl_event_callback_t callback, const vo
user_data);
if (RCL_RET_OK != ret) {
using rclcpp::exceptions::throw_from_rcl_error;
throw_from_rcl_error(ret, "failed to set the on new request callback for service");
rclcpp::exceptions::throw_from_rcl_error(
ret, "failed to set the on new request callback for service");
}
}

View File

@@ -22,16 +22,19 @@
#include "rcpputils/scope_exit.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rclcpp/experimental/intra_process_manager.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/event_handler.hpp"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
#include "rosidl_dynamic_typesupport/types.h"
using rclcpp::SubscriptionBase;
SubscriptionBase::SubscriptionBase(
@@ -41,7 +44,7 @@ SubscriptionBase::SubscriptionBase(
const rcl_subscription_options_t & subscription_options,
const SubscriptionEventCallbacks & event_callbacks,
bool use_default_callbacks,
bool is_serialized)
SubscriptionType subscription_type)
: node_base_(node_base),
node_handle_(node_base_->get_shared_rcl_node_handle()),
node_logger_(rclcpp::get_node_logger(node_handle_.get())),
@@ -49,8 +52,16 @@ SubscriptionBase::SubscriptionBase(
intra_process_subscription_id_(0),
event_callbacks_(event_callbacks),
type_support_(type_support_handle),
is_serialized_(is_serialized)
subscription_type_(subscription_type)
{
if (!rmw_feature_supported(RMW_MIDDLEWARE_CAN_TAKE_DYNAMIC_MESSAGE) &&
subscription_type == rclcpp::SubscriptionType::DYNAMIC_MESSAGE_DIRECT)
{
throw std::runtime_error(
"Cannot set subscription to take dynamic message directly, feature not supported in rmw"
);
}
auto custom_deletor = [node_handle = this->node_handle_](rcl_subscription_t * rcl_subs)
{
if (rcl_subscription_fini(rcl_subs, node_handle.get()) != RCL_RET_OK) {
@@ -114,32 +125,58 @@ SubscriptionBase::bind_event_callbacks(
event_callbacks.deadline_callback,
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
}
if (event_callbacks.liveliness_callback) {
this->add_event_handler(
event_callbacks.liveliness_callback,
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
}
QOSRequestedIncompatibleQoSCallbackType incompatible_qos_cb;
if (event_callbacks.incompatible_qos_callback) {
this->add_event_handler(
event_callbacks.incompatible_qos_callback,
RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
incompatible_qos_cb = event_callbacks.incompatible_qos_callback;
} else if (use_default_callbacks) {
// Register default callback when not specified
try {
this->add_event_handler(
[this](QOSRequestedIncompatibleQoSInfo & info) {
this->default_incompatible_qos_callback(info);
},
RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
} catch (UnsupportedEventTypeException & /*exc*/) {
// pass
}
incompatible_qos_cb = [this](QOSRequestedIncompatibleQoSInfo & info) {
this->default_incompatible_qos_callback(info);
};
}
// Register default callback when not specified
try {
if (incompatible_qos_cb) {
this->add_event_handler(incompatible_qos_cb, RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
// pass
}
IncompatibleTypeCallbackType incompatible_type_cb;
if (event_callbacks.incompatible_type_callback) {
incompatible_type_cb = event_callbacks.incompatible_type_callback;
} else if (use_default_callbacks) {
// Register default callback when not specified
incompatible_type_cb = [this](IncompatibleTypeInfo & info) {
this->default_incompatible_type_callback(info);
};
}
try {
if (incompatible_type_cb) {
this->add_event_handler(incompatible_type_cb, RCL_SUBSCRIPTION_INCOMPATIBLE_TYPE);
}
} catch (UnsupportedEventTypeException & /*exc*/) {
// pass
}
if (event_callbacks.message_lost_callback) {
this->add_event_handler(
event_callbacks.message_lost_callback,
RCL_SUBSCRIPTION_MESSAGE_LOST);
}
if (event_callbacks.matched_callback) {
this->add_event_handler(
event_callbacks.matched_callback,
RCL_SUBSCRIPTION_MATCHED);
}
}
const char *
@@ -161,7 +198,7 @@ SubscriptionBase::get_subscription_handle() const
}
const
std::unordered_map<rcl_subscription_event_type_t, std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
std::unordered_map<rcl_subscription_event_type_t, std::shared_ptr<rclcpp::EventHandlerBase>> &
SubscriptionBase::get_event_handlers() const
{
return event_handlers_;
@@ -232,7 +269,13 @@ SubscriptionBase::get_message_type_support_handle() const
bool
SubscriptionBase::is_serialized() const
{
return is_serialized_;
return subscription_type_ == rclcpp::SubscriptionType::SERIALIZED_MESSAGE;
}
rclcpp::SubscriptionType
SubscriptionBase::get_subscription_type() const
{
return subscription_type_;
}
size_t
@@ -299,6 +342,17 @@ SubscriptionBase::default_incompatible_qos_callback(
policy_name.c_str());
}
void
SubscriptionBase::default_incompatible_type_callback(
rclcpp::IncompatibleTypeInfo & event) const
{
(void)event;
RCLCPP_WARN(
rclcpp::get_logger(rcl_node_get_logger_name(node_handle_.get())),
"Incompatible type on topic '%s', no messages will be sent to it.", get_topic_name());
}
bool
SubscriptionBase::matches_any_intra_process_publishers(const rmw_gid_t * sender_gid) const
{
@@ -405,8 +459,7 @@ SubscriptionBase::set_content_filter(
rcl_subscription_content_filter_options_t options =
rcl_get_zero_initialized_subscription_content_filter_options();
std::vector<const char *> cstrings =
get_c_vector_string(expression_parameters);
std::vector<const char *> cstrings = get_c_vector_string(expression_parameters);
rcl_ret_t ret = rcl_subscription_content_filter_options_init(
subscription_handle_.get(),
get_c_string(filter_expression),
@@ -478,3 +531,14 @@ SubscriptionBase::get_content_filter() const
return ret_options;
}
// DYNAMIC TYPE ==================================================================================
bool
SubscriptionBase::take_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage & /*message_out*/,
rclcpp::MessageInfo & /*message_info_out*/)
{
throw std::runtime_error("Unimplemented");
return false;
}

View File

@@ -444,17 +444,23 @@ function(test_generic_pubsub_for_rmw_implementation)
endif()
endfunction()
call_for_each_rmw_implementation(test_generic_pubsub_for_rmw_implementation)
ament_add_gtest(test_qos_event test_qos_event.cpp)
if(TARGET test_qos_event)
ament_target_dependencies(test_qos_event
"rmw"
"test_msgs"
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}
)
target_link_libraries(test_qos_event
${PROJECT_NAME}
mimick
)
endif()
if(TARGET test_qos_event${target_suffix})
target_link_libraries(test_qos_event${target_suffix} ${PROJECT_NAME} mimick)
ament_target_dependencies(test_qos_event${target_suffix}
"rmw"
"rosidl_typesupport_cpp"
"test_msgs"
)
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)
target_link_libraries(test_qos_overriding_options
@@ -508,6 +514,17 @@ if(TARGET test_service)
)
target_link_libraries(test_service ${PROJECT_NAME} mimick)
endif()
ament_add_gmock(test_service_introspection test_service_introspection.cpp)
if(TARGET test_service)
ament_target_dependencies(test_service_introspection
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_service_introspection ${PROJECT_NAME} mimick)
endif()
# Creating and destroying nodes is slow with Connext, so this needs larger timeout.
ament_add_gtest(test_subscription test_subscription.cpp TIMEOUT 120)
if(TARGET test_subscription)

View File

@@ -593,3 +593,106 @@ TEST(TestExecutors, testSpinUntilFutureCompleteNodePtr) {
rclcpp::shutdown();
}
template<typename T>
class TestIntraprocessExecutors : public ::testing::Test
{
public:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void SetUp()
{
const auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
std::stringstream test_name;
test_name << test_info->test_case_name() << "_" << test_info->name();
node = std::make_shared<rclcpp::Node>("node", test_name.str());
callback_count = 0;
const std::string topic_name = std::string("topic_") + test_name.str();
rclcpp::PublisherOptions po;
po.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
publisher = node->create_publisher<test_msgs::msg::Empty>(topic_name, rclcpp::QoS(1), po);
auto callback = [this](test_msgs::msg::Empty::ConstSharedPtr) {
this->callback_count.fetch_add(1);
};
rclcpp::SubscriptionOptions so;
so.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
subscription =
node->create_subscription<test_msgs::msg::Empty>(
topic_name, rclcpp::QoS(kNumMessages), std::move(callback), so);
}
void TearDown()
{
publisher.reset();
subscription.reset();
node.reset();
}
const size_t kNumMessages = 100;
rclcpp::Node::SharedPtr node;
rclcpp::Publisher<test_msgs::msg::Empty>::SharedPtr publisher;
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
std::atomic_int callback_count;
};
TYPED_TEST_SUITE(TestIntraprocessExecutors, ExecutorTypes, ExecutorTypeNames);
TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) {
// This tests that executors will continue to service intraprocess subscriptions in the case
// that publishers aren't continuing to publish.
// This was previously broken in that intraprocess guard conditions were only triggered on
// publish and the test was added to prevent future regressions.
const size_t kNumMessages = 100;
using ExecutorType = TypeParam;
ExecutorType executor;
executor.add_node(this->node);
EXPECT_EQ(0, this->callback_count.load());
this->publisher->publish(test_msgs::msg::Empty());
// Wait for up to 5 seconds for the first message to come available.
const std::chrono::milliseconds sleep_per_loop(10);
int loops = 0;
while (1u != this->callback_count.load() && loops < 500) {
rclcpp::sleep_for(sleep_per_loop);
executor.spin_some();
loops++;
}
EXPECT_EQ(1u, this->callback_count.load());
// reset counter
this->callback_count.store(0);
for (size_t ii = 0; ii < kNumMessages; ++ii) {
this->publisher->publish(test_msgs::msg::Empty());
}
// Fire a timer every 10ms up to 5 seconds waiting for subscriptions to be read.
loops = 0;
auto timer = this->node->create_wall_timer(
std::chrono::milliseconds(10), [this, &executor, &loops, &kNumMessages]() {
loops++;
if (kNumMessages == this->callback_count.load() ||
loops == 500)
{
executor.cancel();
}
});
executor.spin();
EXPECT_EQ(kNumMessages, this->callback_count.load());
}

View File

@@ -28,6 +28,7 @@
#include "rclcpp/node_interfaces/node_graph.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcutils/strdup.h"
#include "test_msgs/msg/empty.h"
#include "test_msgs/msg/empty.hpp"
#include "test_msgs/srv/empty.hpp"
@@ -599,6 +600,18 @@ TEST_F(TestNodeGraph, get_info_by_topic)
rclcpp::QoS const_actual_qos = const_publisher_endpoint_info.qos_profile();
EXPECT_EQ(const_actual_qos.reliability(), rclcpp::ReliabilityPolicy::Reliable);
const rosidl_type_hash_t expected_type_hash = *test_msgs__msg__Empty__get_type_hash(nullptr);
EXPECT_EQ(
0, memcmp(
&publisher_endpoint_info.topic_type_hash(),
&expected_type_hash,
sizeof(rosidl_type_hash_t)));
EXPECT_EQ(
0, memcmp(
&const_publisher_endpoint_info.topic_type_hash(),
&expected_type_hash,
sizeof(rosidl_type_hash_t)));
auto endpoint_gid = publisher_endpoint_info.endpoint_gid();
auto const_endpoint_gid = const_publisher_endpoint_info.endpoint_gid();
bool endpoint_gid_is_all_zeros = true;

View File

@@ -28,6 +28,11 @@
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
using rclcpp::dynamic_typesupport::DynamicMessage;
using rclcpp::dynamic_typesupport::DynamicMessageType;
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
namespace
{
@@ -77,6 +82,18 @@ public:
const std::shared_ptr<rclcpp::SerializedMessage> &, const rclcpp::MessageInfo &) override {}
void return_message(std::shared_ptr<void> &) override {}
void return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> &) override {}
DynamicMessageType::SharedPtr get_shared_dynamic_message_type() override {return nullptr;}
DynamicMessage::SharedPtr get_shared_dynamic_message() override {return nullptr;}
DynamicSerializationSupport::SharedPtr get_shared_dynamic_serialization_support() override
{
return nullptr;
}
DynamicMessage::SharedPtr create_dynamic_message() override {return nullptr;}
void return_dynamic_message(DynamicMessage::SharedPtr &) override {}
void handle_dynamic_message(
const DynamicMessage::SharedPtr &,
const rclcpp::MessageInfo &) override {}
};
class TestNodeTopics : public ::testing::Test

View File

@@ -63,6 +63,57 @@ public:
}
};
static bool test_waitable_result2 = false;
class TestWaitable2 : public rclcpp::Waitable
{
public:
explicit TestWaitable2(rcl_publisher_t * pub_ptr)
: pub_ptr_(pub_ptr),
pub_event_(rcl_get_zero_initialized_event())
{
EXPECT_EQ(
rcl_publisher_event_init(&pub_event_, pub_ptr_, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED),
RCL_RET_OK);
}
~TestWaitable2()
{
EXPECT_EQ(rcl_event_fini(&pub_event_), RCL_RET_OK);
}
void add_to_wait_set(rcl_wait_set_t * wait_set) override
{
EXPECT_EQ(rcl_wait_set_add_event(wait_set, &pub_event_, &wait_set_event_index_), RCL_RET_OK);
}
bool is_ready(rcl_wait_set_t *) override
{
return test_waitable_result2;
}
std::shared_ptr<void>
take_data() override
{
return nullptr;
}
void execute(std::shared_ptr<void> & data) override
{
(void) data;
}
size_t get_number_of_ready_events() override
{
return 1;
}
private:
rcl_publisher_t * pub_ptr_;
rcl_event_t pub_event_;
size_t wait_set_event_index_;
};
struct RclWaitSetSizes
{
size_t size_of_subscriptions = 0;
@@ -497,8 +548,8 @@ TEST_F(TestAllocatorMemoryStrategy, add_remove_waitables) {
TEST_F(TestAllocatorMemoryStrategy, number_of_entities_with_subscription) {
RclWaitSetSizes expected_sizes = {};
expected_sizes.size_of_subscriptions = 1;
expected_sizes.size_of_events = 1;
expected_sizes.size_of_waitables = 1;
expected_sizes.size_of_events = 2;
expected_sizes.size_of_waitables = 2;
auto node_with_subscription = create_node_with_subscription("subscription_node");
EXPECT_TRUE(TestNumberOfEntitiesAfterCollection(node_with_subscription, expected_sizes));
}
@@ -657,20 +708,129 @@ TEST_F(TestAllocatorMemoryStrategy, get_next_timer) {
}
TEST_F(TestAllocatorMemoryStrategy, get_next_waitable) {
auto node1 = std::make_shared<rclcpp::Node>("waitable_node", "ns");
auto node2 = std::make_shared<rclcpp::Node>("waitable_node2", "ns");
rclcpp::Waitable::SharedPtr waitable1 = std::make_shared<TestWaitable>();
rclcpp::Waitable::SharedPtr waitable2 = std::make_shared<TestWaitable>();
node1->get_node_waitables_interface()->add_waitable(waitable1, nullptr);
node2->get_node_waitables_interface()->add_waitable(waitable2, nullptr);
auto get_next_entity = [this](const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) {
rclcpp::AnyExecutable result;
allocator_memory_strategy()->get_next_waitable(result, weak_groups_to_nodes);
return result;
};
EXPECT_TRUE(TestGetNextEntity(node1, node2, get_next_entity));
{
auto node1 = std::make_shared<rclcpp::Node>(
"waitable_node", "ns",
rclcpp::NodeOptions()
.start_parameter_event_publisher(false)
.start_parameter_services(false));
rclcpp::PublisherOptions pub_options;
pub_options.use_default_callbacks = false;
auto pub1 = node1->create_publisher<test_msgs::msg::Empty>(
"test_topic_1", rclcpp::QoS(10), pub_options);
auto waitable1 =
std::make_shared<TestWaitable2>(pub1->get_publisher_handle().get());
node1->get_node_waitables_interface()->add_waitable(waitable1, nullptr);
auto basic_node = create_node_with_disabled_callback_groups("basic_node");
WeakCallbackGroupsToNodesMap weak_groups_to_nodes;
basic_node->for_each_callback_group(
[basic_node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
group_ptr,
basic_node->get_node_base_interface()));
});
node1->for_each_callback_group(
[node1, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
group_ptr,
node1->get_node_base_interface()));
});
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
ASSERT_EQ(
rcl_wait_set_init(
&wait_set,
allocator_memory_strategy()->number_of_ready_subscriptions(),
allocator_memory_strategy()->number_of_guard_conditions(),
allocator_memory_strategy()->number_of_ready_timers(),
allocator_memory_strategy()->number_of_ready_clients(),
allocator_memory_strategy()->number_of_ready_services(),
allocator_memory_strategy()->number_of_ready_events(),
rclcpp::contexts::get_global_default_context()->get_rcl_context().get(),
allocator_memory_strategy()->get_allocator()),
RCL_RET_OK);
ASSERT_TRUE(allocator_memory_strategy()->add_handles_to_wait_set(&wait_set));
ASSERT_EQ(
rcl_wait(
&wait_set,
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds(100))
.count()),
RCL_RET_OK);
test_waitable_result2 = true;
allocator_memory_strategy()->remove_null_handles(&wait_set);
rclcpp::AnyExecutable result = get_next_entity(weak_groups_to_nodes);
EXPECT_EQ(result.node_base, node1->get_node_base_interface());
test_waitable_result2 = false;
EXPECT_EQ(rcl_wait_set_fini(&wait_set), RCL_RET_OK);
}
{
auto node2 = std::make_shared<rclcpp::Node>(
"waitable_node2", "ns",
rclcpp::NodeOptions()
.start_parameter_services(false)
.start_parameter_event_publisher(false));
rclcpp::PublisherOptions pub_options;
pub_options.use_default_callbacks = false;
auto pub2 = node2->create_publisher<test_msgs::msg::Empty>(
"test_topic_2", rclcpp::QoS(10), pub_options);
auto waitable2 =
std::make_shared<TestWaitable2>(pub2->get_publisher_handle().get());
node2->get_node_waitables_interface()->add_waitable(waitable2, nullptr);
auto basic_node2 = std::make_shared<rclcpp::Node>(
"basic_node2", "ns",
rclcpp::NodeOptions()
.start_parameter_services(false)
.start_parameter_event_publisher(false));
WeakCallbackGroupsToNodesMap weak_groups_to_uncollected_nodes;
basic_node2->for_each_callback_group(
[basic_node2, &weak_groups_to_uncollected_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_uncollected_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
group_ptr,
basic_node2->get_node_base_interface()));
});
node2->for_each_callback_group(
[node2,
&weak_groups_to_uncollected_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
{
weak_groups_to_uncollected_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
group_ptr,
node2->get_node_base_interface()));
});
rclcpp::AnyExecutable failed_result = get_next_entity(weak_groups_to_uncollected_nodes);
EXPECT_EQ(failed_result.node_base, nullptr);
}
}
TEST_F(TestAllocatorMemoryStrategy, get_next_subscription_mutually_exclusive) {

View File

@@ -393,6 +393,7 @@ TEST(TestFunctionTraits, argument_types) {
auto bind_one_bool = std::bind(
&ObjectMember::callback_one_bool, &object_member, std::placeholders::_1);
(void)bind_one_bool; // to quiet clang
static_assert(
std::is_same<
@@ -402,6 +403,7 @@ TEST(TestFunctionTraits, argument_types) {
auto bind_one_bool_const = std::bind(
&ObjectMember::callback_one_bool_const, &object_member, std::placeholders::_1);
(void)bind_one_bool_const; // to quiet clang
static_assert(
std::is_same<
@@ -413,6 +415,7 @@ TEST(TestFunctionTraits, argument_types) {
auto bind_two_bools = std::bind(
&ObjectMember::callback_two_bools, &object_member, std::placeholders::_1,
std::placeholders::_2);
(void)bind_two_bools; // to quiet clang
static_assert(
std::is_same<
@@ -429,6 +432,7 @@ TEST(TestFunctionTraits, argument_types) {
auto bind_one_bool_one_float = std::bind(
&ObjectMember::callback_one_bool_one_float, &object_member, std::placeholders::_1,
std::placeholders::_2);
(void)bind_one_bool_one_float; // to quiet clang
static_assert(
std::is_same<
@@ -447,6 +451,7 @@ TEST(TestFunctionTraits, argument_types) {
>::value, "Functor accepts a float as second argument");
auto bind_one_int = std::bind(func_one_int, std::placeholders::_1);
(void)bind_one_int; // to quiet clang
static_assert(
std::is_same<
@@ -455,6 +460,7 @@ TEST(TestFunctionTraits, argument_types) {
>::value, "Functor accepts an int as first argument");
auto bind_two_ints = std::bind(func_two_ints, std::placeholders::_1, std::placeholders::_2);
(void)bind_two_ints; // to quiet clang
static_assert(
std::is_same<
@@ -470,6 +476,7 @@ TEST(TestFunctionTraits, argument_types) {
auto bind_one_int_one_char = std::bind(
func_one_int_one_char, std::placeholders::_1, std::placeholders::_2);
(void)bind_one_int_one_char; // to quiet clang
static_assert(
std::is_same<
@@ -530,18 +537,21 @@ TEST(TestFunctionTraits, check_arguments) {
(void)one;
return 1;
};
(void)lambda_one_int; // to quiet clang
auto lambda_two_ints = [](int one, int two) {
(void)one;
(void)two;
return 2;
};
(void)lambda_two_ints; // to quiet clang
auto lambda_one_int_one_char = [](int one, char two) {
(void)one;
(void)two;
return 3;
};
(void)lambda_one_int_one_char; // to quiet clang
static_assert(
rclcpp::function_traits::check_arguments<decltype(lambda_one_int), int>::value,
@@ -572,6 +582,7 @@ TEST(TestFunctionTraits, check_arguments) {
auto bind_one_bool = std::bind(
&ObjectMember::callback_one_bool, &object_member, std::placeholders::_1);
(void)bind_one_bool; // to quiet clang
// Test std::bind functions
static_assert(
@@ -580,6 +591,7 @@ TEST(TestFunctionTraits, check_arguments) {
auto bind_one_bool_const = std::bind(
&ObjectMember::callback_one_bool_const, &object_member, std::placeholders::_1);
(void)bind_one_bool_const; // to quiet clang
// Test std::bind functions
static_assert(
@@ -745,6 +757,7 @@ TEST_F(TestMember, bind_member_functor) {
auto bind_member_functor = std::bind(
&TestMember::MemberFunctor, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3);
(void)bind_member_functor; // to quiet clang
static_assert(
rclcpp::function_traits::check_arguments<decltype(bind_member_functor), int, float,

View File

@@ -186,7 +186,6 @@ TEST_F(TestLoanedMessage, move_loaned_message) {
auto loaned_msg_moved_to = LoanedMessageT(std::move(loaned_msg_to_move));
ASSERT_TRUE(loaned_msg_moved_to.is_valid());
ASSERT_FALSE(loaned_msg_to_move.is_valid());
loaned_msg_moved_to.get().float32_value = 42.0f;
ASSERT_EQ(42.0f, loaned_msg_moved_to.get().float32_value);

View File

@@ -160,6 +160,50 @@ TEST(TestLogger, set_level) {
EXPECT_EQ(RCUTILS_RET_OK, rcutils_logging_shutdown());
}
TEST(TestLogger, get_effective_level) {
ASSERT_EQ(RCUTILS_RET_OK, rcutils_logging_initialize());
rclcpp::Logger logger = rclcpp::get_logger("test_logger");
rclcpp::Logger child_logger = rclcpp::get_logger("test_logger.child");
// set child logger level unset to test effective level
child_logger.set_level(rclcpp::Logger::Level::Unset);
// default
EXPECT_EQ(rclcpp::Logger::Level::Info, logger.get_effective_level());
EXPECT_EQ(rclcpp::Logger::Level::Info, child_logger.get_effective_level());
// unset
logger.set_level(rclcpp::Logger::Level::Unset);
EXPECT_EQ(rclcpp::Logger::Level::Info, logger.get_effective_level());
EXPECT_EQ(rclcpp::Logger::Level::Info, child_logger.get_effective_level());
// debug
logger.set_level(rclcpp::Logger::Level::Debug);
EXPECT_EQ(rclcpp::Logger::Level::Debug, logger.get_effective_level());
EXPECT_EQ(rclcpp::Logger::Level::Debug, child_logger.get_effective_level());
// info
logger.set_level(rclcpp::Logger::Level::Info);
EXPECT_EQ(rclcpp::Logger::Level::Info, logger.get_effective_level());
EXPECT_EQ(rclcpp::Logger::Level::Info, child_logger.get_effective_level());
// warn
logger.set_level(rclcpp::Logger::Level::Warn);
EXPECT_EQ(rclcpp::Logger::Level::Warn, logger.get_effective_level());
EXPECT_EQ(rclcpp::Logger::Level::Warn, child_logger.get_effective_level());
// error
logger.set_level(rclcpp::Logger::Level::Error);
EXPECT_EQ(rclcpp::Logger::Level::Error, logger.get_effective_level());
EXPECT_EQ(rclcpp::Logger::Level::Error, child_logger.get_effective_level());
// fatal
logger.set_level(rclcpp::Logger::Level::Fatal);
EXPECT_EQ(rclcpp::Logger::Level::Fatal, logger.get_effective_level());
EXPECT_EQ(rclcpp::Logger::Level::Fatal, child_logger.get_effective_level());
}
TEST(TestLogger, get_logging_directory) {
ASSERT_EQ(true, rcutils_set_env("HOME", "/fake_home_dir"));
ASSERT_EQ(true, rcutils_set_env("USERPROFILE", nullptr));

View File

@@ -409,9 +409,7 @@ TEST_F(TestPublisher, intra_process_publish_failures) {
std::allocator<void> allocator;
{
rclcpp::LoanedMessage<test_msgs::msg::Empty> loaned_msg(*publisher, allocator);
RCLCPP_EXPECT_THROW_EQ(
publisher->publish(std::move(loaned_msg)),
std::runtime_error("storing loaned messages in intra process is not supported yet"));
EXPECT_NO_THROW(publisher->publish(std::move(loaned_msg)));
}
{

View File

@@ -14,6 +14,7 @@
#include <gtest/gtest.h>
#include <atomic>
#include <chrono>
#include <functional>
#include <future>
@@ -232,7 +233,7 @@ TEST_F(TestQosEvent, construct_destruct_rcl_error) {
auto throwing_statement = [callback, rcl_handle, event_type]() {
// reset() is not needed for the exception, but it handles unused return value warning
std::make_shared<
rclcpp::QOSEventHandler<decltype(callback), std::shared_ptr<rcl_publisher_t>>>(
rclcpp::EventHandler<decltype(callback), std::shared_ptr<rcl_publisher_t>>>(
callback, rcl_publisher_event_init, rcl_handle, event_type).reset();
};
// This is done through a lambda because the compiler is having trouble parsing the templated
@@ -248,7 +249,7 @@ TEST_F(TestQosEvent, construct_destruct_rcl_error) {
auto throwing_statement = [callback, rcl_handle, event_type]() {
// reset() is needed for this exception
std::make_shared<
rclcpp::QOSEventHandler<decltype(callback), std::shared_ptr<rcl_publisher_t>>>(
rclcpp::EventHandler<decltype(callback), std::shared_ptr<rcl_publisher_t>>>(
callback, rcl_publisher_event_init, rcl_handle, event_type).reset();
};
@@ -267,7 +268,7 @@ TEST_F(TestQosEvent, execute) {
auto callback = [&handler_callback_executed](int) {handler_callback_executed = true;};
rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
rclcpp::QOSEventHandler<decltype(callback), decltype(rcl_handle)> handler(
rclcpp::EventHandler<decltype(callback), decltype(rcl_handle)> handler(
callback, rcl_publisher_event_init, rcl_handle, event_type);
std::shared_ptr<void> data = handler.take_data();
@@ -292,7 +293,7 @@ TEST_F(TestQosEvent, add_to_wait_set) {
auto callback = [](int) {};
rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
rclcpp::QOSEventHandler<decltype(callback), decltype(rcl_handle)> handler(
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());
@@ -313,6 +314,11 @@ TEST_F(TestQosEvent, add_to_wait_set) {
TEST_F(TestQosEvent, test_on_new_event_callback)
{
// rmw_connextdds doesn't support rmw_event_set_callback() interface
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto offered_deadline = rclcpp::Duration(std::chrono::milliseconds(1));
auto requested_deadline = rclcpp::Duration(std::chrono::milliseconds(2));
@@ -354,6 +360,11 @@ TEST_F(TestQosEvent, test_on_new_event_callback)
TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
{
// rmw_connextdds doesn't support rmw_event_set_callback() interface
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
auto pub = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
auto sub = node->create_subscription<test_msgs::msg::Empty>(topic_name, 10, message_callback);
auto dummy_cb = [](size_t count_events) {(void)count_events;};
@@ -376,6 +387,12 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
EXPECT_NO_THROW(
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS));
EXPECT_NO_THROW(
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_MATCHED));
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));
@@ -394,6 +411,12 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
EXPECT_NO_THROW(
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS));
EXPECT_NO_THROW(
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_MATCHED));
EXPECT_NO_THROW(
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_MATCHED));
std::function<void(size_t)> invalid_cb;
rclcpp::SubscriptionOptions sub_options;
@@ -413,3 +436,170 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
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)
{
// rmw_connextdds doesn't support rmw_event_set_callback() interface
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
std::atomic_size_t matched_count = 0;
rclcpp::PublisherOptions pub_options;
pub_options.event_callbacks.matched_callback = [](auto) {};
auto pub = node->create_publisher<test_msgs::msg::Empty>(
topic_name, 10, pub_options);
auto matched_event_callback = [&matched_count](size_t count) {
matched_count += count;
};
pub->set_on_new_qos_event_callback(matched_event_callback, RCL_PUBLISHER_MATCHED);
rclcpp::executors::SingleThreadedExecutor ex;
ex.add_node(node->get_node_base_interface());
const auto timeout = std::chrono::milliseconds(200);
{
auto sub1 = node->create_subscription<test_msgs::msg::Empty>(topic_name, 10, message_callback);
ex.spin_some(timeout);
EXPECT_EQ(matched_count, static_cast<size_t>(1));
{
auto sub2 = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback);
ex.spin_some(timeout);
EXPECT_EQ(matched_count, static_cast<size_t>(2));
}
ex.spin_some(timeout);
EXPECT_EQ(matched_count, static_cast<size_t>(3));
}
ex.spin_some(timeout);
EXPECT_EQ(matched_count, static_cast<size_t>(4));
}
TEST_F(TestQosEvent, test_sub_matched_event_by_set_event_callback)
{
// rmw_connextdds doesn't support rmw_event_set_callback() interface
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
GTEST_SKIP();
}
std::atomic_size_t matched_count = 0;
rclcpp::SubscriptionOptions sub_options;
sub_options.event_callbacks.matched_callback = [](auto) {};
auto sub = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, sub_options);
auto matched_event_callback = [&matched_count](size_t count) {
matched_count += count;
};
sub->set_on_new_qos_event_callback(matched_event_callback, RCL_SUBSCRIPTION_MATCHED);
rclcpp::executors::SingleThreadedExecutor ex;
ex.add_node(node->get_node_base_interface());
const auto timeout = std::chrono::milliseconds(200);
{
auto pub1 = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
ex.spin_some(timeout);
EXPECT_EQ(matched_count, static_cast<size_t>(1));
{
auto pub2 = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
ex.spin_some(timeout);
EXPECT_EQ(matched_count, static_cast<size_t>(2));
}
ex.spin_some(timeout);
EXPECT_EQ(matched_count, static_cast<size_t>(3));
}
ex.spin_some(timeout);
EXPECT_EQ(matched_count, static_cast<size_t>(4));
}
TEST_F(TestQosEvent, test_pub_matched_event_by_option_event_callback)
{
rmw_matched_status_t matched_expected_result;
rclcpp::PublisherOptions pub_options;
pub_options.event_callbacks.matched_callback =
[&matched_expected_result](rmw_matched_status_t & s) {
EXPECT_EQ(s.total_count, matched_expected_result.total_count);
EXPECT_EQ(s.total_count_change, matched_expected_result.total_count_change);
EXPECT_EQ(s.current_count, matched_expected_result.current_count);
EXPECT_EQ(s.current_count_change, matched_expected_result.current_count_change);
};
auto pub = node->create_publisher<test_msgs::msg::Empty>(
topic_name, 10, pub_options);
rclcpp::executors::SingleThreadedExecutor ex;
ex.add_node(node->get_node_base_interface());
// Create a connected subscription
matched_expected_result.total_count = 1;
matched_expected_result.total_count_change = 1;
matched_expected_result.current_count = 1;
matched_expected_result.current_count_change = 1;
const auto timeout = std::chrono::milliseconds(200);
{
auto sub = node->create_subscription<test_msgs::msg::Empty>(topic_name, 10, message_callback);
ex.spin_some(timeout);
// destroy a connected subscription
matched_expected_result.total_count = 1;
matched_expected_result.total_count_change = 0;
matched_expected_result.current_count = 0;
matched_expected_result.current_count_change = -1;
}
ex.spin_some(timeout);
}
TEST_F(TestQosEvent, test_sub_matched_event_by_option_event_callback)
{
rmw_matched_status_t matched_expected_result;
rclcpp::SubscriptionOptions sub_options;
sub_options.event_callbacks.matched_callback =
[&matched_expected_result](rmw_matched_status_t & s) {
EXPECT_EQ(s.total_count, matched_expected_result.total_count);
EXPECT_EQ(s.total_count_change, matched_expected_result.total_count_change);
EXPECT_EQ(s.current_count, matched_expected_result.current_count);
EXPECT_EQ(s.current_count_change, matched_expected_result.current_count_change);
};
auto sub = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, sub_options);
rclcpp::executors::SingleThreadedExecutor ex;
ex.add_node(node->get_node_base_interface());
// Create a connected publisher
matched_expected_result.total_count = 1;
matched_expected_result.total_count_change = 1;
matched_expected_result.current_count = 1;
matched_expected_result.current_count_change = 1;
const auto timeout = std::chrono::milliseconds(200);
{
auto pub1 = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
ex.spin_some(timeout);
// destroy a connected publisher
matched_expected_result.total_count = 1;
matched_expected_result.total_count_change = 0;
matched_expected_result.current_count = 0;
matched_expected_result.current_count_change = -1;
}
ex.spin_some(timeout);
}

View File

@@ -67,8 +67,6 @@ TEST(TestSerializedMessage, various_constructors) {
rclcpp::SerializedMessage yet_another_serialized_message(std::move(other_serialized_message));
auto & yet_another_rcl_handle = yet_another_serialized_message.get_rcl_serialized_message();
EXPECT_TRUE(nullptr == other_rcl_handle.buffer);
EXPECT_EQ(0u, other_serialized_message.capacity());
EXPECT_EQ(0u, other_serialized_message.size());
EXPECT_TRUE(nullptr != yet_another_rcl_handle.buffer);
EXPECT_EQ(content_size, yet_another_serialized_message.size());
EXPECT_EQ(content_size, yet_another_serialized_message.capacity());

View File

@@ -0,0 +1,339 @@
// Copyright 2022 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <rcl/service_introspection.h>
#include <rmw/rmw.h>
#include <map>
#include <string>
#include "gmock/gmock.h"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/node_options.hpp"
#include "rclcpp/parameter.hpp"
#include "../mocking_utils/patch.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
#include "test_msgs/srv/basic_types.hpp"
#include "service_msgs/msg/service_event_info.hpp"
using namespace std::chrono_literals;
using test_msgs::srv::BasicTypes;
using service_msgs::msg::ServiceEventInfo;
class TestServiceIntrospection : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void SetUp()
{
node = std::make_shared<rclcpp::Node>(
"my_node", "/ns");
auto srv_callback =
[](const BasicTypes::Request::SharedPtr & req, const BasicTypes::Response::SharedPtr & resp) {
resp->set__bool_value(!req->bool_value);
resp->set__int64_value(req->int64_value);
return resp;
};
auto callback = [this](const std::shared_ptr<const BasicTypes::Event> & msg) {
events.push_back(msg);
(void)msg;
};
client = node->create_client<BasicTypes>("service");
service = node->create_service<BasicTypes>("service", srv_callback);
sub = node->create_subscription<BasicTypes::Event>("service/_service_event", 10, callback);
events.clear();
}
void TearDown()
{
node.reset();
}
rclcpp::Node::SharedPtr node;
rclcpp::Client<BasicTypes>::SharedPtr client;
rclcpp::Service<BasicTypes>::SharedPtr service;
rclcpp::Subscription<BasicTypes::Event>::SharedPtr sub;
std::vector<std::shared_ptr<const BasicTypes::Event>> events;
std::chrono::milliseconds timeout = std::chrono::milliseconds(1000);
};
TEST_F(TestServiceIntrospection, service_introspection_nominal)
{
auto request = std::make_shared<BasicTypes::Request>();
request->set__bool_value(true);
request->set__int64_value(42);
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
auto future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
BasicTypes::Response::SharedPtr response = future.get();
ASSERT_EQ(response->bool_value, false);
ASSERT_EQ(response->int64_value, 42);
// wrap up work to get all the service_event messages
auto start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
}
std::map<uint8_t, std::shared_ptr<const BasicTypes::Event>> event_map;
for (auto & event : events) {
event_map[event->info.event_type] = event;
}
ASSERT_EQ(event_map.size(), 4U);
rmw_gid_t client_gid;
rmw_get_gid_for_client(rcl_client_get_rmw_handle(client->get_client_handle().get()), &client_gid);
std::array<uint8_t, RMW_GID_STORAGE_SIZE> client_gid_arr;
std::move(std::begin(client_gid.data), std::end(client_gid.data), client_gid_arr.begin());
ASSERT_THAT(
client_gid_arr,
testing::Eq(event_map[ServiceEventInfo::REQUEST_SENT]->info.client_gid));
ASSERT_EQ(
event_map[ServiceEventInfo::REQUEST_SENT]->info.sequence_number,
event_map[ServiceEventInfo::REQUEST_RECEIVED]->info.sequence_number);
ASSERT_EQ(
event_map[ServiceEventInfo::RESPONSE_SENT]->info.sequence_number,
event_map[ServiceEventInfo::RESPONSE_RECEIVED]->info.sequence_number);
ASSERT_EQ(
event_map[ServiceEventInfo::REQUEST_SENT]->info.sequence_number,
event_map[ServiceEventInfo::RESPONSE_SENT]->info.sequence_number);
ASSERT_EQ(
event_map[ServiceEventInfo::REQUEST_RECEIVED]->info.sequence_number,
event_map[ServiceEventInfo::RESPONSE_RECEIVED]->info.sequence_number);
ASSERT_EQ(event_map[ServiceEventInfo::REQUEST_SENT]->request[0].int64_value, 42);
ASSERT_EQ(event_map[ServiceEventInfo::REQUEST_SENT]->request[0].bool_value, true);
ASSERT_EQ(event_map[ServiceEventInfo::RESPONSE_SENT]->response[0].int64_value, 42);
ASSERT_EQ(event_map[ServiceEventInfo::RESPONSE_SENT]->response[0].bool_value, false);
ASSERT_EQ(event_map[ServiceEventInfo::RESPONSE_SENT]->request.size(), 0U);
ASSERT_EQ(event_map[ServiceEventInfo::REQUEST_RECEIVED]->response.size(), 0U);
}
TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
{
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
auto request = std::make_shared<BasicTypes::Request>();
request->set__bool_value(true);
request->set__int64_value(42);
auto future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
auto start = std::chrono::steady_clock::now();
while ((std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
}
EXPECT_EQ(events.size(), 0U);
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 2 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
}
EXPECT_EQ(events.size(), 2U);
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 2 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
}
EXPECT_EQ(events.size(), 2U);
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
}
EXPECT_EQ(events.size(), 4U);
}
TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_content)
{
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
auto request = std::make_shared<BasicTypes::Request>();
request->set__bool_value(true);
request->set__int64_value(42);
auto future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
auto start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
}
EXPECT_EQ(events.size(), 4U);
for (const auto & event : events) {
EXPECT_EQ(event->request.size(), 0U);
EXPECT_EQ(event->response.size(), 0U);
}
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
}
EXPECT_EQ(events.size(), 4U);
for (const auto & event : events) {
switch (event->info.event_type) {
case ServiceEventInfo::REQUEST_SENT:
EXPECT_EQ(event->request.size(), 1U);
break;
case ServiceEventInfo::REQUEST_RECEIVED:
EXPECT_EQ(event->request.size(), 0U);
break;
case ServiceEventInfo::RESPONSE_SENT:
EXPECT_EQ(event->response.size(), 0U);
break;
case ServiceEventInfo::RESPONSE_RECEIVED:
EXPECT_EQ(event->response.size(), 1U);
break;
}
}
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
}
EXPECT_EQ(events.size(), 4U);
for (const auto & event : events) {
switch (event->info.event_type) {
case ServiceEventInfo::REQUEST_SENT:
EXPECT_EQ(event->request.size(), 0U);
break;
case ServiceEventInfo::REQUEST_RECEIVED:
EXPECT_EQ(event->request.size(), 1U);
break;
case ServiceEventInfo::RESPONSE_SENT:
EXPECT_EQ(event->response.size(), 1U);
break;
case ServiceEventInfo::RESPONSE_RECEIVED:
EXPECT_EQ(event->response.size(), 0U);
break;
}
}
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
}
EXPECT_EQ(events.size(), 4U);
for (const auto & event : events) {
switch (event->info.event_type) {
case ServiceEventInfo::REQUEST_SENT:
case ServiceEventInfo::REQUEST_RECEIVED:
EXPECT_EQ(event->request.size(), 1U);
break;
case ServiceEventInfo::RESPONSE_SENT:
case ServiceEventInfo::RESPONSE_RECEIVED:
EXPECT_EQ(event->response.size(), 1U);
break;
}
}
}

View File

@@ -81,7 +81,7 @@ void spin_until_time(
executor.spin_once(10ms);
if (clock->now().nanoseconds() >= end_time.count()) {
if (clock->now().nanoseconds() == end_time.count()) {
return;
}
}

View File

@@ -3,6 +3,16 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20.0.0 (2023-04-13)
-------------------
* extract the result response before the callback is issued. (`#2132 <https://github.com/ros2/rclcpp/issues/2132>`_)
* Update all rclcpp packages to C++17. (`#2121 <https://github.com/ros2/rclcpp/issues/2121>`_)
* Fix the GoalUUID to_string representation (`#1999 <https://github.com/ros2/rclcpp/issues/1999>`_)
* Contributors: Chris Lalancette, Nathan Wiebe Neufeldt, Tomoya Fujita
19.3.0 (2023-03-01)
-------------------
19.2.0 (2023-02-24)
-------------------

View File

@@ -9,9 +9,10 @@ find_package(rcl_action REQUIRED)
find_package(rcpputils REQUIRED)
find_package(rosidl_runtime_c REQUIRED)
# Default to C++14
# Default to C++17
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(

View File

@@ -34,7 +34,7 @@ using GoalUUID = std::array<uint8_t, UUID_SIZE>;
using GoalStatus = action_msgs::msg::GoalStatus;
using GoalInfo = action_msgs::msg::GoalInfo;
/// Convert a goal id to a human readable string.
/// Convert a goal id to a human readable RFC-4122 compliant string.
RCLCPP_ACTION_PUBLIC
std::string
to_string(const GoalUUID & goal_id);

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp_action</name>
<version>19.2.0</version>
<version>20.0.0</version>
<description>Adds action APIs for C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -318,14 +318,19 @@ ClientBase::handle_result_response(
const rmw_request_id_t & response_header,
std::shared_ptr<void> response)
{
std::lock_guard<std::mutex> guard(pimpl_->result_requests_mutex);
const int64_t & sequence_number = response_header.sequence_number;
if (pimpl_->pending_result_responses.count(sequence_number) == 0) {
RCLCPP_ERROR(pimpl_->logger, "unknown result response, ignoring...");
return;
std::map<int64_t, ResponseCallback>::node_type pending_result_response;
{
std::lock_guard<std::mutex> guard(pimpl_->result_requests_mutex);
const int64_t & sequence_number = response_header.sequence_number;
if (pimpl_->pending_result_responses.count(sequence_number) == 0) {
RCLCPP_ERROR(pimpl_->logger, "unknown result response, ignoring...");
return;
}
pending_result_response =
pimpl_->pending_result_responses.extract(sequence_number);
}
pimpl_->pending_result_responses[sequence_number](response);
pimpl_->pending_result_responses.erase(sequence_number);
auto & response_callback = pending_result_response.mapped();
response_callback(response);
}
void

View File

@@ -15,25 +15,33 @@
#include "rclcpp_action/types.hpp"
#include <string>
#include <sstream>
namespace rclcpp_action
{
std::string
to_string(const GoalUUID & goal_id)
{
std::stringstream stream;
stream << std::hex;
for (const auto & element : goal_id) {
stream << static_cast<int>(element);
constexpr char HEX[] = "0123456789abcdef";
std::string result;
result.resize(36);
size_t i = 0;
for (uint8_t byte : goal_id) {
result[i++] = HEX[byte >> 4];
result[i++] = HEX[byte & 0x0f];
// A RFC-4122 compliant UUID looks like:
// 00000000-0000-0000-0000-000000000000
// That means that there is a '-' at offset 8, 13, 18, and 23
if (i == 8 || i == 13 || i == 18 || i == 23) {
result[i++] = '-';
}
}
return stream.str();
return result;
}
void
convert(const GoalUUID & goal_id, rcl_action_goal_info_t * info)
{
for (size_t i = 0; i < 16; ++i) {
for (size_t i = 0; i < UUID_SIZE; ++i) {
info->goal_id.uuid[i] = goal_id[i];
}
}
@@ -41,7 +49,7 @@ convert(const GoalUUID & goal_id, rcl_action_goal_info_t * info)
void
convert(const rcl_action_goal_info_t & info, GoalUUID * goal_id)
{
for (size_t i = 0; i < 16; ++i) {
for (size_t i = 0; i < UUID_SIZE; ++i) {
(*goal_id)[i] = info.goal_id.uuid[i];
}
}

View File

@@ -22,17 +22,17 @@ TEST(TestActionTypes, goal_uuid_to_string) {
for (uint8_t i = 0; i < UUID_SIZE; ++i) {
goal_id[i] = i;
}
EXPECT_STREQ("0123456789abcdef", rclcpp_action::to_string(goal_id).c_str());
EXPECT_STREQ("00010203-0405-0607-0809-0a0b0c0d0e0f", rclcpp_action::to_string(goal_id).c_str());
for (uint8_t i = 0; i < UUID_SIZE; ++i) {
goal_id[i] = static_cast<uint8_t>(16u + i);
}
EXPECT_STREQ("101112131415161718191a1b1c1d1e1f", rclcpp_action::to_string(goal_id).c_str());
EXPECT_STREQ("10111213-1415-1617-1819-1a1b1c1d1e1f", rclcpp_action::to_string(goal_id).c_str());
for (uint8_t i = 0; i < UUID_SIZE; ++i) {
goal_id[i] = static_cast<uint8_t>(std::numeric_limits<uint8_t>::max() - i);
}
EXPECT_STREQ("fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0", rclcpp_action::to_string(goal_id).c_str());
EXPECT_STREQ("fffefdfc-fbfa-f9f8-f7f6-f5f4f3f2f1f0", rclcpp_action::to_string(goal_id).c_str());
}
TEST(TestActionTypes, goal_uuid_to_rcl_action_goal_info) {
@@ -54,7 +54,7 @@ TEST(TestActionTypes, rcl_action_goal_info_to_goal_uuid) {
}
rclcpp_action::GoalUUID goal_id;
rclcpp_action::convert(goal_id, &goal_info);
rclcpp_action::convert(goal_info, &goal_id);
for (uint8_t i = 0; i < UUID_SIZE; ++i) {
EXPECT_EQ(goal_info.goal_id.uuid[i], goal_id[i]);
}

View File

@@ -2,6 +2,14 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20.0.0 (2023-04-13)
-------------------
* Update all rclcpp packages to C++17. (`#2121 <https://github.com/ros2/rclcpp/issues/2121>`_)
* Contributors: Chris Lalancette
19.3.0 (2023-03-01)
-------------------
19.2.0 (2023-02-24)
-------------------

View File

@@ -2,9 +2,10 @@ cmake_minimum_required(VERSION 3.5)
project(rclcpp_components)
# Default to C++14
# Default to C++17
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp_components</name>
<version>19.2.0</version>
<version>20.0.0</version>
<description>Package containing tools for dynamically loadable components</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -3,6 +3,17 @@ Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20.0.0 (2023-04-13)
-------------------
* Fixes to silence some clang warnings. (`#2127 <https://github.com/ros2/rclcpp/issues/2127>`_)
* Update all rclcpp packages to C++17. (`#2121 <https://github.com/ros2/rclcpp/issues/2121>`_)
* Use the correct macro for LifecycleNode::get_fully_qualified_name (`#2117 <https://github.com/ros2/rclcpp/issues/2117>`_)
* add get_fully_qualified_name to rclcpp_lifecycle (`#2115 <https://github.com/ros2/rclcpp/issues/2115>`_)
* Contributors: Chris Lalancette, Steve Macenski
19.3.0 (2023-03-01)
-------------------
19.2.0 (2023-02-24)
-------------------

View File

@@ -2,9 +2,10 @@ cmake_minimum_required(VERSION 3.5)
project(rclcpp_lifecycle)
# Default to C++14
# Default to C++17
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic -Wnon-virtual-dtor -Woverloaded-virtual)

View File

@@ -172,6 +172,15 @@ public:
const char *
get_namespace() const;
/// Get the fully-qualified name of the node.
/**
* The fully-qualified name includes the local namespace and name of the node.
* \return fully-qualified name of the node.
*/
RCLCPP_LIFECYCLE_PUBLIC
const char *
get_fully_qualified_name() const;
/// Get the logger of the node.
/**
* \return The logger of the node.

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp_lifecycle</name>
<version>19.2.0</version>
<version>20.0.0</version>
<description>Package containing a prototype for lifecycle implementation</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -165,6 +165,12 @@ LifecycleNode::get_namespace() const
return node_base_->get_namespace();
}
const char *
LifecycleNode::get_fully_qualified_name() const
{
return node_base_->get_fully_qualified_name();
}
rclcpp::Logger
LifecycleNode::get_logger() const
{

View File

@@ -229,6 +229,7 @@ TEST_F(TestDefaultStateMachine, empty_initializer) {
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
EXPECT_STREQ("testnode", test_node->get_name());
EXPECT_STREQ("/", test_node->get_namespace());
EXPECT_STREQ("/testnode", test_node->get_fully_qualified_name());
EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, test_node->get_current_state().id());
}

View File

@@ -133,6 +133,7 @@ TEST_P(TestLifecyclePublisher, publish_managed_by_node) {
rclcpp_lifecycle::Transition(Transition::TRANSITION_DEACTIVATE), ret);
ASSERT_EQ(success, ret);
ret = reset_key;
(void)ret; // Just to make clang happy
EXPECT_FALSE(node_->publisher()->is_activated());
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();