Compare commits
35 Commits
irobot/eve
...
runtime_in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10e482ee5c | ||
|
|
c8734b3e1d | ||
|
|
8447d36206 | ||
|
|
969067242b | ||
|
|
32859cdf3c | ||
|
|
30210a2c02 | ||
|
|
d871af007a | ||
|
|
29577dab24 | ||
|
|
000f788b65 | ||
|
|
ca17caf6c4 | ||
|
|
e7e9a298c0 | ||
|
|
8571b28664 | ||
|
|
7e0d063154 | ||
|
|
113dc01dc7 | ||
|
|
aac28e7b9f | ||
|
|
24e533eba7 | ||
|
|
ca1fe2b41b | ||
|
|
57900a9f44 | ||
|
|
363861f70c | ||
|
|
9555859691 | ||
|
|
78a568d14a | ||
|
|
9ff28439d1 | ||
|
|
bfcc29e64b | ||
|
|
927b9de2a5 | ||
|
|
5af57f8010 | ||
|
|
0ce81c4fed | ||
|
|
a2484be7c3 | ||
|
|
833efe0e2d | ||
|
|
554ad022a6 | ||
|
|
a3e5d0a91a | ||
|
|
b8fd304546 | ||
|
|
71a06985af | ||
|
|
73d555b402 | ||
|
|
a5368e6fe4 | ||
|
|
20e9cd17f6 |
@@ -49,11 +49,13 @@ 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/experimental/executors/events_executor/events_executor_entities_collector.cpp
|
||||
src/rclcpp/experimental/executors/events_executor/events_executor.cpp
|
||||
src/rclcpp/experimental/timers_manager.cpp
|
||||
src/rclcpp/executable_list.cpp
|
||||
src/rclcpp/executor.cpp
|
||||
src/rclcpp/executors.cpp
|
||||
@@ -98,6 +100,7 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/qos.cpp
|
||||
src/rclcpp/event_handler.cpp
|
||||
src/rclcpp/qos_overriding_options.cpp
|
||||
src/rclcpp/dynamic_subscription.cpp
|
||||
src/rclcpp/serialization.cpp
|
||||
src/rclcpp/serialized_message.cpp
|
||||
src/rclcpp/service.cpp
|
||||
|
||||
176
rclcpp/include/rclcpp/dynamic_subscription.hpp
Normal file
176
rclcpp/include/rclcpp/dynamic_subscription.hpp
Normal file
@@ -0,0 +1,176 @@
|
||||
// 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.
|
||||
|
||||
#ifndef RCLCPP__DYNAMIC_SUBSCRIPTION_HPP_
|
||||
#define RCLCPP__DYNAMIC_SUBSCRIPTION_HPP_
|
||||
|
||||
#include <rosidl_dynamic_typesupport/identifier.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rcpputils/shared_library.hpp"
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_message_type_support.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/serialized_message.hpp"
|
||||
#include "rclcpp/subscription_base.hpp"
|
||||
#include "rclcpp/typesupport_helpers.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// %Subscription for messages whose type descriptions are obtained at runtime.
|
||||
/**
|
||||
* Since the type is not known at compile time, this is not a template, and the dynamic library
|
||||
* containing type support information has to be identified and loaded based on the type name.
|
||||
*
|
||||
* NOTE(methylDragon): No considerations for intra-process handling are made.
|
||||
*/
|
||||
class DynamicSubscription : public rclcpp::SubscriptionBase
|
||||
{
|
||||
public:
|
||||
// cppcheck-suppress unknownMacro
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(DynamicSubscription)
|
||||
|
||||
template<typename AllocatorT = std::allocator<void>>
|
||||
DynamicSubscription(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
rclcpp::dynamic_typesupport::DynamicMessageTypeSupport::SharedPtr type_support,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
std::function<void(
|
||||
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr,
|
||||
const rosidl_runtime_c__type_description__TypeDescription &
|
||||
)> callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
|
||||
bool use_take_dynamic_message = true)
|
||||
: SubscriptionBase(
|
||||
node_base,
|
||||
type_support->get_const_rosidl_message_type_support(),
|
||||
topic_name,
|
||||
options.to_rcl_subscription_options(
|
||||
qos),
|
||||
options.event_callbacks,
|
||||
options.use_default_callbacks,
|
||||
use_take_dynamic_message ? SubscriptionType::DYNAMIC_MESSAGE_DIRECT : SubscriptionType::DYNAMIC_MESSAGE_FROM_SERIALIZED), // NOLINT
|
||||
ts_(type_support),
|
||||
callback_(callback),
|
||||
serialization_support_(nullptr),
|
||||
dynamic_message_(nullptr),
|
||||
dynamic_message_type_(nullptr)
|
||||
{
|
||||
if (!type_support) {
|
||||
throw std::runtime_error("DynamicMessageTypeSupport cannot be nullptr!");
|
||||
}
|
||||
|
||||
if (type_support->get_const_rosidl_message_type_support().typesupport_identifier !=
|
||||
rosidl_get_dynamic_typesupport_identifier())
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"DynamicSubscription must use dynamic type introspection type support!");
|
||||
}
|
||||
|
||||
serialization_support_ = type_support->get_shared_dynamic_serialization_support();
|
||||
dynamic_message_type_ = type_support->get_shared_dynamic_message_type()->clone_shared();
|
||||
dynamic_message_ = type_support->get_shared_dynamic_message()->clone_shared();
|
||||
}
|
||||
|
||||
// TODO(methylDragon):
|
||||
/// Deferred type description constructor, only usable if the middleware implementation supports
|
||||
/// type discovery
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~DynamicSubscription() = default;
|
||||
|
||||
// Same as create_serialized_message() as the subscription is to serialized_messages only
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<void> create_message() override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<rclcpp::SerializedMessage> create_serialized_message() override;
|
||||
|
||||
/// Cast the message to a rclcpp::SerializedMessage and call the callback.
|
||||
RCLCPP_PUBLIC
|
||||
void handle_message(
|
||||
std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info) override;
|
||||
|
||||
/// Handle dispatching rclcpp::SerializedMessage to user callback.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
|
||||
const rclcpp::MessageInfo & message_info) override;
|
||||
|
||||
/// This function is currently not implemented.
|
||||
RCLCPP_PUBLIC
|
||||
void handle_loaned_message(
|
||||
void * loaned_message, const rclcpp::MessageInfo & message_info) override;
|
||||
|
||||
// Same as return_serialized_message() as the subscription is to serialized_messages only
|
||||
RCLCPP_PUBLIC
|
||||
void return_message(std::shared_ptr<void> & message) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> & message) override;
|
||||
|
||||
|
||||
// DYNAMIC TYPE ==================================================================================
|
||||
// TODO(methylDragon): Reorder later
|
||||
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(DynamicSubscription)
|
||||
|
||||
rclcpp::dynamic_typesupport::DynamicMessageTypeSupport::SharedPtr ts_;
|
||||
std::function<void(
|
||||
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr,
|
||||
const rosidl_runtime_c__type_description__TypeDescription &
|
||||
)> callback_;
|
||||
|
||||
rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr serialization_support_;
|
||||
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr dynamic_message_;
|
||||
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr dynamic_message_type_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DYNAMIC_SUBSCRIPTION_HPP_
|
||||
@@ -0,0 +1,327 @@
|
||||
// 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__DETAIL__DYNAMIC_MESSAGE_IMPL_HPP_
|
||||
#define RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_IMPL_HPP_
|
||||
|
||||
#include <rosidl_dynamic_typesupport/types.h>
|
||||
#include <rosidl_dynamic_typesupport/api/dynamic_data.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
|
||||
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_HPP_
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
#define __DYNAMIC_MESSAGE_GET_VALUE_BY_ID_FN(ValueT, FunctionT) \
|
||||
template<> \
|
||||
ValueT \
|
||||
DynamicMessage::get_value<ValueT>(rosidl_dynamic_typesupport_member_id_t id) \
|
||||
{ \
|
||||
ValueT out; \
|
||||
rosidl_dynamic_typesupport_dynamic_data_get_ ## FunctionT ## _value( \
|
||||
&rosidl_dynamic_data_, id, &out); \
|
||||
return out; \
|
||||
}
|
||||
|
||||
#define __DYNAMIC_MESSAGE_GET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
|
||||
template<> \
|
||||
ValueT \
|
||||
DynamicMessage::get_value<ValueT>(const std::string & name) \
|
||||
{ \
|
||||
return get_value<ValueT>(get_member_id(name)); \
|
||||
}
|
||||
|
||||
#define __DYNAMIC_MESSAGE_SET_VALUE_BY_ID_FN(ValueT, FunctionT) \
|
||||
template<> \
|
||||
void \
|
||||
DynamicMessage::set_value<ValueT>(rosidl_dynamic_typesupport_member_id_t id, ValueT value) \
|
||||
{ \
|
||||
rosidl_dynamic_typesupport_dynamic_data_set_ ## FunctionT ## _value( \
|
||||
&rosidl_dynamic_data_, id, value); \
|
||||
}
|
||||
|
||||
#define __DYNAMIC_MESSAGE_SET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
|
||||
template<> \
|
||||
void \
|
||||
DynamicMessage::set_value<ValueT>(const std::string & name, ValueT value) \
|
||||
{ \
|
||||
set_value<ValueT>(get_member_id(name), value); \
|
||||
}
|
||||
|
||||
#define __DYNAMIC_MESSAGE_INSERT_VALUE(ValueT, FunctionT) \
|
||||
template<> \
|
||||
rosidl_dynamic_typesupport_member_id_t \
|
||||
DynamicMessage::insert_value<ValueT>(ValueT value) \
|
||||
{ \
|
||||
rosidl_dynamic_typesupport_member_id_t out; \
|
||||
rosidl_dynamic_typesupport_dynamic_data_insert_ ## FunctionT ## _value( \
|
||||
&rosidl_dynamic_data_, value, &out); \
|
||||
return out; \
|
||||
}
|
||||
|
||||
#define DYNAMIC_MESSAGE_DEFINITIONS(ValueT, FunctionT) \
|
||||
__DYNAMIC_MESSAGE_GET_VALUE_BY_ID_FN(ValueT, FunctionT) \
|
||||
__DYNAMIC_MESSAGE_GET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
|
||||
__DYNAMIC_MESSAGE_SET_VALUE_BY_ID_FN(ValueT, FunctionT) \
|
||||
__DYNAMIC_MESSAGE_SET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
|
||||
__DYNAMIC_MESSAGE_INSERT_VALUE(ValueT, FunctionT)
|
||||
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace dynamic_typesupport
|
||||
{
|
||||
|
||||
/**
|
||||
* Since we're in a ROS layer, these should support all ROS interface C++ types as found in:
|
||||
* https://docs.ros.org/en/rolling/Concepts/About-ROS-Interfaces.html
|
||||
*
|
||||
* Explicitly:
|
||||
* - Basic types: bool, byte, char
|
||||
* - Float types: float, double
|
||||
* - Int types: int8_t, int16_t, int32_t, int64_t
|
||||
* - Unsigned int types: uint8_t, uint16_t, uint32_t, uint64_t
|
||||
* - String types: std::string, std::u16string
|
||||
*/
|
||||
|
||||
DYNAMIC_MESSAGE_DEFINITIONS(bool, bool);
|
||||
// DYNAMIC_MESSAGE_DEFINITIONS(std::byte, byte);
|
||||
DYNAMIC_MESSAGE_DEFINITIONS(char, char);
|
||||
DYNAMIC_MESSAGE_DEFINITIONS(float, float32);
|
||||
DYNAMIC_MESSAGE_DEFINITIONS(double, float64);
|
||||
DYNAMIC_MESSAGE_DEFINITIONS(int8_t, int8);
|
||||
DYNAMIC_MESSAGE_DEFINITIONS(int16_t, int16);
|
||||
DYNAMIC_MESSAGE_DEFINITIONS(int32_t, int32);
|
||||
DYNAMIC_MESSAGE_DEFINITIONS(int64_t, int64);
|
||||
DYNAMIC_MESSAGE_DEFINITIONS(uint8_t, uint8);
|
||||
DYNAMIC_MESSAGE_DEFINITIONS(uint16_t, uint16);
|
||||
DYNAMIC_MESSAGE_DEFINITIONS(uint32_t, uint32);
|
||||
DYNAMIC_MESSAGE_DEFINITIONS(uint64_t, uint64);
|
||||
// DYNAMIC_MESSAGE_DEFINITIONS(std::string, std::string);
|
||||
// DYNAMIC_MESSAGE_DEFINITIONS(std::u16string, std::u16string);
|
||||
|
||||
// Byte and String getters have a different implementation and are defined below
|
||||
|
||||
|
||||
// BYTE ============================================================================================
|
||||
template<>
|
||||
std::byte
|
||||
DynamicMessage::get_value<std::byte>(rosidl_dynamic_typesupport_member_id_t id)
|
||||
{
|
||||
unsigned char out;
|
||||
rosidl_dynamic_typesupport_dynamic_data_get_byte_value(&get_rosidl_dynamic_data(), id, &out);
|
||||
return static_cast<std::byte>(out);
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
std::byte
|
||||
DynamicMessage::get_value<std::byte>(const std::string & name)
|
||||
{
|
||||
return get_value<std::byte>(get_member_id(name));
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
void
|
||||
DynamicMessage::set_value<std::byte>(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::byte value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_set_byte_value(
|
||||
&rosidl_dynamic_data_, id, static_cast<unsigned char>(value));
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
void
|
||||
DynamicMessage::set_value<std::byte>(const std::string & name, const std::byte value)
|
||||
{
|
||||
set_value<std::byte>(get_member_id(name), value);
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicMessage::insert_value<std::byte>(const std::byte value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_member_id_t out;
|
||||
rosidl_dynamic_typesupport_dynamic_data_insert_byte_value(
|
||||
&rosidl_dynamic_data_, static_cast<unsigned char>(value), &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// STRINGS =========================================================================================
|
||||
template<>
|
||||
std::string
|
||||
DynamicMessage::get_value<std::string>(rosidl_dynamic_typesupport_member_id_t id)
|
||||
{
|
||||
size_t buf_length;
|
||||
char * buf = nullptr;
|
||||
rosidl_dynamic_typesupport_dynamic_data_get_string_value(
|
||||
&get_rosidl_dynamic_data(), id, &buf, &buf_length);
|
||||
auto out = std::string(buf, buf_length);
|
||||
delete buf;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
std::u16string
|
||||
DynamicMessage::get_value<std::u16string>(rosidl_dynamic_typesupport_member_id_t id)
|
||||
{
|
||||
size_t buf_length;
|
||||
char16_t * buf = nullptr;
|
||||
rosidl_dynamic_typesupport_dynamic_data_get_wstring_value(
|
||||
&get_rosidl_dynamic_data(), id, &buf, &buf_length);
|
||||
auto out = std::u16string(buf, buf_length);
|
||||
delete buf;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
std::string
|
||||
DynamicMessage::get_value<std::string>(const std::string & name)
|
||||
{
|
||||
return get_value<std::string>(get_member_id(name));
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
std::u16string
|
||||
DynamicMessage::get_value<std::u16string>(const std::string & name)
|
||||
{
|
||||
return get_value<std::u16string>(get_member_id(name));
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
void
|
||||
DynamicMessage::set_value<std::string>(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_set_string_value(
|
||||
&rosidl_dynamic_data_, id, value.c_str(), value.size());
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
void
|
||||
DynamicMessage::set_value<std::u16string>(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::u16string value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_set_wstring_value(
|
||||
&rosidl_dynamic_data_, id, value.c_str(), value.size());
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
void
|
||||
DynamicMessage::set_value<std::string>(const std::string & name, const std::string value)
|
||||
{
|
||||
set_value<std::string>(get_member_id(name), value);
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
void
|
||||
DynamicMessage::set_value<std::u16string>(const std::string & name, const std::u16string value)
|
||||
{
|
||||
set_value<std::u16string>(get_member_id(name), value);
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicMessage::insert_value<std::string>(const std::string value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_member_id_t out;
|
||||
rosidl_dynamic_typesupport_dynamic_data_insert_string_value(
|
||||
&rosidl_dynamic_data_, value.c_str(), value.size(), &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicMessage::insert_value<std::u16string>(const std::u16string value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_member_id_t out;
|
||||
rosidl_dynamic_typesupport_dynamic_data_insert_wstring_value(
|
||||
&rosidl_dynamic_data_, value.c_str(), value.size(), &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// THROW FOR UNSUPPORTED TYPES =====================================================================
|
||||
template<typename ValueT>
|
||||
ValueT
|
||||
DynamicMessage::get_value(rosidl_dynamic_typesupport_member_id_t id)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError("get_value is not implemented for input type");
|
||||
}
|
||||
|
||||
|
||||
template<typename ValueT>
|
||||
ValueT
|
||||
DynamicMessage::get_value(const std::string & name)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError("get_value is not implemented for input type");
|
||||
}
|
||||
|
||||
|
||||
template<typename ValueT>
|
||||
void
|
||||
DynamicMessage::set_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, ValueT value)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError("set_value is not implemented for input type");
|
||||
}
|
||||
|
||||
|
||||
template<typename ValueT>
|
||||
void
|
||||
DynamicMessage::set_value(const std::string & name, ValueT value)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError("set_value is not implemented for input type");
|
||||
}
|
||||
|
||||
|
||||
template<typename ValueT>
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicMessage::insert_value(ValueT value)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError("insert_value is not implemented for input type");
|
||||
}
|
||||
|
||||
|
||||
} // namespace dynamic_typesupport
|
||||
} // namespace rclcpp
|
||||
|
||||
#undef __DYNAMIC_MESSAGE_GET_VALUE_BY_ID_FN
|
||||
#undef __DYNAMIC_MESSAGE_GET_VALUE_BY_NAME_FN
|
||||
#undef __DYNAMIC_MESSAGE_SET_VALUE_BY_ID_FN
|
||||
#undef __DYNAMIC_MESSAGE_SET_VALUE_BY_NAME_FN
|
||||
#undef __DYNAMIC_MESSAGE_INSERT_VALUE
|
||||
#undef DYNAMIC_MESSAGE_DEFINITIONS
|
||||
|
||||
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_IMPL_HPP_
|
||||
@@ -0,0 +1,189 @@
|
||||
// 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__DETAIL__DYNAMIC_MESSAGE_TYPE_BUILDER_IMPL_HPP_
|
||||
#define RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_TYPE_BUILDER_IMPL_HPP_
|
||||
|
||||
#include <rosidl_dynamic_typesupport/types.h>
|
||||
#include <rosidl_dynamic_typesupport/api/dynamic_type.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
|
||||
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_BUILDER_HPP_
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_message_type_builder.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
#define __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_MEMBER_FN(MemberT, FunctionT) \
|
||||
template<> \
|
||||
void \
|
||||
DynamicMessageTypeBuilder::add_member<MemberT>( \
|
||||
rosidl_dynamic_typesupport_member_id_t id, \
|
||||
const std::string & name, \
|
||||
const std::string & default_value) \
|
||||
{ \
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## _member( \
|
||||
&rosidl_dynamic_type_builder_, \
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size()); \
|
||||
}
|
||||
|
||||
#define __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_ARRAY_MEMBER_FN(MemberT, FunctionT) \
|
||||
template<> \
|
||||
void \
|
||||
DynamicMessageTypeBuilder::add_array_member<MemberT>( \
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, \
|
||||
size_t array_length, \
|
||||
const std::string & default_value) \
|
||||
{ \
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## _array_member( \
|
||||
&rosidl_dynamic_type_builder_, \
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(), \
|
||||
array_length); \
|
||||
}
|
||||
|
||||
#define __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_UNBOUNDED_SEQUENCE_MEMBER_FN(MemberT, FunctionT) \
|
||||
template<> \
|
||||
void \
|
||||
DynamicMessageTypeBuilder::add_unbounded_sequence_member<MemberT>( \
|
||||
rosidl_dynamic_typesupport_member_id_t id, \
|
||||
const std::string & name, \
|
||||
const std::string & default_value) \
|
||||
{ \
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## \
|
||||
_unbounded_sequence_member( \
|
||||
&rosidl_dynamic_type_builder_, \
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size()); \
|
||||
}
|
||||
|
||||
#define __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_BOUNDED_SEQUENCE_MEMBER_FN(MemberT, FunctionT) \
|
||||
template<> \
|
||||
void \
|
||||
DynamicMessageTypeBuilder::add_bounded_sequence_member<MemberT>( \
|
||||
rosidl_dynamic_typesupport_member_id_t id, \
|
||||
const std::string & name, \
|
||||
size_t sequence_bound, \
|
||||
const std::string & default_value) \
|
||||
{ \
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## _bounded_sequence_member( \
|
||||
&rosidl_dynamic_type_builder_, \
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(), \
|
||||
sequence_bound); \
|
||||
}
|
||||
|
||||
#define DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(MemberT, FunctionT) \
|
||||
__DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_MEMBER_FN(MemberT, FunctionT) \
|
||||
__DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_ARRAY_MEMBER_FN(MemberT, FunctionT) \
|
||||
__DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_UNBOUNDED_SEQUENCE_MEMBER_FN(MemberT, FunctionT) \
|
||||
__DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_BOUNDED_SEQUENCE_MEMBER_FN(MemberT, FunctionT) \
|
||||
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace dynamic_typesupport
|
||||
{
|
||||
|
||||
/**
|
||||
* Since we're in a ROS layer, these should support all ROS interface C++ types as found in:
|
||||
* https://docs.ros.org/en/rolling/Concepts/About-ROS-Interfaces.html
|
||||
*
|
||||
* Explicitly:
|
||||
* - Basic types: bool, byte, char
|
||||
* - Float types: float, double
|
||||
* - Int types: int8_t, int16_t, int32_t, int64_t
|
||||
* - Unsigned int types: uint8_t, uint16_t, uint32_t, uint64_t
|
||||
* - String types: std::string, std::u16string
|
||||
*/
|
||||
|
||||
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(bool, bool);
|
||||
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(std::byte, byte);
|
||||
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(char, char);
|
||||
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(float, float32);
|
||||
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(double, float64);
|
||||
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(int8_t, int8);
|
||||
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(int16_t, int16);
|
||||
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(int32_t, int32);
|
||||
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(int64_t, int64);
|
||||
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(uint8_t, uint8);
|
||||
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(uint16_t, uint16);
|
||||
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(uint32_t, uint32);
|
||||
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(uint64_t, uint64);
|
||||
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(std::string, string);
|
||||
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(std::u16string, wstring);
|
||||
|
||||
|
||||
// THROW FOR UNSUPPORTED TYPES =====================================================================
|
||||
template<typename MemberT>
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id,
|
||||
const std::string & name,
|
||||
const std::string & default_value)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"add_member is not implemented for input type");
|
||||
}
|
||||
|
||||
|
||||
template<typename MemberT>
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id,
|
||||
const std::string & name,
|
||||
size_t array_length, const std::string & default_value)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"add_array_member is not implemented for input type");
|
||||
}
|
||||
|
||||
|
||||
template<typename MemberT>
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id,
|
||||
const std::string & name,
|
||||
const std::string & default_value)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"add_unbounded_sequence_member is not implemented for input type");
|
||||
}
|
||||
|
||||
|
||||
template<typename MemberT>
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id,
|
||||
const std::string & name,
|
||||
size_t sequence_bound,
|
||||
const std::string & default_value)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"add_bounded_sequence_member is not implemented for input type");
|
||||
}
|
||||
|
||||
|
||||
} // namespace dynamic_typesupport
|
||||
} // namespace rclcpp
|
||||
|
||||
#undef __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_MEMBER_FN
|
||||
#undef __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_ARRAY_MEMBER_FN
|
||||
#undef __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_UNBOUNDED_SEQUENCE_MEMBER_FN
|
||||
#undef __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_BOUNDED_SEQUENCE_MEMBER_FN
|
||||
#undef DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS
|
||||
|
||||
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_TYPE_BUILDER_IMPL_HPP_
|
||||
432
rclcpp/include/rclcpp/dynamic_typesupport/dynamic_message.hpp
Normal file
432
rclcpp/include/rclcpp/dynamic_typesupport/dynamic_message.hpp
Normal file
@@ -0,0 +1,432 @@
|
||||
// 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
|
||||
{
|
||||
|
||||
class DynamicMessageType;
|
||||
class DynamicMessageTypeBuilder;
|
||||
|
||||
/// Utility wrapper class for rosidl_dynamic_typesupport_dynamic_data_t
|
||||
/**
|
||||
* This class:
|
||||
* - Exposes getter methods for the struct
|
||||
* - Exposes the underlying serialization support API
|
||||
*
|
||||
* Ownership:
|
||||
* - This class borrows the rosidl_dynamic_typesupport_serialization_support_t stored in the passed
|
||||
* DynamicSerializationSupport. So it cannot outlive the DynamicSerializationSupport.
|
||||
* - The DynamicSerializationSupport's rosidl_dynamic_typesupport_serialization_support_t pointer
|
||||
* must point to the same location in memory as the stored raw pointer!
|
||||
*
|
||||
* Note: This class is meant to map to the lower level rosidl_dynamic_typesupport_dynamic_data_t,
|
||||
* even though rosidl_dynamic_typesupport_dynamic_data_t is equivalent to
|
||||
* rosidl_dynamic_typesupport_dynamic_data_t, exposing the fundamental methods available to
|
||||
* rosidl_dynamic_typesupport_dynamic_data_t, allowing the user to access the data's fields.
|
||||
*/
|
||||
class DynamicMessage : public std::enable_shared_from_this<DynamicMessage>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessage)
|
||||
|
||||
// CONSTRUCTION ==================================================================================
|
||||
// Most constructors require a passed in DynamicSerializationSupport::SharedPtr, to extend the
|
||||
// lifetime of the serialization support (if the constructor cannot otherwise get it from args).
|
||||
//
|
||||
// In cases where a dynamic data pointer is passed, the serialization support composed by
|
||||
// the data should be the exact same object managed by the DynamicSerializationSupport,
|
||||
// otherwise the lifetime management will not work properly.
|
||||
|
||||
/// Construct a new DynamicMessage with the provided dynamic type builder, using its allocator
|
||||
RCLCPP_PUBLIC
|
||||
explicit DynamicMessage(std::shared_ptr<DynamicMessageTypeBuilder> dynamic_type_builder);
|
||||
|
||||
/// Construct a new DynamicMessage with the provided dynamic type builder and allocator
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage(
|
||||
std::shared_ptr<DynamicMessageTypeBuilder> dynamic_type_builder,
|
||||
rcl_allocator_t allocator);
|
||||
|
||||
/// Construct a new DynamicMessage with the provided dynamic type, using its allocator
|
||||
RCLCPP_PUBLIC
|
||||
explicit DynamicMessage(std::shared_ptr<DynamicMessageType> dynamic_type);
|
||||
|
||||
/// Construct a new DynamicMessage with the provided dynamic type and allocator
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage(
|
||||
std::shared_ptr<DynamicMessageType> dynamic_type,
|
||||
rcl_allocator_t allocator);
|
||||
|
||||
/// Assume ownership of struct
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
rosidl_dynamic_typesupport_dynamic_data_t && rosidl_dynamic_data);
|
||||
|
||||
/// Loaning constructor
|
||||
/// Must only be called with a rosidl dynaimc data object obtained from loaning!
|
||||
// NOTE(methylDragon): I'd put this in protected, but I need this exposed to
|
||||
// enable_shared_from_this...
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage(
|
||||
DynamicMessage::SharedPtr parent_data,
|
||||
rosidl_dynamic_typesupport_dynamic_data_t && rosidl_loaned_data);
|
||||
|
||||
// NOTE(methylDragon): Deliberately no constructor from description to nudge users towards using
|
||||
// construction from dynamic type/builder, which is more efficient
|
||||
|
||||
/// Move constructor
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage(DynamicMessage && other) noexcept;
|
||||
|
||||
/// Move assignment
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage & operator=(DynamicMessage && other) noexcept;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~DynamicMessage();
|
||||
|
||||
|
||||
// GETTERS =======================================================================================
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_serialization_library_identifier() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_name() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_dynamic_data_t &
|
||||
get_rosidl_dynamic_data();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_dynamic_typesupport_dynamic_data_t &
|
||||
get_rosidl_dynamic_data() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicSerializationSupport::SharedPtr
|
||||
get_shared_dynamic_serialization_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicSerializationSupport::ConstSharedPtr
|
||||
get_shared_dynamic_serialization_support() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_item_count() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
get_member_id(size_t index) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
get_member_id(const std::string & name) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
get_array_index(size_t index) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
get_array_index(const std::string & name) const;
|
||||
|
||||
|
||||
// METHODS =======================================================================================
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage
|
||||
clone(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage::SharedPtr
|
||||
clone_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage
|
||||
init_from_type(
|
||||
DynamicMessageType & type, rcl_allocator_t allocator = rcl_get_default_allocator()) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage::SharedPtr
|
||||
init_from_type_shared(
|
||||
DynamicMessageType & type, rcl_allocator_t allocator = rcl_get_default_allocator()) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
equals(const DynamicMessage & other) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage::SharedPtr
|
||||
loan_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id,
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage::SharedPtr
|
||||
loan_value(
|
||||
const std::string & name,
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
clear_all_values();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
clear_nonkey_values();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
clear_value(rosidl_dynamic_typesupport_member_id_t id);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
clear_value(const std::string & name);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
clear_sequence();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
insert_sequence_data();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_sequence_data(rosidl_dynamic_typesupport_member_id_t index);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
serialize(rcl_serialized_message_t & buffer);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
deserialize(rcl_serialized_message_t & buffer);
|
||||
|
||||
|
||||
// MEMBER ACCESS TEMPLATES =======================================================================
|
||||
/**
|
||||
* Since we're in a ROS layer, these should support all ROS interface C++ types as found in:
|
||||
* https://docs.ros.org/en/rolling/Concepts/About-ROS-Interfaces.html
|
||||
*
|
||||
* Explicitly:
|
||||
* - Basic types: bool, byte, char
|
||||
* - Float types: float, double
|
||||
* - Int types: int8_t, int16_t, int32_t, int64_t
|
||||
* - Unsigned int types: uint8_t, uint16_t, uint32_t, uint64_t
|
||||
* - String types: std::string, std::u16string
|
||||
*/
|
||||
|
||||
template<typename ValueT>
|
||||
ValueT
|
||||
get_value(rosidl_dynamic_typesupport_member_id_t id);
|
||||
|
||||
template<typename ValueT>
|
||||
ValueT
|
||||
get_value(const std::string & name);
|
||||
|
||||
template<typename ValueT>
|
||||
void
|
||||
set_value(rosidl_dynamic_typesupport_member_id_t id, ValueT value);
|
||||
|
||||
template<typename ValueT>
|
||||
void
|
||||
set_value(const std::string & name, ValueT value);
|
||||
|
||||
template<typename ValueT>
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
insert_value(ValueT value);
|
||||
|
||||
|
||||
// FIXED STRING MEMBER ACCESS ====================================================================
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_fixed_string_value(rosidl_dynamic_typesupport_member_id_t id, size_t string_length);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_fixed_string_value(const std::string & name, size_t string_length);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::u16string
|
||||
get_fixed_wstring_value(rosidl_dynamic_typesupport_member_id_t id, size_t wstring_length);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::u16string
|
||||
get_fixed_wstring_value(const std::string & name, size_t wstring_length);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_fixed_string_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string value, size_t string_length);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_fixed_string_value(const std::string & name, const std::string value, size_t string_length);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_fixed_wstring_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::u16string value, size_t wstring_length);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_fixed_wstring_value(
|
||||
const std::string & name, const std::u16string value, size_t wstring_length);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
insert_fixed_string_value(const std::string value, size_t string_length);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
insert_fixed_wstring_value(const std::u16string value, size_t wstring_length);
|
||||
|
||||
|
||||
// BOUNDED STRING MEMBER ACCESS ==================================================================
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_bounded_string_value(rosidl_dynamic_typesupport_member_id_t id, size_t string_bound);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_bounded_string_value(const std::string & name, size_t string_bound);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::u16string
|
||||
get_bounded_wstring_value(rosidl_dynamic_typesupport_member_id_t id, size_t wstring_bound);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::u16string
|
||||
get_bounded_wstring_value(const std::string & name, size_t wstring_bound);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_bounded_string_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string value, size_t string_bound);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_bounded_string_value(const std::string & name, const std::string value, size_t string_bound);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_bounded_wstring_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::u16string value, size_t wstring_bound);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_bounded_wstring_value(
|
||||
const std::string & name, const std::u16string value, size_t wstring_bound);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
insert_bounded_string_value(const std::string value, size_t string_bound);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
insert_bounded_wstring_value(const std::u16string value, size_t wstring_bound);
|
||||
|
||||
|
||||
// NESTED MEMBER ACCESS ==========================================================================
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage
|
||||
get_complex_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id,
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage
|
||||
get_complex_value(
|
||||
const std::string & name,
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage::SharedPtr
|
||||
get_complex_value_shared(
|
||||
rosidl_dynamic_typesupport_member_id_t id,
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage::SharedPtr
|
||||
get_complex_value_shared(
|
||||
const std::string & name,
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_complex_value(rosidl_dynamic_typesupport_member_id_t id, DynamicMessage & value);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_complex_value(const std::string & name, DynamicMessage & value);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
insert_complex_value_copy(const DynamicMessage & value);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
insert_complex_value(DynamicMessage & value);
|
||||
|
||||
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();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
match_serialization_support_(
|
||||
const DynamicSerializationSupport & serialization_support,
|
||||
const rosidl_dynamic_typesupport_dynamic_data_t & dynamic_data);
|
||||
};
|
||||
|
||||
} // namespace dynamic_typesupport
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_HPP_
|
||||
@@ -0,0 +1,197 @@
|
||||
// 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
|
||||
{
|
||||
|
||||
class DynamicMessage;
|
||||
class DynamicMessageTypeBuilder;
|
||||
|
||||
/// Utility wrapper class for `rosidl_dynamic_typesupport_dynamic_type_t`
|
||||
/**
|
||||
* This class:
|
||||
* - Exposes getter methods for the struct
|
||||
* - Exposes the underlying serialization support API
|
||||
*
|
||||
* Ownership:
|
||||
* - This class borrows the `rosidl_dynamic_typesupport_serialization_support_t` stored in the
|
||||
* passed `DynamicSerializationSupport`.
|
||||
* So it cannot outlive the `DynamicSerializationSupport`.
|
||||
* - The `DynamicSerializationSupport`'s `rosidl_dynamic_typesupport_serialization_support_t`
|
||||
* pointer must point to the same location in memory as the stored raw pointer!
|
||||
*
|
||||
* This class is meant to map to the lower level `rosidl_dynamic_typesupport_dynamic_type_t`,
|
||||
* which can be constructed via `DynamicMessageTypeBuilder`, which maps to
|
||||
* `rosidl_dynamic_typesupport_dynamic_type_builder_t`.
|
||||
*
|
||||
* The usual method of obtaining a `DynamicMessageType` is through construction of
|
||||
* `rosidl_message_type_support_t` via `rcl_dynamic_message_type_support_handle_create()`, then
|
||||
* taking ownership of its contents. But `DynamicMessageTypeBuilder` can also be used to obtain
|
||||
* `DynamicMessageType` by constructing it bottom-up instead, since it exposes the lower_level
|
||||
* rosidl methods.
|
||||
*/
|
||||
class DynamicMessageType : public std::enable_shared_from_this<DynamicMessageType>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessageType)
|
||||
|
||||
// CONSTRUCTION ==================================================================================
|
||||
// Most constructors require a passed in `DynamicSerializationSupport::SharedPtr`, to extend the
|
||||
// lifetime of the serialization support (if the constructor cannot otherwise get it from args).
|
||||
//
|
||||
// In cases where a dynamic type pointer is passed, the serialization support composed by
|
||||
// the type should be the exact same object managed by the `DynamicSerializationSupport`,
|
||||
// otherwise the lifetime management will not work properly.
|
||||
|
||||
/// Construct a new `DynamicMessageType` with the provided dynamic type builder,
|
||||
/// using its allocator
|
||||
RCLCPP_PUBLIC
|
||||
explicit DynamicMessageType(std::shared_ptr<DynamicMessageTypeBuilder> dynamic_type_builder);
|
||||
|
||||
/// Construct a new `DynamicMessageType` with the provided dynamic type builder and allocator
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageType(
|
||||
std::shared_ptr<DynamicMessageTypeBuilder> dynamic_type_builder,
|
||||
rcl_allocator_t allocator);
|
||||
|
||||
/// Assume ownership of struct
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageType(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
rosidl_dynamic_typesupport_dynamic_type_t && rosidl_dynamic_type);
|
||||
|
||||
/// From description
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageType(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const rosidl_runtime_c__type_description__TypeDescription & description,
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
/// Move constructor
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageType(DynamicMessageType && other) noexcept;
|
||||
|
||||
/// Move assignment
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageType & operator=(DynamicMessageType && other) noexcept;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~DynamicMessageType();
|
||||
|
||||
/// Swaps the serialization support if `serialization_support` is populated
|
||||
/**
|
||||
* The user can call this with another description to reconfigure the type without changing the
|
||||
* serialization support
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init_from_description(
|
||||
const rosidl_runtime_c__type_description__TypeDescription & description,
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator(),
|
||||
DynamicSerializationSupport::SharedPtr serialization_support = nullptr);
|
||||
|
||||
// GETTERS =======================================================================================
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_serialization_library_identifier() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_name() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_member_count() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_dynamic_type_t &
|
||||
get_rosidl_dynamic_type();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_dynamic_typesupport_dynamic_type_t &
|
||||
get_rosidl_dynamic_type() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicSerializationSupport::SharedPtr
|
||||
get_shared_dynamic_serialization_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicSerializationSupport::ConstSharedPtr
|
||||
get_shared_dynamic_serialization_support() const;
|
||||
|
||||
|
||||
// METHODS =======================================================================================
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageType
|
||||
clone(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageType::SharedPtr
|
||||
clone_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
equals(const DynamicMessageType & other) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage
|
||||
build_dynamic_message(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<DynamicMessage>
|
||||
build_dynamic_message_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
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();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
match_serialization_support_(
|
||||
const DynamicSerializationSupport & serialization_support,
|
||||
const rosidl_dynamic_typesupport_dynamic_type_t & rosidl_dynamic_type);
|
||||
};
|
||||
|
||||
} // namespace dynamic_typesupport
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_HPP_
|
||||
@@ -0,0 +1,409 @@
|
||||
// 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
|
||||
{
|
||||
|
||||
class DynamicMessage;
|
||||
class DynamicMessageType;
|
||||
|
||||
/// Utility wrapper class for `rosidl_dynamic_typesupport_dynamic_type_builder_t *`
|
||||
/**
|
||||
* This class:
|
||||
* - Manages the lifetime of the raw pointer.
|
||||
* - Exposes getter methods to get the raw pointer and shared pointers
|
||||
* - Exposes the underlying serialization support API
|
||||
*
|
||||
* Ownership:
|
||||
* - This class borrows the rosidl_dynamic_typesupport_serialization_support_t stored in the passed
|
||||
* `DynamicSerializationSupport`.
|
||||
* So it cannot outlive the `DynamicSerializationSupport`.
|
||||
* - The `DynamicSerializationSupport`'s `rosidl_dynamic_typesupport_serialization_support_t`
|
||||
* pointer must point to the same location in memory as the stored raw pointer!
|
||||
*
|
||||
* This class is meant to map to rosidl_dynamic_typesupport_dynamic_type_builder_t, facilitating the
|
||||
* construction of dynamic types bottom-up in the C++ layer.
|
||||
*
|
||||
* The usual method of obtaining a `DynamicMessageType` is through construction of
|
||||
* `rosidl_message_type_support_t` via `rcl_dynamic_message_type_support_handle_create()`, then
|
||||
* taking ownership of its contents.
|
||||
* But `DynamicMessageTypeBuilder` can also be used to obtain `DynamicMessageType` by constructing
|
||||
* it bottom-up instead, since it exposes the lower_level rosidl methods.
|
||||
*/
|
||||
class DynamicMessageTypeBuilder : public std::enable_shared_from_this<DynamicMessageTypeBuilder>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessageTypeBuilder)
|
||||
|
||||
// CONSTRUCTION ==================================================================================
|
||||
// All constructors require a passed in `DynamicSerializationSupport::SharedPtr`, to extend the
|
||||
// lifetime of the serialization support.
|
||||
//
|
||||
// In cases where a dynamic type builder pointer is passed, the serialization support composed by
|
||||
// the builder should be the exact same object managed by the `DynamicSerializationSupport`,
|
||||
// otherwise the lifetime management will not work properly.
|
||||
|
||||
/// Construct a new `DynamicMessageTypeBuilder` with the provided serialization support using its
|
||||
/// allocator
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const std::string & name);
|
||||
|
||||
/// Construct a new `DynamicMessageTypeBuilder` with the provided serialization support and
|
||||
/// allocator
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const std::string & name,
|
||||
rcl_allocator_t allocator);
|
||||
|
||||
/// Assume ownership of struct
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_t && dynamic_type_builder);
|
||||
|
||||
/// From description
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const rosidl_runtime_c__type_description__TypeDescription & description,
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
/// Move constructor
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeBuilder(DynamicMessageTypeBuilder && other) noexcept;
|
||||
|
||||
/// Move assignment
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeBuilder & operator=(DynamicMessageTypeBuilder && other) noexcept;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~DynamicMessageTypeBuilder();
|
||||
|
||||
/// Swaps the serialization support if serialization_support is populated
|
||||
/**
|
||||
* The user can call this with another description to reconfigure the type without changing the
|
||||
* serialization support
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init_from_description(
|
||||
const rosidl_runtime_c__type_description__TypeDescription & description,
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator(),
|
||||
DynamicSerializationSupport::SharedPtr serialization_support = nullptr);
|
||||
|
||||
|
||||
// GETTERS =======================================================================================
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_serialization_library_identifier() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_name() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_t &
|
||||
get_rosidl_dynamic_type_builder();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_dynamic_typesupport_dynamic_type_builder_t &
|
||||
get_rosidl_dynamic_type_builder() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicSerializationSupport::SharedPtr
|
||||
get_shared_dynamic_serialization_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicSerializationSupport::ConstSharedPtr
|
||||
get_shared_dynamic_serialization_support() const;
|
||||
|
||||
|
||||
// METHODS =======================================================================================
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_name(const std::string & name);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeBuilder
|
||||
clone(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeBuilder::SharedPtr
|
||||
clone_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
clear(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage
|
||||
build_dynamic_message(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage::SharedPtr
|
||||
build_dynamic_message_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageType
|
||||
build_dynamic_message_type(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageType::SharedPtr
|
||||
build_dynamic_message_type_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
|
||||
// ADD MEMBERS TEMPLATES =========================================================================
|
||||
/**
|
||||
* Since we're in a ROS layer, these should support all ROS interface C++ types as found in:
|
||||
* https://docs.ros.org/en/rolling/Concepts/About-ROS-Interfaces.html
|
||||
*
|
||||
* Explicitly:
|
||||
* - Basic types: bool, byte, char
|
||||
* - Float types: float, double
|
||||
* - Int types: int8_t, int16_t, int32_t, int64_t
|
||||
* - Unsigned int types: uint8_t, uint16_t, uint32_t, uint64_t
|
||||
* - String types: std::string, std::u16string
|
||||
*/
|
||||
|
||||
template<typename MemberT>
|
||||
void
|
||||
add_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
const std::string & default_value = "");
|
||||
|
||||
template<typename MemberT>
|
||||
void
|
||||
add_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t array_length,
|
||||
const std::string & default_value = "");
|
||||
|
||||
template<typename MemberT>
|
||||
void
|
||||
add_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
const std::string & default_value = "");
|
||||
|
||||
template<typename MemberT>
|
||||
void
|
||||
add_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t sequence_bound,
|
||||
const std::string & default_value = "");
|
||||
|
||||
|
||||
// ADD FIXED STRING MEMBERS ======================================================================
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_fixed_string_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_length,
|
||||
const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_fixed_wstring_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_length,
|
||||
const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_fixed_string_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t string_length, size_t array_length, const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_fixed_wstring_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t wstring_length, size_t array_length, const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_fixed_string_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_length,
|
||||
const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_fixed_wstring_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_length,
|
||||
const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_fixed_string_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t string_length, size_t sequence_bound, const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_fixed_wstring_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t wstring_length, size_t sequence_bound, const std::string & default_value = "");
|
||||
|
||||
|
||||
// ADD BOUNDED STRING MEMBERS ====================================================================
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_bounded_string_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_bound,
|
||||
const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_bounded_wstring_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound,
|
||||
const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_bounded_string_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t string_bound, size_t array_length, const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_bounded_wstring_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t wstring_bound, size_t array_length, const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_bounded_string_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_bound,
|
||||
const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_bounded_wstring_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound,
|
||||
const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_bounded_string_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t string_bound, size_t sequence_bound, const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_bounded_wstring_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t wstring_bound, size_t sequence_bound, const std::string & default_value = "");
|
||||
|
||||
|
||||
// ADD NESTED MEMBERS ============================================================================
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageType & nested_type, const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageType & nested_type, size_t array_length, const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageType & nested_type, const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageType & nested_type, size_t sequence_bound,
|
||||
const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageTypeBuilder & nested_type_builder, const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_array_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageTypeBuilder & nested_type_builder, size_t array_length,
|
||||
const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_unbounded_sequence_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageTypeBuilder & nested_type_builder, const std::string & default_value = "");
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_bounded_sequence_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageTypeBuilder & nested_type_builder, size_t sequence_bound,
|
||||
const std::string & default_value = "");
|
||||
|
||||
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();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init_from_serialization_support_(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const std::string & name,
|
||||
rcl_allocator_t allocator);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
match_serialization_support_(
|
||||
const DynamicSerializationSupport & serialization_support,
|
||||
const rosidl_dynamic_typesupport_dynamic_type_builder_t & dynamic_type_builder);
|
||||
};
|
||||
|
||||
} // namespace dynamic_typesupport
|
||||
} // namespace rclcpp
|
||||
|
||||
|
||||
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_BUILDER_HPP_
|
||||
@@ -0,0 +1,166 @@
|
||||
// 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
|
||||
/// instances of the typesupport handle impl.
|
||||
/**
|
||||
*
|
||||
* NOTE: This class is the recommended way to obtain the dynamic message type
|
||||
* support struct, instead of `rcl_dynamic_message_type_support_handle_init()`,
|
||||
* because this class will manage the lifetimes for you.
|
||||
*
|
||||
* Do NOT call rcl_dynamic_message_type_support_handle_fini
|
||||
*
|
||||
* This class:
|
||||
* - Exposes getter methods for the struct
|
||||
* - Stores shared pointers to wrapper classes that expose the underlying
|
||||
* serialization support API
|
||||
*
|
||||
* Ownership:
|
||||
* - This class, similarly to the `rosidl_dynamic_typesupport_serialization_support_t`, must outlive
|
||||
* all downstream usages of the serialization support.
|
||||
*/
|
||||
class DynamicMessageTypeSupport : public std::enable_shared_from_this<DynamicMessageTypeSupport>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessageTypeSupport)
|
||||
|
||||
// CONSTRUCTION ==================================================================================
|
||||
/// From description
|
||||
/// Does NOT take ownership of the description (copies instead.)
|
||||
/// Constructs type support top-down (calling `rcl_dynamic_message_type_support_handle_create()`)
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeSupport(
|
||||
const rosidl_runtime_c__type_description__TypeDescription & description,
|
||||
const std::string & serialization_library_name = "",
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
/// From description, for provided serialization support
|
||||
/// Does NOT take ownership of the description (copies instead.)
|
||||
/// Constructs type support top-down (calling `rosidl_dynamic_message_type_support_handle_init()`)
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeSupport(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const rosidl_runtime_c__type_description__TypeDescription & description,
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
/// Assume ownership of managed types
|
||||
/// Does NOT take ownership of the description (copies instead.)
|
||||
///
|
||||
/// The serialization support used to construct all managed SharedPtrs must match.
|
||||
/// The structure of the provided `description` must match the `dynamic_message_type`
|
||||
/// The structure of the provided `dynamic_message_type` must match the `dynamic_message
|
||||
///
|
||||
/// In this case, the user would have constructed the type support bototm-up (by creating the
|
||||
/// respective dynamic members.)
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeSupport(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
DynamicMessageType::SharedPtr dynamic_message_type,
|
||||
DynamicMessage::SharedPtr dynamic_message,
|
||||
const rosidl_runtime_c__type_description__TypeDescription & description,
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~DynamicMessageTypeSupport();
|
||||
|
||||
|
||||
// GETTERS =======================================================================================
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_serialization_library_identifier() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_message_type_support_t &
|
||||
get_const_rosidl_message_type_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_message_type_support_t &
|
||||
get_const_rosidl_message_type_support() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_runtime_c__type_description__TypeDescription &
|
||||
get_rosidl_runtime_c_type_description() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicSerializationSupport::SharedPtr
|
||||
get_shared_dynamic_serialization_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicSerializationSupport::ConstSharedPtr
|
||||
get_shared_dynamic_serialization_support() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageType::SharedPtr
|
||||
get_shared_dynamic_message_type();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageType::ConstSharedPtr
|
||||
get_shared_dynamic_message_type() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage::SharedPtr
|
||||
get_shared_dynamic_message();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessage::ConstSharedPtr
|
||||
get_shared_dynamic_message() const;
|
||||
|
||||
protected:
|
||||
RCLCPP_DISABLE_COPY(DynamicMessageTypeSupport)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_message_type_support_t &
|
||||
get_rosidl_message_type_support();
|
||||
|
||||
private:
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeSupport();
|
||||
|
||||
DynamicSerializationSupport::SharedPtr serialization_support_;
|
||||
DynamicMessageType::SharedPtr dynamic_message_type_;
|
||||
DynamicMessage::SharedPtr dynamic_message_;
|
||||
|
||||
rosidl_message_type_support_t rosidl_message_type_support_;
|
||||
};
|
||||
|
||||
} // namespace dynamic_typesupport
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_SUPPORT_HPP_
|
||||
@@ -0,0 +1,98 @@
|
||||
// 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
|
||||
/**
|
||||
* This class:
|
||||
* - Exposes getter methods for the struct
|
||||
* - Exposes the underlying serialization support API
|
||||
*
|
||||
* Ownership:
|
||||
* - This class, similarly to the rosidl_dynamic_typesupport_serialization_support_t, must outlive
|
||||
* all downstream usages of the serialization support.
|
||||
*/
|
||||
class DynamicSerializationSupport : public std::enable_shared_from_this<DynamicSerializationSupport>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicSerializationSupport)
|
||||
|
||||
// CONSTRUCTION ==================================================================================
|
||||
RCLCPP_PUBLIC
|
||||
explicit DynamicSerializationSupport(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
/// Get the rmw middleware implementation specific serialization support (configured by name)
|
||||
RCLCPP_PUBLIC
|
||||
DynamicSerializationSupport(
|
||||
const std::string & serialization_library_name,
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
/// Assume ownership of struct
|
||||
RCLCPP_PUBLIC
|
||||
explicit DynamicSerializationSupport(
|
||||
rosidl_dynamic_typesupport_serialization_support_t && rosidl_serialization_support);
|
||||
|
||||
/// Move constructor
|
||||
RCLCPP_PUBLIC
|
||||
DynamicSerializationSupport(DynamicSerializationSupport && other) noexcept;
|
||||
|
||||
/// Move assignment
|
||||
RCLCPP_PUBLIC
|
||||
DynamicSerializationSupport & operator=(DynamicSerializationSupport && other) noexcept;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~DynamicSerializationSupport();
|
||||
|
||||
|
||||
// GETTERS =======================================================================================
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_serialization_library_identifier() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_serialization_support_t &
|
||||
get_rosidl_serialization_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_dynamic_typesupport_serialization_support_t &
|
||||
get_rosidl_serialization_support() const;
|
||||
|
||||
protected:
|
||||
RCLCPP_DISABLE_COPY(DynamicSerializationSupport)
|
||||
|
||||
private:
|
||||
rosidl_dynamic_typesupport_serialization_support_t rosidl_serialization_support_;
|
||||
};
|
||||
|
||||
} // namespace dynamic_typesupport
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_SERIALIZATION_SUPPORT_HPP_
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "rclcpp/executors/multi_threaded_executor.hpp"
|
||||
#include "rclcpp/executors/single_threaded_executor.hpp"
|
||||
#include "rclcpp/executors/static_single_threaded_executor.hpp"
|
||||
#include "rclcpp/experimental/executors/events_executor/events_executor.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright 2023 iRobot Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENT_WAITABLE_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENT_WAITABLE_HPP_
|
||||
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace experimental
|
||||
{
|
||||
namespace executors
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief This class provides a wrapper around the waitable object, that is
|
||||
* meant to be used with the EventsExecutor.
|
||||
* The waitset related methods are stubbed out as they should not be called.
|
||||
* This class is abstract as the execute method of rclcpp::Waitable is not implemented.
|
||||
* Nodes who want to implement a custom EventWaitable, can derive from this class and override
|
||||
* the execute method.
|
||||
*/
|
||||
class EventWaitable : public rclcpp::Waitable
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
RCLCPP_PUBLIC
|
||||
EventWaitable() = default;
|
||||
|
||||
// Destructor
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~EventWaitable() = default;
|
||||
|
||||
// Stub API: not used by EventsExecutor
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set) final
|
||||
{
|
||||
(void)wait_set;
|
||||
throw std::runtime_error("EventWaitable can't be checked if it's ready");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stub API: not used by EventsExecutor
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set) final
|
||||
{
|
||||
(void)wait_set;
|
||||
throw std::runtime_error("EventWaitable can't be added to a wait_set");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace executors
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENT_WAITABLE_HPP_
|
||||
@@ -1,237 +0,0 @@
|
||||
// Copyright 2023 iRobot Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_HPP_
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/executor.hpp"
|
||||
#include "rclcpp/experimental/executors/events_executor/events_executor_entities_collector.hpp"
|
||||
#include "rclcpp/experimental/executors/events_executor/events_executor_event_types.hpp"
|
||||
#include "rclcpp/experimental/executors/events_executor/events_executor_notify_waitable.hpp"
|
||||
#include "rclcpp/experimental/executors/events_executor/events_queue.hpp"
|
||||
#include "rclcpp/experimental/executors/events_executor/simple_events_queue.hpp"
|
||||
#include "rclcpp/experimental/timers_manager.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace experimental
|
||||
{
|
||||
namespace executors
|
||||
{
|
||||
|
||||
/// Events executor implementation
|
||||
/**
|
||||
* This executor uses an events queue and a timers manager to execute entities from its
|
||||
* associated nodes and callback groups.
|
||||
* The RMW listener APIs are used to collect new events.
|
||||
*
|
||||
* This executor tries to reduce as much as possible the amount of maintenance operations.
|
||||
* This allows to use customized `EventsQueue` classes to achieve different goals such
|
||||
* as very low CPU usage, bounded memory requirement, determinism, etc.
|
||||
*
|
||||
* The executor uses a weak ownership model and it locks entities only while executing
|
||||
* their related events.
|
||||
*
|
||||
* To run this executor:
|
||||
* rclcpp::experimental::executors::EventsExecutor executor;
|
||||
* executor.add_node(node);
|
||||
* executor.spin();
|
||||
* executor.remove_node(node);
|
||||
*/
|
||||
class EventsExecutor : public rclcpp::Executor
|
||||
{
|
||||
friend class EventsExecutorEntitiesCollector;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(EventsExecutor)
|
||||
|
||||
/// Default constructor. See the default constructor for Executor.
|
||||
/**
|
||||
* \param[in] events_queue The queue used to store events.
|
||||
* \param[in] execute_timers_separate_thread If true, timers are executed in a separate
|
||||
* thread. If false, timers are executed in the same thread as all other entities.
|
||||
* \param[in] options Options used to configure the executor.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
explicit EventsExecutor(
|
||||
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue = std::make_unique<
|
||||
rclcpp::experimental::executors::SimpleEventsQueue>(),
|
||||
bool execute_timers_separate_thread = false,
|
||||
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions());
|
||||
|
||||
/// Default destructor.
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~EventsExecutor();
|
||||
|
||||
/// Events executor implementation of spin.
|
||||
/**
|
||||
* This function will block until work comes in, execute it, and keep blocking.
|
||||
* It will only be interrupted by a CTRL-C (managed by the global signal handler).
|
||||
* \throws std::runtime_error when spin() called while already spinning
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin() override;
|
||||
|
||||
/// Events executor implementation of spin some
|
||||
/**
|
||||
* This non-blocking function will execute the timers and events
|
||||
* that were ready when this API was called, until timeout or no
|
||||
* more work available. New ready-timers/events arrived while
|
||||
* executing work, won't be taken into account here.
|
||||
*
|
||||
* Example:
|
||||
* while(condition) {
|
||||
* spin_some();
|
||||
* sleep(); // User should have some sync work or
|
||||
* // sleep to avoid a 100% CPU usage
|
||||
* }
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0)) override;
|
||||
|
||||
/// Events executor implementation of spin all
|
||||
/**
|
||||
* This non-blocking function will execute timers and events
|
||||
* until timeout or no more work available. If new ready-timers/events
|
||||
* arrive while executing work available, they will be executed
|
||||
* as long as the timeout hasn't expired.
|
||||
*
|
||||
* Example:
|
||||
* while(condition) {
|
||||
* spin_all();
|
||||
* sleep(); // User should have some sync work or
|
||||
* // sleep to avoid a 100% CPU usage
|
||||
* }
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_all(std::chrono::nanoseconds max_duration) override;
|
||||
|
||||
/// Add a node to the executor.
|
||||
/**
|
||||
* \sa rclcpp::Executor::add_node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_node(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
bool notify = true) override;
|
||||
|
||||
/// Convenience function which takes Node and forwards NodeBaseInterface.
|
||||
/**
|
||||
* \sa rclcpp::EventsExecutor::add_node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
|
||||
|
||||
/// Remove a node from the executor.
|
||||
/**
|
||||
* \sa rclcpp::Executor::remove_node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_node(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
bool notify = true) override;
|
||||
|
||||
/// Convenience function which takes Node and forwards NodeBaseInterface.
|
||||
/**
|
||||
* \sa rclcpp::Executor::remove_node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
|
||||
|
||||
/// Add a callback group to an executor.
|
||||
/**
|
||||
* \sa rclcpp::Executor::add_callback_group
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
bool notify = true) override;
|
||||
|
||||
/// Remove callback group from the executor
|
||||
/**
|
||||
* \sa rclcpp::Executor::remove_callback_group
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
bool notify = true) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_all_callback_groups() override;
|
||||
|
||||
/// Get callback groups that belong to executor.
|
||||
/**
|
||||
* \sa rclcpp::Executor::get_manually_added_callback_groups()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_manually_added_callback_groups() override;
|
||||
|
||||
/// Get callback groups that belong to executor.
|
||||
/**
|
||||
* \sa rclcpp::Executor::get_automatically_added_callback_groups_from_nodes()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_automatically_added_callback_groups_from_nodes() override;
|
||||
|
||||
protected:
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_once_impl(std::chrono::nanoseconds timeout) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive);
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(EventsExecutor)
|
||||
|
||||
// Execute a single event
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
execute_event(const ExecutorEvent & event);
|
||||
|
||||
// Queue where entities can push events
|
||||
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue_;
|
||||
|
||||
EventsExecutorEntitiesCollector::SharedPtr entities_collector_;
|
||||
EventsExecutorNotifyWaitable::SharedPtr executor_notifier_;
|
||||
|
||||
// Timers manager
|
||||
std::shared_ptr<rclcpp::experimental::TimersManager> timers_manager_;
|
||||
};
|
||||
|
||||
} // namespace executors
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_HPP_
|
||||
@@ -1,348 +0,0 @@
|
||||
// Copyright 2023 iRobot Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_ENTITIES_COLLECTOR_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_ENTITIES_COLLECTOR_HPP_
|
||||
|
||||
#include <chrono>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/wait.h"
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/experimental/executors/events_executor/events_executor_event_types.hpp"
|
||||
#include "rclcpp/experimental/timers_manager.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace experimental
|
||||
{
|
||||
namespace executors
|
||||
{
|
||||
// forward declaration of EventsExecutor to avoid circular dependency
|
||||
class EventsExecutor;
|
||||
|
||||
typedef std::map<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
|
||||
std::owner_less<rclcpp::CallbackGroup::WeakPtr>> WeakCallbackGroupsToNodesMap;
|
||||
|
||||
/**
|
||||
* @brief This class provides a waitable object that is used for managing the
|
||||
* entities (i.e. nodes and their subscriptions, timers, services, etc)
|
||||
* added to an EventsExecutor.
|
||||
* The add/remove node APIs are used when a node is added/removed from
|
||||
* the associated EventsExecutor and result in setting/unsetting the
|
||||
* events callbacks and adding timers to the timers manager.
|
||||
*
|
||||
* Being this class derived from Waitable, it can be used to wake up an
|
||||
* executor thread while it's spinning.
|
||||
* When this occurs, the execute API takes care of handling changes
|
||||
* in the entities currently used by the executor.
|
||||
*/
|
||||
class EventsExecutorEntitiesCollector final
|
||||
: public rclcpp::Waitable,
|
||||
public std::enable_shared_from_this<EventsExecutorEntitiesCollector>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(EventsExecutorEntitiesCollector)
|
||||
|
||||
// Constructor
|
||||
RCLCPP_PUBLIC
|
||||
EventsExecutorEntitiesCollector(
|
||||
EventsExecutor * executor);
|
||||
|
||||
// Destructor
|
||||
RCLCPP_PUBLIC
|
||||
~EventsExecutorEntitiesCollector() override;
|
||||
|
||||
// Initialize entities collector
|
||||
RCLCPP_PUBLIC
|
||||
void init();
|
||||
|
||||
/// Execute the waitable.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
execute(std::shared_ptr<void> & data) override;
|
||||
|
||||
/// Function to add_handles_to_wait_set and wait for work and
|
||||
/**
|
||||
* block until the wait set is ready or until the timeout has been exceeded.
|
||||
* \throws std::runtime_error if wait set couldn't be cleared or filled.
|
||||
* \throws any rcl errors from rcl_wait, \see rclcpp::exceptions::throw_from_rcl_error()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
refresh_wait_set(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
|
||||
|
||||
/**
|
||||
* \throws std::runtime_error if it couldn't add guard condition to wait set
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set) override;
|
||||
|
||||
/// Complete all available queued work without blocking.
|
||||
/**
|
||||
* This function checks if after the guard condition was triggered
|
||||
* (or a spurious wakeup happened) we are really ready to execute
|
||||
* i.e. re-collect entities
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<void>
|
||||
take_data() override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<void>
|
||||
take_data_by_entity_id(size_t id) override;
|
||||
|
||||
/// Add a callback group to an executor.
|
||||
/**
|
||||
* \see rclcpp::Executor::add_callback_group
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
add_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
|
||||
|
||||
/// Add a callback group to an executor.
|
||||
/**
|
||||
* \see rclcpp::Executor::add_callback_group
|
||||
* \return boolean whether the node from the callback group is new
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
add_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
/// Remove a callback group from the executor.
|
||||
/**
|
||||
* \see rclcpp::Executor::remove_callback_group
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
remove_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr);
|
||||
|
||||
/// Remove a callback group from the executor.
|
||||
/**
|
||||
* \see rclcpp::Executor::remove_callback_group_from_map
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
remove_callback_group_from_map(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
/**
|
||||
* \see rclcpp::Executor::add_node()
|
||||
* \throw std::runtime_error if node was already added
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
add_node(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
|
||||
|
||||
/**
|
||||
* \see rclcpp::Executor::remove_node()
|
||||
* \throw std::runtime_error if no guard condition is associated with node.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
remove_node(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_all_callback_groups();
|
||||
|
||||
/// Get callback groups that belong to executor.
|
||||
/**
|
||||
* \see rclcpp::Executor::get_manually_added_callback_groups()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_manually_added_callback_groups();
|
||||
|
||||
/// Get callback groups that belong to executor.
|
||||
/**
|
||||
* \see rclcpp::Executor::get_automatically_added_callback_groups_from_nodes()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_automatically_added_callback_groups_from_nodes();
|
||||
|
||||
///
|
||||
/**
|
||||
* Get the subscription shared pointer corresponding
|
||||
* to a subscription identifier
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::SubscriptionBase::SharedPtr
|
||||
get_subscription(const void * subscription_id);
|
||||
|
||||
///
|
||||
/**
|
||||
* Get the client shared pointer corresponding
|
||||
* to a client identifier
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::ClientBase::SharedPtr
|
||||
get_client(const void * client_id);
|
||||
|
||||
///
|
||||
/**
|
||||
* Get the service shared pointer corresponding
|
||||
* to a service identifier
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::ServiceBase::SharedPtr
|
||||
get_service(const void * service_id);
|
||||
|
||||
///
|
||||
/**
|
||||
* Get the waitable shared pointer corresponding
|
||||
* to a waitable identifier
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::Waitable::SharedPtr
|
||||
get_waitable(const void * waitable_id);
|
||||
|
||||
///
|
||||
/**
|
||||
* Add a weak pointer to a waitable
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_waitable(rclcpp::Waitable::SharedPtr waitable);
|
||||
|
||||
private:
|
||||
/// Return true if the node belongs to the collector
|
||||
/**
|
||||
* \param[in] node_ptr a node base interface shared pointer
|
||||
* \param[in] weak_groups_to_nodes map to nodes to lookup
|
||||
* \return boolean whether a node belongs the collector
|
||||
*/
|
||||
bool
|
||||
has_node(
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) const;
|
||||
|
||||
/// Add all callback groups that can be automatically added by any executor
|
||||
/// and is not already associated with an executor from nodes
|
||||
/// that are associated with executor
|
||||
/**
|
||||
* \see rclcpp::Executor::add_callback_groups_from_nodes_associated_to_executor()
|
||||
*/
|
||||
void
|
||||
add_callback_groups_from_nodes_associated_to_executor();
|
||||
|
||||
void
|
||||
callback_group_added_impl(
|
||||
rclcpp::CallbackGroup::SharedPtr group);
|
||||
|
||||
void
|
||||
node_added_impl(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node);
|
||||
|
||||
void
|
||||
callback_group_removed_impl(
|
||||
rclcpp::CallbackGroup::SharedPtr group);
|
||||
|
||||
void
|
||||
node_removed_impl(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node);
|
||||
|
||||
void
|
||||
set_entities_event_callbacks_from_map(
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
void
|
||||
set_callback_group_entities_callbacks(rclcpp::CallbackGroup::SharedPtr group);
|
||||
|
||||
void
|
||||
unset_callback_group_entities_callbacks(rclcpp::CallbackGroup::SharedPtr group);
|
||||
|
||||
void
|
||||
set_guard_condition_callback(rclcpp::GuardCondition * guard_condition);
|
||||
|
||||
void
|
||||
unset_guard_condition_callback(rclcpp::GuardCondition * guard_condition);
|
||||
|
||||
std::function<void(size_t)>
|
||||
create_entity_callback(void * exec_entity_id, ExecutorEventType type);
|
||||
|
||||
std::function<void(size_t, int)>
|
||||
create_waitable_callback(void * waitable_id);
|
||||
|
||||
typedef std::map<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::GuardCondition *,
|
||||
std::owner_less<rclcpp::CallbackGroup::WeakPtr>>
|
||||
WeakCallbackGroupsToGuardConditionsMap;
|
||||
|
||||
typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
|
||||
const rclcpp::GuardCondition *,
|
||||
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>
|
||||
WeakNodesToGuardConditionsMap;
|
||||
|
||||
/// maps callback groups to guard conditions
|
||||
WeakCallbackGroupsToGuardConditionsMap weak_groups_to_guard_conditions_;
|
||||
|
||||
WeakNodesToGuardConditionsMap weak_nodes_to_guard_conditions_;
|
||||
|
||||
// maps callback groups to nodes.
|
||||
WeakCallbackGroupsToNodesMap weak_groups_associated_with_executor_to_nodes_;
|
||||
// maps callback groups to nodes.
|
||||
WeakCallbackGroupsToNodesMap weak_groups_to_nodes_associated_with_executor_;
|
||||
|
||||
/// List of weak nodes registered in the static executor
|
||||
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> weak_nodes_;
|
||||
|
||||
// Maps: entity identifiers to weak pointers from the entities registered in the executor
|
||||
// so in the case of an event providing and ID, we can retrieve and own the corresponding
|
||||
// entity while it performs work
|
||||
std::unordered_map<const void *, rclcpp::SubscriptionBase::WeakPtr> weak_subscriptions_map_;
|
||||
std::unordered_map<const void *, rclcpp::ServiceBase::WeakPtr> weak_services_map_;
|
||||
std::unordered_map<const void *, rclcpp::ClientBase::WeakPtr> weak_clients_map_;
|
||||
std::unordered_map<const void *, rclcpp::Waitable::WeakPtr> weak_waitables_map_;
|
||||
|
||||
/// Executor using this entities collector object
|
||||
EventsExecutor * associated_executor_ = nullptr;
|
||||
/// Instance of the timers manager used by the associated executor
|
||||
rclcpp::experimental::TimersManager::SharedPtr timers_manager_;
|
||||
|
||||
// Mutex to protect vector of new nodes.
|
||||
std::recursive_mutex reentrant_mutex_;
|
||||
};
|
||||
|
||||
} // namespace executors
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_ENTITIES_COLLECTOR_HPP_
|
||||
@@ -1,46 +0,0 @@
|
||||
// Copyright 2023 iRobot Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_EVENT_TYPES_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_EVENT_TYPES_HPP_
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace experimental
|
||||
{
|
||||
namespace executors
|
||||
{
|
||||
|
||||
enum ExecutorEventType
|
||||
{
|
||||
CLIENT_EVENT,
|
||||
SUBSCRIPTION_EVENT,
|
||||
SERVICE_EVENT,
|
||||
TIMER_EVENT,
|
||||
WAITABLE_EVENT
|
||||
};
|
||||
|
||||
struct ExecutorEvent
|
||||
{
|
||||
const void * exec_entity_id;
|
||||
int gen_entity_id;
|
||||
ExecutorEventType type;
|
||||
size_t num_events;
|
||||
};
|
||||
|
||||
} // namespace executors
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_EVENT_TYPES_HPP_
|
||||
@@ -1,103 +0,0 @@
|
||||
// Copyright 2023 iRobot Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_NOTIFY_WAITABLE_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_NOTIFY_WAITABLE_HPP_
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rclcpp/experimental/executors/events_executor/event_waitable.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace experimental
|
||||
{
|
||||
namespace executors
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief This class provides an EventWaitable that allows to
|
||||
* wake up an EventsExecutor when a guard condition is notified.
|
||||
*/
|
||||
class EventsExecutorNotifyWaitable final : public EventWaitable
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(EventsExecutorNotifyWaitable)
|
||||
|
||||
// Constructor
|
||||
RCLCPP_PUBLIC
|
||||
EventsExecutorNotifyWaitable() = default;
|
||||
|
||||
// Destructor
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~EventsExecutorNotifyWaitable() = default;
|
||||
|
||||
// The function is a no-op, since we only care of waking up the executor
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
execute(std::shared_ptr<void> & data) override
|
||||
{
|
||||
(void)data;
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_guard_condition(rclcpp::GuardCondition * guard_condition)
|
||||
{
|
||||
notify_guard_conditions_.push_back(guard_condition);
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_on_ready_callback(std::function<void(size_t, int)> callback) override
|
||||
{
|
||||
// The second argument of the callback should identify which guard condition
|
||||
// triggered the event. However it's not relevant here as we only
|
||||
// care about waking up the executor, so we pass a 0.
|
||||
auto gc_callback = [callback](size_t count) {
|
||||
callback(count, 0);
|
||||
};
|
||||
|
||||
for (auto gc : notify_guard_conditions_) {
|
||||
gc->set_on_trigger_callback(gc_callback);
|
||||
}
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<void>
|
||||
take_data() override
|
||||
{
|
||||
// This waitable doesn't handle any data
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<void>
|
||||
take_data_by_entity_id(size_t id) override
|
||||
{
|
||||
(void) id;
|
||||
return take_data();
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<rclcpp::GuardCondition *> notify_guard_conditions_;
|
||||
};
|
||||
|
||||
} // namespace executors
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_NOTIFY_WAITABLE_HPP_
|
||||
@@ -1,100 +0,0 @@
|
||||
// Copyright 2023 iRobot Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_QUEUE_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_QUEUE_HPP_
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "rclcpp/experimental/executors/events_executor/events_executor_event_types.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace experimental
|
||||
{
|
||||
namespace executors
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief This abstract class can be used to implement different types of queues
|
||||
* where `ExecutorEvent` can be stored.
|
||||
* The derived classes should choose which underlying container to use and
|
||||
* the strategy for pushing and popping events.
|
||||
* For example a queue implementation may be bounded or unbounded and have
|
||||
* different pruning strategies.
|
||||
* Implementations may or may not check the validity of events and decide how to handle
|
||||
* the situation where an event is not valid anymore (e.g. a subscription history cache overruns)
|
||||
*/
|
||||
class EventsQueue
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(EventsQueue)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
EventsQueue() = default;
|
||||
|
||||
/**
|
||||
* @brief Destruct the object.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~EventsQueue() = default;
|
||||
|
||||
/**
|
||||
* @brief push event into the queue
|
||||
* @param event The event to push into the queue
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
enqueue(const rclcpp::experimental::executors::ExecutorEvent & event) = 0;
|
||||
|
||||
/**
|
||||
* @brief Extracts an event from the queue, eventually waiting until timeout
|
||||
* if none is available.
|
||||
* @return true if event has been found, false if timeout
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
dequeue(
|
||||
rclcpp::experimental::executors::ExecutorEvent & event,
|
||||
std::chrono::nanoseconds timeout = std::chrono::nanoseconds::max()) = 0;
|
||||
|
||||
/**
|
||||
* @brief Test whether queue is empty
|
||||
* @return true if the queue's size is 0, false otherwise.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
empty() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in the queue.
|
||||
* @return the number of elements in the queue.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
size_t
|
||||
size() const = 0;
|
||||
};
|
||||
|
||||
} // namespace executors
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_QUEUE_HPP_
|
||||
@@ -1,134 +0,0 @@
|
||||
// Copyright 2023 iRobot Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__SIMPLE_EVENTS_QUEUE_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__SIMPLE_EVENTS_QUEUE_HPP_
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/experimental/executors/events_executor/events_queue.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace experimental
|
||||
{
|
||||
namespace executors
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief This class implements an EventsQueue as a simple wrapper around a std::queue.
|
||||
* It does not perform any checks about the size of queue, which can grow
|
||||
* unbounded without being pruned.
|
||||
* The simplicity of this implementation makes it suitable for optimizing CPU usage.
|
||||
*/
|
||||
class SimpleEventsQueue : public EventsQueue
|
||||
{
|
||||
public:
|
||||
RCLCPP_PUBLIC
|
||||
~SimpleEventsQueue() override = default;
|
||||
|
||||
/**
|
||||
* @brief enqueue event into the queue
|
||||
* Thread safe
|
||||
* @param event The event to enqueue into the queue
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
enqueue(const rclcpp::experimental::executors::ExecutorEvent & event) override
|
||||
{
|
||||
rclcpp::experimental::executors::ExecutorEvent single_event = event;
|
||||
single_event.num_events = 1;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
for (size_t ev = 0; ev < event.num_events; ev++) {
|
||||
event_queue_.push(single_event);
|
||||
}
|
||||
}
|
||||
events_queue_cv_.notify_one();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief waits for an event until timeout, gets a single event
|
||||
* Thread safe
|
||||
* @return true if event, false if timeout
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
dequeue(
|
||||
rclcpp::experimental::executors::ExecutorEvent & event,
|
||||
std::chrono::nanoseconds timeout = std::chrono::nanoseconds::max()) override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
|
||||
// Initialize to true because it's only needed if we have a valid timeout
|
||||
bool has_data = true;
|
||||
if (timeout != std::chrono::nanoseconds::max()) {
|
||||
has_data =
|
||||
events_queue_cv_.wait_for(lock, timeout, [this]() {return !event_queue_.empty();});
|
||||
} else {
|
||||
events_queue_cv_.wait(lock, [this]() {return !event_queue_.empty();});
|
||||
}
|
||||
|
||||
if (has_data) {
|
||||
event = event_queue_.front();
|
||||
event_queue_.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test whether queue is empty
|
||||
* Thread safe
|
||||
* @return true if the queue's size is 0, false otherwise.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
empty() const override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
return event_queue_.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in the queue.
|
||||
* Thread safe
|
||||
* @return the number of elements in the queue.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
size() const override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
return event_queue_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
// The underlying queue implementation
|
||||
std::queue<rclcpp::experimental::executors::ExecutorEvent> event_queue_;
|
||||
// Mutex to protect the insertion/extraction of events in the queue
|
||||
mutable std::mutex mutex_;
|
||||
// Variable used to notify when an event is added to the queue
|
||||
std::condition_variable events_queue_cv_;
|
||||
};
|
||||
|
||||
} // namespace executors
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__SIMPLE_EVENTS_QUEUE_HPP_
|
||||
@@ -1,539 +0,0 @@
|
||||
// Copyright 2023 iRobot Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__EXPERIMENTAL__TIMERS_MANAGER_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__TIMERS_MANAGER_HPP_
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/timer.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace experimental
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief This class provides a way for storing and executing timer objects.
|
||||
* It provides APIs to suit the needs of different applications and execution models.
|
||||
* All public APIs provided by this class are thread-safe.
|
||||
*
|
||||
* Timers management
|
||||
* This class provides APIs to add/remove timers to/from an internal storage.
|
||||
* It keeps a list of weak pointers from added timers, and locks them only when
|
||||
* they need to be executed or modified.
|
||||
* Timers are kept ordered in a binary-heap priority queue.
|
||||
* Calls to add/remove APIs will temporarily block the execution of the timers and
|
||||
* will require to reorder the internal priority queue.
|
||||
* Because of this, they have a not-negligible impact on the performance.
|
||||
*
|
||||
* Timers execution
|
||||
* The most efficient implementation consists in letting a TimersManager object
|
||||
* to spawn a thread where timers are monitored and periodically executed.
|
||||
* Besides this, other APIs allow to either execute a single timer or all the
|
||||
* currently ready ones.
|
||||
* This class assumes that the `execute_callback()` API of the stored timers is never
|
||||
* called by other entities, but it can only be called from here.
|
||||
* If this assumption is not respected, the heap property may be invalidated,
|
||||
* so timers may be executed out of order, without this object noticing it.
|
||||
*
|
||||
*/
|
||||
class TimersManager
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(TimersManager)
|
||||
|
||||
/**
|
||||
* @brief Construct a new TimersManager object
|
||||
*
|
||||
* @param context custom context to be used.
|
||||
* Shared ownership of the context is held until destruction.
|
||||
* @param on_ready_callback The timers on ready callback. if not callable,
|
||||
* this object will directly execute timers when they are ready.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
TimersManager(
|
||||
std::shared_ptr<rclcpp::Context> context,
|
||||
std::function<void(void *)> on_ready_callback = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Destruct the TimersManager object making sure to stop thread and release memory.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
~TimersManager();
|
||||
|
||||
/**
|
||||
* @brief Adds a new timer to the storage, maintaining weak ownership of it.
|
||||
* Function is thread safe and it can be called regardless of the state of the timers thread.
|
||||
*
|
||||
* @param timer the timer to add.
|
||||
* @throws std::invalid_argument if timer is a nullptr.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void add_timer(rclcpp::TimerBase::SharedPtr timer);
|
||||
|
||||
/**
|
||||
* @brief Remove a single timer from the object storage.
|
||||
* Will do nothing if the timer was not being stored here.
|
||||
* Function is thread safe and it can be called regardless of the state of the timers thread.
|
||||
*
|
||||
* @param timer the timer to remove.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void remove_timer(rclcpp::TimerBase::SharedPtr timer);
|
||||
|
||||
/**
|
||||
* @brief Remove all the timers stored in the object.
|
||||
* Function is thread safe and it can be called regardless of the state of the timers thread.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief Starts a thread that takes care of executing the timers stored in this object.
|
||||
* Function will throw an error if the timers thread was already running.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void start();
|
||||
|
||||
/**
|
||||
* @brief Stops the timers thread.
|
||||
* Will do nothing if the timer thread was not running.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* @brief Executes all the timers currently ready when the function was invoked.
|
||||
* This function will lock all the stored timers throughout its duration.
|
||||
* This function is thread safe.
|
||||
* @throws std::runtime_error if the timers thread was already running.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void execute_ready_timers();
|
||||
|
||||
/**
|
||||
* @brief Get the number of timers that are currently ready.
|
||||
* This function is thread safe.
|
||||
*
|
||||
* @return size_t number of ready timers.
|
||||
* @throws std::runtime_error if the timers thread was already running.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
size_t get_number_ready_timers();
|
||||
|
||||
/**
|
||||
* @brief Executes head timer if ready.
|
||||
* This function is thread safe.
|
||||
*
|
||||
* @return true if head timer was ready.
|
||||
* @throws std::runtime_error if the timers thread was already running.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool execute_head_timer();
|
||||
|
||||
/**
|
||||
* @brief Executes timer identified by its ID.
|
||||
* This function is thread safe.
|
||||
*
|
||||
* @param timer_id the timer ID of the timer to execute
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void execute_ready_timer(const void * timer_id);
|
||||
|
||||
/**
|
||||
* @brief Get the amount of time before the next timer triggers.
|
||||
* This function is thread safe.
|
||||
*
|
||||
* @return std::chrono::nanoseconds to wait,
|
||||
* the returned value could be negative if the timer is already expired
|
||||
* or std::chrono::nanoseconds::max() if there are no timers stored in the object.
|
||||
* @throws std::runtime_error if the timers thread was already running.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::chrono::nanoseconds get_head_timeout();
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(TimersManager)
|
||||
|
||||
using TimerPtr = rclcpp::TimerBase::SharedPtr;
|
||||
using WeakTimerPtr = rclcpp::TimerBase::WeakPtr;
|
||||
|
||||
// Forward declaration
|
||||
class TimersHeap;
|
||||
|
||||
/**
|
||||
* @brief This class allows to store weak pointers to timers in a heap-like data structure.
|
||||
* The root of the heap is the timer that triggers first.
|
||||
* Since this class uses weak ownership, it is not guaranteed that it represents a valid heap
|
||||
* at any point in time as timers could go out of scope, thus invalidating it.
|
||||
* The "validate_and_lock" API allows to restore the heap property and also returns a locked version
|
||||
* of the timers heap.
|
||||
* This class is not thread safe and requires external mutexes to protect its usage.
|
||||
*/
|
||||
class WeakTimersHeap
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Add a new timer to the heap. After the addition, the heap property is enforced.
|
||||
*
|
||||
* @param timer new timer to add.
|
||||
* @return true if timer has been added, false if it was already there.
|
||||
*/
|
||||
bool add_timer(TimerPtr timer)
|
||||
{
|
||||
TimersHeap locked_heap = this->validate_and_lock();
|
||||
bool added = locked_heap.add_timer(std::move(timer));
|
||||
|
||||
if (added) {
|
||||
// Re-create the weak heap with the new timer added
|
||||
this->store(locked_heap);
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove a timer from the heap. After the removal, the heap property is enforced.
|
||||
*
|
||||
* @param timer timer to remove.
|
||||
* @return true if timer has been removed, false if it was not there.
|
||||
*/
|
||||
bool remove_timer(TimerPtr timer)
|
||||
{
|
||||
TimersHeap locked_heap = this->validate_and_lock();
|
||||
bool removed = locked_heap.remove_timer(std::move(timer));
|
||||
|
||||
if (removed) {
|
||||
// Re-create the weak heap with the timer removed
|
||||
this->store(locked_heap);
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
TimerPtr get_timer(const void * timer_id)
|
||||
{
|
||||
for (auto & weak_timer : weak_heap_) {
|
||||
auto timer = weak_timer.lock();
|
||||
if (timer.get() == timer_id) {
|
||||
return timer;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a const reference to the front element.
|
||||
*/
|
||||
const WeakTimerPtr & front() const
|
||||
{
|
||||
return weak_heap_.front();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns whether the heap is empty or not.
|
||||
*/
|
||||
bool empty() const
|
||||
{
|
||||
return weak_heap_.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function restores the current object as a valid heap
|
||||
* and it returns a locked version of it.
|
||||
* Timers that went out of scope are removed from the container.
|
||||
* It is the only public API to access and manipulate the stored timers.
|
||||
*
|
||||
* @return TimersHeap owned timers corresponding to the current object
|
||||
*/
|
||||
TimersHeap validate_and_lock()
|
||||
{
|
||||
TimersHeap locked_heap;
|
||||
bool any_timer_destroyed = false;
|
||||
|
||||
for (auto weak_timer : weak_heap_) {
|
||||
auto timer = weak_timer.lock();
|
||||
if (timer) {
|
||||
// This timer is valid, so add it to the locked heap
|
||||
// Note: we access friend private `owned_heap_` member field.
|
||||
locked_heap.owned_heap_.push_back(std::move(timer));
|
||||
} else {
|
||||
// This timer went out of scope, so we don't add it to locked heap
|
||||
// and we mark the corresponding flag.
|
||||
// It's not needed to erase it from weak heap, as we are going to re-heapify.
|
||||
// Note: we can't exit from the loop here, as we need to find all valid timers.
|
||||
any_timer_destroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If a timer has gone out of scope, then the remaining elements do not represent
|
||||
// a valid heap anymore. We need to re-heapify the timers heap.
|
||||
if (any_timer_destroyed) {
|
||||
locked_heap.heapify();
|
||||
// Re-create the weak heap now that elements have been heapified again
|
||||
this->store(locked_heap);
|
||||
}
|
||||
|
||||
return locked_heap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function allows to recreate the heap of weak pointers
|
||||
* from an heap of owned pointers.
|
||||
* It is required to be called after a locked TimersHeap generated from this object
|
||||
* has been modified in any way (e.g. timers triggered, added, removed).
|
||||
*
|
||||
* @param heap timers heap to store as weak pointers
|
||||
*/
|
||||
void store(const TimersHeap & heap)
|
||||
{
|
||||
weak_heap_.clear();
|
||||
// Note: we access friend private `owned_heap_` member field.
|
||||
for (auto t : heap.owned_heap_) {
|
||||
weak_heap_.push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove all timers from the heap.
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
weak_heap_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<WeakTimerPtr> weak_heap_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This class is the equivalent of WeakTimersHeap but with ownership of the timers.
|
||||
* It can be generated by locking the weak version.
|
||||
* It provides operations to manipulate the heap.
|
||||
* This class is not thread safe and requires external mutexes to protect its usage.
|
||||
*/
|
||||
class TimersHeap
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Try to add a new timer to the heap.
|
||||
* After the addition, the heap property is preserved.
|
||||
* @param timer new timer to add.
|
||||
* @return true if timer has been added, false if it was already there.
|
||||
*/
|
||||
bool add_timer(TimerPtr timer)
|
||||
{
|
||||
// Nothing to do if the timer is already stored here
|
||||
auto it = std::find(owned_heap_.begin(), owned_heap_.end(), timer);
|
||||
if (it != owned_heap_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
owned_heap_.push_back(std::move(timer));
|
||||
std::push_heap(owned_heap_.begin(), owned_heap_.end(), timer_greater);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Try to remove a timer from the heap.
|
||||
* After the removal, the heap property is preserved.
|
||||
* @param timer timer to remove.
|
||||
* @return true if timer has been removed, false if it was not there.
|
||||
*/
|
||||
bool remove_timer(TimerPtr timer)
|
||||
{
|
||||
// Nothing to do if the timer is not stored here
|
||||
auto it = std::find(owned_heap_.begin(), owned_heap_.end(), timer);
|
||||
if (it == owned_heap_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
owned_heap_.erase(it);
|
||||
this->heapify();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the front element.
|
||||
* @return reference to front element.
|
||||
*/
|
||||
TimerPtr & front()
|
||||
{
|
||||
return owned_heap_.front();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a const reference to the front element.
|
||||
* @return const reference to front element.
|
||||
*/
|
||||
const TimerPtr & front() const
|
||||
{
|
||||
return owned_heap_.front();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns whether the heap is empty or not.
|
||||
* @return true if the heap is empty.
|
||||
*/
|
||||
bool empty() const
|
||||
{
|
||||
return owned_heap_.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the size of the heap.
|
||||
* @return the number of valid timers in the heap.
|
||||
*/
|
||||
size_t size() const
|
||||
{
|
||||
return owned_heap_.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the number of timers that are currently ready.
|
||||
* @return size_t number of ready timers.
|
||||
*/
|
||||
size_t get_number_ready_timers() const
|
||||
{
|
||||
size_t ready_timers = 0;
|
||||
|
||||
for (TimerPtr t : owned_heap_) {
|
||||
if (t->is_ready()) {
|
||||
ready_timers++;
|
||||
}
|
||||
}
|
||||
|
||||
return ready_timers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restore a valid heap after the root value has been replaced (e.g. timer triggered).
|
||||
*/
|
||||
void heapify_root()
|
||||
{
|
||||
// The following code is a more efficient version than doing
|
||||
// pop_heap, pop_back, push_back, push_heap
|
||||
// as it removes the need for the last push_heap
|
||||
|
||||
// Push the modified element (i.e. the current root) at the bottom of the heap
|
||||
owned_heap_.push_back(owned_heap_[0]);
|
||||
// Exchange first and last-1 elements and reheapify
|
||||
std::pop_heap(owned_heap_.begin(), owned_heap_.end(), timer_greater);
|
||||
// Remove last element
|
||||
owned_heap_.pop_back();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Completely restores the structure to a valid heap
|
||||
*/
|
||||
void heapify()
|
||||
{
|
||||
std::make_heap(owned_heap_.begin(), owned_heap_.end(), timer_greater);
|
||||
}
|
||||
|
||||
void clear_callbacks()
|
||||
{
|
||||
for (TimerPtr & t : owned_heap_) {
|
||||
t->clear_on_reset_callback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Friend declaration to allow the `validate_and_lock()` function to access the
|
||||
* underlying heap container
|
||||
*/
|
||||
friend TimersHeap WeakTimersHeap::validate_and_lock();
|
||||
|
||||
/**
|
||||
* @brief Friend declaration to allow the `store()` function to access the
|
||||
* underlying heap container
|
||||
*/
|
||||
friend void WeakTimersHeap::store(const TimersHeap & heap);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Comparison function between timers.
|
||||
* @return true if `a` triggers after `b`.
|
||||
*/
|
||||
static bool timer_greater(TimerPtr a, TimerPtr b)
|
||||
{
|
||||
// FIXME!
|
||||
return a->time_until_trigger() > b->time_until_trigger();
|
||||
}
|
||||
|
||||
std::vector<TimerPtr> owned_heap_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implements a loop that keeps executing ready timers.
|
||||
* This function is executed in the timers thread.
|
||||
*/
|
||||
void run_timers();
|
||||
|
||||
/**
|
||||
* @brief Get the amount of time before the next timer triggers.
|
||||
* This function is not thread safe, acquire a mutex before calling it.
|
||||
*
|
||||
* @return std::chrono::nanoseconds to wait,
|
||||
* the returned value could be negative if the timer is already expired
|
||||
* or std::chrono::nanoseconds::max() if the heap is empty.
|
||||
* This function is not thread safe, acquire the timers_mutex_ before calling it.
|
||||
*/
|
||||
std::chrono::nanoseconds get_head_timeout_unsafe();
|
||||
|
||||
/**
|
||||
* @brief Executes all the timers currently ready when the function is invoked
|
||||
* while keeping the heap correctly sorted.
|
||||
* This function is not thread safe, acquire the timers_mutex_ before calling it.
|
||||
*/
|
||||
void execute_ready_timers_unsafe();
|
||||
|
||||
// Callback to be called when timer is ready
|
||||
std::function<void(void *)> on_ready_callback_ = nullptr;
|
||||
|
||||
// Thread used to run the timers execution task
|
||||
std::thread timers_thread_;
|
||||
// Protects access to timers
|
||||
std::mutex timers_mutex_;
|
||||
// Protects access to stop()
|
||||
std::mutex stop_mutex_;
|
||||
// Notifies the timers thread whenever timers are added/removed
|
||||
std::condition_variable timers_cv_;
|
||||
// Flag used as predicate by timers_cv_ that denotes one or more timers being added/removed
|
||||
bool timers_updated_ {false};
|
||||
// Indicates whether the timers thread is currently running or not
|
||||
std::atomic<bool> running_ {false};
|
||||
// Parent context used to understand if ROS is still active
|
||||
std::shared_ptr<rclcpp::Context> context_;
|
||||
// Timers heap storage with weak ownership
|
||||
WeakTimersHeap weak_timers_heap_;
|
||||
};
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXPERIMENTAL__TIMERS_MANAGER_HPP_
|
||||
@@ -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,32 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
void return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> & message) override;
|
||||
|
||||
|
||||
// DYNAMIC TYPE ==================================================================================
|
||||
// TODO(methylDragon): Reorder later
|
||||
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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -117,6 +117,21 @@
|
||||
* - 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
|
||||
* - Dynamic subscription
|
||||
* - rclcpp::DynamicSubscription
|
||||
* - rclcpp/dynamic_subscription.hpp
|
||||
* - Generic publisher
|
||||
* - rclcpp::Node::create_generic_publisher()
|
||||
* - rclcpp::GenericPublisher
|
||||
@@ -167,4 +182,6 @@
|
||||
#include "rclcpp/waitable.hpp"
|
||||
#include "rclcpp/wait_set.hpp"
|
||||
|
||||
#include "rclcpp/dynamic_subscription.hpp"
|
||||
|
||||
#endif // RCLCPP__RCLCPP_HPP_
|
||||
|
||||
@@ -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_
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
|
||||
#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"
|
||||
@@ -60,6 +63,14 @@ namespace experimental
|
||||
class IntraProcessManager;
|
||||
} // namespace experimental
|
||||
|
||||
enum class SubscriptionType : uint8_t
|
||||
{
|
||||
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 +87,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 +97,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
|
||||
@@ -227,13 +238,13 @@ public:
|
||||
const rosidl_message_type_support_t &
|
||||
get_message_type_support_handle() const;
|
||||
|
||||
/// Return if the subscription is serialized
|
||||
/// Return the type of the subscription.
|
||||
/**
|
||||
* \return `true` if the subscription is serialized, `false` otherwise
|
||||
* \return `SubscriptionType`, which adjusts how messages are received and delivered.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
is_serialized() const;
|
||||
SubscriptionType
|
||||
get_subscription_type() const;
|
||||
|
||||
/// Get matching publisher count.
|
||||
/** \return The number of publishers on this topic. */
|
||||
@@ -535,6 +546,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
|
||||
@@ -568,6 +622,10 @@ protected:
|
||||
|
||||
rclcpp::node_interfaces::NodeBaseInterface * const node_base_;
|
||||
|
||||
// TODO(methylDragon): Remove if we don't need this
|
||||
// rclcpp::node_interfaces::NodeGraphInterface * const node_graph_;
|
||||
// rclcpp::node_interfaces::NodeServicesInterface * const node_services_;
|
||||
|
||||
std::shared_ptr<rcl_node_t> node_handle_;
|
||||
std::shared_ptr<rcl_subscription_t> subscription_handle_;
|
||||
std::shared_ptr<rcl_subscription_t> intra_process_subscription_handle_;
|
||||
@@ -587,7 +645,7 @@ 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};
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
113
rclcpp/src/rclcpp/dynamic_subscription.cpp
Normal file
113
rclcpp/src/rclcpp/dynamic_subscription.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// 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 "rclcpp/dynamic_subscription.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rcl/subscription.h"
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
std::shared_ptr<void> DynamicSubscription::create_message()
|
||||
{
|
||||
return create_serialized_message();
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::SerializedMessage> DynamicSubscription::create_serialized_message()
|
||||
{
|
||||
return std::make_shared<rclcpp::SerializedMessage>(0);
|
||||
}
|
||||
|
||||
void DynamicSubscription::handle_message(std::shared_ptr<void> &, const rclcpp::MessageInfo &)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"handle_message is not implemented for DynamicSubscription");
|
||||
}
|
||||
|
||||
void DynamicSubscription::handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> &, const rclcpp::MessageInfo &)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"handle_serialized_message is not implemented for DynamicSubscription");
|
||||
}
|
||||
|
||||
void DynamicSubscription::handle_loaned_message(void *, const rclcpp::MessageInfo &)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"handle_loaned_message is not implemented for DynamicSubscription");
|
||||
}
|
||||
|
||||
void DynamicSubscription::return_message(std::shared_ptr<void> & message)
|
||||
{
|
||||
auto typed_message = std::static_pointer_cast<rclcpp::SerializedMessage>(message);
|
||||
return_serialized_message(typed_message);
|
||||
}
|
||||
|
||||
void DynamicSubscription::return_serialized_message(
|
||||
std::shared_ptr<rclcpp::SerializedMessage> & message)
|
||||
{
|
||||
message.reset();
|
||||
}
|
||||
|
||||
|
||||
// DYNAMIC TYPE ====================================================================================
|
||||
// TODO(methylDragon): Re-order later
|
||||
|
||||
// Does not clone
|
||||
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr
|
||||
DynamicSubscription::get_shared_dynamic_message_type()
|
||||
{
|
||||
return dynamic_message_type_;
|
||||
}
|
||||
|
||||
// Does not clone
|
||||
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
|
||||
DynamicSubscription::get_shared_dynamic_message()
|
||||
{
|
||||
return dynamic_message_;
|
||||
}
|
||||
|
||||
rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr
|
||||
DynamicSubscription::get_shared_dynamic_serialization_support()
|
||||
{
|
||||
return serialization_support_;
|
||||
}
|
||||
|
||||
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
|
||||
DynamicSubscription::create_dynamic_message()
|
||||
{
|
||||
return dynamic_message_->init_from_type_shared(*dynamic_message_type_);
|
||||
}
|
||||
|
||||
void
|
||||
DynamicSubscription::return_dynamic_message(
|
||||
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message)
|
||||
{
|
||||
message.reset();
|
||||
}
|
||||
|
||||
void DynamicSubscription::handle_dynamic_message(
|
||||
const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
(void) message_info;
|
||||
callback_(message, ts_->get_rosidl_runtime_c_type_description());
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
769
rclcpp/src/rclcpp/dynamic_typesupport/dynamic_message.cpp
Normal file
769
rclcpp/src/rclcpp/dynamic_typesupport/dynamic_message.cpp
Normal file
@@ -0,0 +1,769 @@
|
||||
// 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;
|
||||
|
||||
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_IMPL_HPP_
|
||||
// Template specialization implementations
|
||||
#include "rclcpp/dynamic_typesupport/detail/dynamic_message_impl.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
// CONSTRUCTION ==================================================================================
|
||||
DynamicMessage::DynamicMessage(const DynamicMessageTypeBuilder::SharedPtr dynamic_type_builder)
|
||||
: DynamicMessage::DynamicMessage(
|
||||
dynamic_type_builder, dynamic_type_builder->get_rosidl_dynamic_type_builder().allocator) {}
|
||||
|
||||
|
||||
DynamicMessage::DynamicMessage(
|
||||
const DynamicMessageTypeBuilder::SharedPtr dynamic_type_builder,
|
||||
rcl_allocator_t allocator)
|
||||
: serialization_support_(dynamic_type_builder->get_shared_dynamic_serialization_support()),
|
||||
rosidl_dynamic_data_(rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data()),
|
||||
is_loaned_(false),
|
||||
parent_data_(nullptr)
|
||||
{
|
||||
if (!serialization_support_) {
|
||||
throw std::runtime_error("dynamic message could not bind serialization support!");
|
||||
}
|
||||
if (!dynamic_type_builder) {
|
||||
throw std::runtime_error("dynamic message type builder cannot be nullptr!");
|
||||
}
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
|
||||
dynamic_type_builder->get_rosidl_dynamic_type_builder();
|
||||
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_init_from_dynamic_type_builder(
|
||||
&rosidl_dynamic_type_builder, &allocator, &get_rosidl_dynamic_data());
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error("could not init new dynamic data object from dynamic type builder");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage::DynamicMessage(const DynamicMessageType::SharedPtr dynamic_type)
|
||||
: DynamicMessage::DynamicMessage(
|
||||
dynamic_type, dynamic_type->get_rosidl_dynamic_type().allocator) {}
|
||||
|
||||
|
||||
DynamicMessage::DynamicMessage(
|
||||
const DynamicMessageType::SharedPtr dynamic_type,
|
||||
rcl_allocator_t allocator)
|
||||
: serialization_support_(dynamic_type->get_shared_dynamic_serialization_support()),
|
||||
rosidl_dynamic_data_(rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data()),
|
||||
is_loaned_(false),
|
||||
parent_data_(nullptr)
|
||||
{
|
||||
if (!serialization_support_) {
|
||||
throw std::runtime_error("dynamic type could not bind serialization support!");
|
||||
}
|
||||
if (!dynamic_type) {
|
||||
throw std::runtime_error("dynamic message type cannot be nullptr!");
|
||||
}
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_type_t rosidl_dynamic_type =
|
||||
dynamic_type->get_rosidl_dynamic_type();
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_init_from_dynamic_type(
|
||||
&rosidl_dynamic_type, &allocator, &rosidl_dynamic_data);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("could not init new dynamic data object from dynamic type") +
|
||||
rcl_get_error_string().str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage::DynamicMessage(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
rosidl_dynamic_typesupport_dynamic_data_t && rosidl_dynamic_data)
|
||||
: serialization_support_(serialization_support),
|
||||
rosidl_dynamic_data_(std::move(rosidl_dynamic_data)),
|
||||
is_loaned_(false),
|
||||
parent_data_(nullptr)
|
||||
{
|
||||
if (serialization_support) {
|
||||
if (!match_serialization_support_(*serialization_support, rosidl_dynamic_data)) {
|
||||
throw std::runtime_error(
|
||||
"serialization support library identifier does not match dynamic data's!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage::DynamicMessage(
|
||||
DynamicMessage::SharedPtr parent_data,
|
||||
rosidl_dynamic_typesupport_dynamic_data_t && rosidl_loaned_data)
|
||||
: serialization_support_(parent_data->get_shared_dynamic_serialization_support()),
|
||||
rosidl_dynamic_data_(std::move(rosidl_loaned_data)),
|
||||
is_loaned_(true),
|
||||
parent_data_(nullptr)
|
||||
{
|
||||
if (!parent_data) {
|
||||
throw std::runtime_error("parent dynamic data cannot be nullptr!");
|
||||
}
|
||||
if (serialization_support_) {
|
||||
if (!match_serialization_support_(*serialization_support_, rosidl_loaned_data)) {
|
||||
throw std::runtime_error(
|
||||
"serialization support library identifier does not match loaned dynamic data's!");
|
||||
}
|
||||
}
|
||||
parent_data_ = parent_data;
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage::DynamicMessage(DynamicMessage && other) noexcept
|
||||
: serialization_support_(std::exchange(other.serialization_support_, nullptr)),
|
||||
rosidl_dynamic_data_(std::exchange(
|
||||
other.rosidl_dynamic_data_,
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data())),
|
||||
is_loaned_(other.is_loaned_),
|
||||
parent_data_(std::exchange(other.parent_data_, nullptr))
|
||||
{}
|
||||
|
||||
|
||||
DynamicMessage &
|
||||
DynamicMessage::operator=(DynamicMessage && other) noexcept
|
||||
{
|
||||
std::swap(serialization_support_, other.serialization_support_);
|
||||
std::swap(rosidl_dynamic_data_, other.rosidl_dynamic_data_);
|
||||
is_loaned_ = other.is_loaned_;
|
||||
std::swap(parent_data_, other.parent_data_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage::~DynamicMessage()
|
||||
{
|
||||
if (!is_loaned_) {
|
||||
if (rosidl_dynamic_typesupport_dynamic_data_fini(&get_rosidl_dynamic_data()) !=
|
||||
RCUTILS_RET_OK)
|
||||
{
|
||||
RCUTILS_LOG_ERROR("could not fini rosidl dynamic data");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Loaned case
|
||||
if (!parent_data_) {
|
||||
RCUTILS_LOG_ERROR("dynamic data is loaned, but parent is missing!!");
|
||||
} else {
|
||||
rosidl_dynamic_typesupport_dynamic_data_return_loaned_value(
|
||||
&parent_data_->get_rosidl_dynamic_data(), &get_rosidl_dynamic_data());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DynamicMessage::match_serialization_support_(
|
||||
const DynamicSerializationSupport & serialization_support,
|
||||
const rosidl_dynamic_typesupport_dynamic_data_t & rosidl_dynamic_type_data)
|
||||
{
|
||||
if (serialization_support.get_serialization_library_identifier() != std::string(
|
||||
rosidl_dynamic_type_data.serialization_support->serialization_library_identifier))
|
||||
{
|
||||
RCUTILS_LOG_ERROR("serialization support library identifier does not match dynamic data's");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// GETTERS =======================================================================================
|
||||
const std::string
|
||||
DynamicMessage::get_serialization_library_identifier() const
|
||||
{
|
||||
return std::string(
|
||||
get_rosidl_dynamic_data().serialization_support->serialization_library_identifier);
|
||||
}
|
||||
|
||||
|
||||
const std::string
|
||||
DynamicMessage::get_name() const
|
||||
{
|
||||
size_t buf_length;
|
||||
const char * buf;
|
||||
if (
|
||||
rosidl_dynamic_typesupport_dynamic_data_get_name(
|
||||
&get_rosidl_dynamic_data(), &buf,
|
||||
&buf_length) !=
|
||||
RCUTILS_RET_OK)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string("could not get name for dynamic data") + rcl_get_error_string().str);
|
||||
}
|
||||
return std::string(buf, buf_length);
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_data_t &
|
||||
DynamicMessage::get_rosidl_dynamic_data()
|
||||
{
|
||||
return rosidl_dynamic_data_;
|
||||
}
|
||||
|
||||
|
||||
const rosidl_dynamic_typesupport_dynamic_data_t &
|
||||
DynamicMessage::get_rosidl_dynamic_data() const
|
||||
{
|
||||
return rosidl_dynamic_data_;
|
||||
}
|
||||
|
||||
|
||||
DynamicSerializationSupport::SharedPtr
|
||||
DynamicMessage::get_shared_dynamic_serialization_support()
|
||||
{
|
||||
return serialization_support_;
|
||||
}
|
||||
|
||||
|
||||
DynamicSerializationSupport::ConstSharedPtr
|
||||
DynamicMessage::get_shared_dynamic_serialization_support() const
|
||||
{
|
||||
return serialization_support_;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
DynamicMessage::get_item_count() const
|
||||
{
|
||||
size_t item_count;
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_get_item_count(
|
||||
&get_rosidl_dynamic_data(), &item_count);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error("could not get item count of dynamic data");
|
||||
}
|
||||
return item_count;
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicMessage::get_member_id(size_t index) const
|
||||
{
|
||||
rosidl_dynamic_typesupport_member_id_t member_id;
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_get_member_id_at_index(
|
||||
&get_rosidl_dynamic_data(), index, &member_id);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error("could not member id of dynamic data element by index");
|
||||
}
|
||||
return member_id;
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicMessage::get_member_id(const std::string & name) const
|
||||
{
|
||||
rosidl_dynamic_typesupport_member_id_t member_id;
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_get_member_id_by_name(
|
||||
&get_rosidl_dynamic_data(), name.c_str(), name.size(), &member_id);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error("could not member id of dynamic data element by name");
|
||||
}
|
||||
return member_id;
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicMessage::get_array_index(size_t index) const
|
||||
{
|
||||
rosidl_dynamic_typesupport_member_id_t array_index;
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_get_array_index(
|
||||
&get_rosidl_dynamic_data(), index, &array_index);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error("could not array index of dynamic data element by index");
|
||||
}
|
||||
return array_index;
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicMessage::get_array_index(const std::string & name) const
|
||||
{
|
||||
return get_array_index(get_member_id(name));
|
||||
}
|
||||
|
||||
|
||||
// METHODS =======================================================================================
|
||||
DynamicMessage
|
||||
DynamicMessage::clone(rcl_allocator_t allocator)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_clone(
|
||||
&get_rosidl_dynamic_data(), &allocator, &rosidl_dynamic_data);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("could not clone dynamic data: ") + rcl_get_error_string().str);
|
||||
}
|
||||
return DynamicMessage(get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_data));
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage::SharedPtr
|
||||
DynamicMessage::clone_shared(rcl_allocator_t allocator)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_clone(
|
||||
&get_rosidl_dynamic_data(), &allocator, &rosidl_dynamic_data);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("could not clone dynamic data: ") + rcl_get_error_string().str);
|
||||
}
|
||||
return DynamicMessage::make_shared(
|
||||
get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_data));
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage
|
||||
DynamicMessage::init_from_type(DynamicMessageType & type, rcl_allocator_t allocator) const
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_init_from_dynamic_type(
|
||||
&type.get_rosidl_dynamic_type(), &allocator, &rosidl_dynamic_data);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error("could not init new dynamic data object from dynamic type");
|
||||
}
|
||||
return DynamicMessage(serialization_support_, std::move(rosidl_dynamic_data));
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage::SharedPtr
|
||||
DynamicMessage::init_from_type_shared(DynamicMessageType & type, rcl_allocator_t allocator) const
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_init_from_dynamic_type(
|
||||
&type.get_rosidl_dynamic_type(), &allocator, &rosidl_dynamic_data);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error("could not init new dynamic data object from dynamic type");
|
||||
}
|
||||
return DynamicMessage::make_shared(serialization_support_, std::move(rosidl_dynamic_data));
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DynamicMessage::equals(const DynamicMessage & other) const
|
||||
{
|
||||
if (get_serialization_library_identifier() != other.get_serialization_library_identifier()) {
|
||||
throw std::runtime_error("library identifiers don't match");
|
||||
}
|
||||
bool equals;
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_equals(
|
||||
&get_rosidl_dynamic_data(), &other.get_rosidl_dynamic_data(), &equals);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error("could not equate dynamic messages");
|
||||
}
|
||||
return equals;
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage::SharedPtr
|
||||
DynamicMessage::loan_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id,
|
||||
rcl_allocator_t allocator)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_loan_value(
|
||||
&get_rosidl_dynamic_data(), id, &allocator, &rosidl_dynamic_data);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("could not loan dynamic data: ") + rcl_get_error_string().str);
|
||||
}
|
||||
return DynamicMessage::make_shared(shared_from_this(), std::move(rosidl_dynamic_data));
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage::SharedPtr
|
||||
DynamicMessage::loan_value(const std::string & name, rcl_allocator_t allocator)
|
||||
{
|
||||
return loan_value(get_member_id(name), allocator);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::clear_all_values()
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_clear_all_values(&get_rosidl_dynamic_data());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::clear_nonkey_values()
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_clear_nonkey_values(&get_rosidl_dynamic_data());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::clear_value(rosidl_dynamic_typesupport_member_id_t id)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_clear_value(&get_rosidl_dynamic_data(), id);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::clear_value(const std::string & name)
|
||||
{
|
||||
clear_value(get_member_id(name));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::clear_sequence()
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_clear_sequence_data(&get_rosidl_dynamic_data());
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicMessage::insert_sequence_data()
|
||||
{
|
||||
rosidl_dynamic_typesupport_member_id_t out;
|
||||
rosidl_dynamic_typesupport_dynamic_data_insert_sequence_data(&get_rosidl_dynamic_data(), &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::remove_sequence_data(rosidl_dynamic_typesupport_member_id_t index)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_remove_sequence_data(
|
||||
&get_rosidl_dynamic_data(), index);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DynamicMessage::serialize(rcl_serialized_message_t & buffer)
|
||||
{
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_serialize(
|
||||
&get_rosidl_dynamic_data(), &buffer);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("could serialize loan dynamic data: ") + rcl_get_error_string().str);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DynamicMessage::deserialize(rcl_serialized_message_t & buffer)
|
||||
{
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_deserialize(
|
||||
&get_rosidl_dynamic_data(), &buffer);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("could deserialize loan dynamic data: ") + rcl_get_error_string().str);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// MEMBER ACCESS ===================================================================================
|
||||
// Defined in "detail/dynamic_message_impl.hpp"
|
||||
|
||||
|
||||
// FIXED STRING MEMBER ACCESS ======================================================================
|
||||
const std::string
|
||||
DynamicMessage::get_fixed_string_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, size_t string_length)
|
||||
{
|
||||
size_t buf_length;
|
||||
char * buf = nullptr;
|
||||
rosidl_dynamic_typesupport_dynamic_data_get_fixed_string_value(
|
||||
&get_rosidl_dynamic_data(), id, &buf, &buf_length, string_length);
|
||||
auto out = std::string(buf, buf_length);
|
||||
delete buf;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
const std::string
|
||||
DynamicMessage::get_fixed_string_value(const std::string & name, size_t string_length)
|
||||
{
|
||||
return get_fixed_string_value(get_member_id(name), string_length);
|
||||
}
|
||||
|
||||
|
||||
const std::u16string
|
||||
DynamicMessage::get_fixed_wstring_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, size_t wstring_length)
|
||||
{
|
||||
size_t buf_length;
|
||||
char16_t * buf = nullptr;
|
||||
rosidl_dynamic_typesupport_dynamic_data_get_fixed_wstring_value(
|
||||
&get_rosidl_dynamic_data(), id, &buf, &buf_length, wstring_length);
|
||||
auto out = std::u16string(buf, buf_length);
|
||||
delete buf;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
const std::u16string
|
||||
DynamicMessage::get_fixed_wstring_value(const std::string & name, size_t wstring_length)
|
||||
{
|
||||
return get_fixed_wstring_value(get_member_id(name), wstring_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::set_fixed_string_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string value, size_t string_length)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_set_fixed_string_value(
|
||||
&get_rosidl_dynamic_data(), id, value.c_str(), value.size(), string_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::set_fixed_string_value(
|
||||
const std::string & name, const std::string value, size_t string_length)
|
||||
{
|
||||
set_fixed_string_value(get_member_id(name), value, string_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::set_fixed_wstring_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::u16string value, size_t wstring_length)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_set_fixed_wstring_value(
|
||||
&get_rosidl_dynamic_data(), id, value.c_str(), value.size(), wstring_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::set_fixed_wstring_value(
|
||||
const std::string & name, const std::u16string value, size_t wstring_length)
|
||||
{
|
||||
set_fixed_wstring_value(get_member_id(name), value, wstring_length);
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicMessage::insert_fixed_string_value(const std::string value, size_t string_length)
|
||||
{
|
||||
rosidl_dynamic_typesupport_member_id_t out;
|
||||
rosidl_dynamic_typesupport_dynamic_data_insert_fixed_string_value(
|
||||
&get_rosidl_dynamic_data(), value.c_str(), value.size(), string_length, &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicMessage::insert_fixed_wstring_value(const std::u16string value, size_t wstring_length)
|
||||
{
|
||||
rosidl_dynamic_typesupport_member_id_t out;
|
||||
rosidl_dynamic_typesupport_dynamic_data_insert_fixed_wstring_value(
|
||||
&get_rosidl_dynamic_data(), value.c_str(), value.size(), wstring_length, &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// BOUNDED STRING MEMBER ACCESS ====================================================================
|
||||
const std::string
|
||||
DynamicMessage::get_bounded_string_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, size_t string_bound)
|
||||
{
|
||||
size_t buf_length;
|
||||
char * buf = nullptr;
|
||||
rosidl_dynamic_typesupport_dynamic_data_get_bounded_string_value(
|
||||
&get_rosidl_dynamic_data(), id, &buf, &buf_length, string_bound);
|
||||
auto out = std::string(buf, buf_length);
|
||||
delete buf;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
const std::string
|
||||
DynamicMessage::get_bounded_string_value(const std::string & name, size_t string_bound)
|
||||
{
|
||||
return get_bounded_string_value(get_member_id(name), string_bound);
|
||||
}
|
||||
|
||||
|
||||
const std::u16string
|
||||
DynamicMessage::get_bounded_wstring_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, size_t wstring_bound)
|
||||
{
|
||||
size_t buf_length;
|
||||
char16_t * buf = nullptr;
|
||||
rosidl_dynamic_typesupport_dynamic_data_get_bounded_wstring_value(
|
||||
&get_rosidl_dynamic_data(), id, &buf, &buf_length, wstring_bound);
|
||||
auto out = std::u16string(buf, buf_length);
|
||||
delete buf;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
const std::u16string
|
||||
DynamicMessage::get_bounded_wstring_value(const std::string & name, size_t wstring_bound)
|
||||
{
|
||||
return get_bounded_wstring_value(get_member_id(name), wstring_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::set_bounded_string_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string value, size_t string_bound)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_set_bounded_string_value(
|
||||
&get_rosidl_dynamic_data(), id, value.c_str(), value.size(), string_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::set_bounded_string_value(
|
||||
const std::string & name, const std::string value, size_t string_bound)
|
||||
{
|
||||
set_bounded_string_value(get_member_id(name), value, string_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::set_bounded_wstring_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::u16string value, size_t wstring_bound)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_set_bounded_wstring_value(
|
||||
&get_rosidl_dynamic_data(), id, value.c_str(), value.size(), wstring_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::set_bounded_wstring_value(
|
||||
const std::string & name, const std::u16string value, size_t wstring_bound)
|
||||
{
|
||||
set_bounded_wstring_value(get_member_id(name), value, wstring_bound);
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicMessage::insert_bounded_string_value(const std::string value, size_t string_bound)
|
||||
{
|
||||
rosidl_dynamic_typesupport_member_id_t out;
|
||||
rosidl_dynamic_typesupport_dynamic_data_insert_bounded_string_value(
|
||||
&get_rosidl_dynamic_data(), value.c_str(), value.size(), string_bound, &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicMessage::insert_bounded_wstring_value(const std::u16string value, size_t wstring_bound)
|
||||
{
|
||||
rosidl_dynamic_typesupport_member_id_t out;
|
||||
rosidl_dynamic_typesupport_dynamic_data_insert_bounded_wstring_value(
|
||||
&get_rosidl_dynamic_data(), value.c_str(), value.size(), wstring_bound, &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// NESTED MEMBER ACCESS ============================================================================
|
||||
DynamicMessage
|
||||
DynamicMessage::get_complex_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, rcl_allocator_t allocator)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_t out =
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
|
||||
rosidl_dynamic_typesupport_dynamic_data_get_complex_value(
|
||||
&get_rosidl_dynamic_data(), id, &allocator, &out);
|
||||
return DynamicMessage(get_shared_dynamic_serialization_support(), std::move(out));
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage
|
||||
DynamicMessage::get_complex_value(const std::string & name, rcl_allocator_t allocator)
|
||||
{
|
||||
return get_complex_value(get_member_id(name), allocator);
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage::SharedPtr
|
||||
DynamicMessage::get_complex_value_shared(
|
||||
rosidl_dynamic_typesupport_member_id_t id, rcl_allocator_t allocator)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_t out =
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
|
||||
rosidl_dynamic_typesupport_dynamic_data_get_complex_value(
|
||||
&get_rosidl_dynamic_data(), id, &allocator, &out);
|
||||
return DynamicMessage::make_shared(get_shared_dynamic_serialization_support(), std::move(out));
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage::SharedPtr
|
||||
DynamicMessage::get_complex_value_shared(const std::string & name, rcl_allocator_t allocator)
|
||||
{
|
||||
return get_complex_value_shared(get_member_id(name), allocator);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::set_complex_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, DynamicMessage & value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_set_complex_value(
|
||||
&get_rosidl_dynamic_data(), id, &value.get_rosidl_dynamic_data());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessage::set_complex_value(const std::string & name, DynamicMessage & value)
|
||||
{
|
||||
set_complex_value(get_member_id(name), value);
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicMessage::insert_complex_value_copy(const DynamicMessage & value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_member_id_t out;
|
||||
rosidl_dynamic_typesupport_dynamic_data_insert_complex_value_copy(
|
||||
&get_rosidl_dynamic_data(), &value.get_rosidl_dynamic_data(), &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicMessage::insert_complex_value(DynamicMessage & value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_member_id_t out;
|
||||
rosidl_dynamic_typesupport_dynamic_data_insert_complex_value(
|
||||
&get_rosidl_dynamic_data(), &value.get_rosidl_dynamic_data(), &out);
|
||||
return out;
|
||||
}
|
||||
281
rclcpp/src/rclcpp/dynamic_typesupport/dynamic_message_type.cpp
Normal file
281
rclcpp/src/rclcpp/dynamic_typesupport/dynamic_message_type.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
// 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;
|
||||
|
||||
|
||||
// CONSTRUCTION ====================================================================================
|
||||
DynamicMessageType::DynamicMessageType(DynamicMessageTypeBuilder::SharedPtr dynamic_type_builder)
|
||||
: DynamicMessageType::DynamicMessageType(
|
||||
dynamic_type_builder, dynamic_type_builder->get_rosidl_dynamic_type_builder().allocator) {}
|
||||
|
||||
|
||||
DynamicMessageType::DynamicMessageType(
|
||||
DynamicMessageTypeBuilder::SharedPtr dynamic_type_builder,
|
||||
rcl_allocator_t allocator)
|
||||
: serialization_support_(dynamic_type_builder->get_shared_dynamic_serialization_support()),
|
||||
rosidl_dynamic_type_(rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type())
|
||||
{
|
||||
if (!serialization_support_) {
|
||||
throw std::runtime_error("dynamic type could not bind serialization support!");
|
||||
}
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
|
||||
dynamic_type_builder->get_rosidl_dynamic_type_builder();
|
||||
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_init_from_dynamic_type_builder(
|
||||
&rosidl_dynamic_type_builder, &allocator, &get_rosidl_dynamic_type());
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error("could not init new dynamic type object");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageType::DynamicMessageType(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
rosidl_dynamic_typesupport_dynamic_type_t && rosidl_dynamic_type)
|
||||
: serialization_support_(serialization_support),
|
||||
rosidl_dynamic_type_(std::move(rosidl_dynamic_type))
|
||||
{
|
||||
if (serialization_support) {
|
||||
if (!match_serialization_support_(*serialization_support, rosidl_dynamic_type)) {
|
||||
throw std::runtime_error(
|
||||
"serialization support library identifier does not match dynamic type's!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageType::DynamicMessageType(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const rosidl_runtime_c__type_description__TypeDescription & description,
|
||||
rcl_allocator_t allocator)
|
||||
: serialization_support_(serialization_support),
|
||||
rosidl_dynamic_type_(rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type())
|
||||
{
|
||||
init_from_description(description, allocator, serialization_support);
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageType::DynamicMessageType(DynamicMessageType && other) noexcept
|
||||
: serialization_support_(std::exchange(other.serialization_support_, nullptr)),
|
||||
rosidl_dynamic_type_(std::exchange(
|
||||
other.rosidl_dynamic_type_, rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type()))
|
||||
{}
|
||||
|
||||
|
||||
DynamicMessageType &
|
||||
DynamicMessageType::operator=(DynamicMessageType && other) noexcept
|
||||
{
|
||||
std::swap(serialization_support_, other.serialization_support_);
|
||||
std::swap(rosidl_dynamic_type_, other.rosidl_dynamic_type_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageType::~DynamicMessageType()
|
||||
{
|
||||
if (rosidl_dynamic_typesupport_dynamic_type_fini(&get_rosidl_dynamic_type()) != RCUTILS_RET_OK) {
|
||||
RCUTILS_LOG_ERROR("could not fini rosidl dynamic type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageType::init_from_description(
|
||||
const rosidl_runtime_c__type_description__TypeDescription & description,
|
||||
rcl_allocator_t allocator,
|
||||
DynamicSerializationSupport::SharedPtr serialization_support)
|
||||
{
|
||||
if (serialization_support) {
|
||||
// Swap serialization support if serialization support is given
|
||||
serialization_support_ = serialization_support;
|
||||
}
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_type_t rosidl_dynamic_type =
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type();
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_init_from_description(
|
||||
&serialization_support_->get_rosidl_serialization_support(),
|
||||
&description,
|
||||
&allocator,
|
||||
&rosidl_dynamic_type);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error("could not init new dynamic type object");
|
||||
}
|
||||
|
||||
rosidl_dynamic_type_ = std::move(rosidl_dynamic_type);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DynamicMessageType::match_serialization_support_(
|
||||
const DynamicSerializationSupport & serialization_support,
|
||||
const rosidl_dynamic_typesupport_dynamic_type_t & rosidl_dynamic_type)
|
||||
{
|
||||
if (serialization_support.get_serialization_library_identifier() != std::string(
|
||||
rosidl_dynamic_type.serialization_support->serialization_library_identifier))
|
||||
{
|
||||
RCUTILS_LOG_ERROR(
|
||||
"serialization support library identifier does not match dynamic type's (%s vs %s)",
|
||||
serialization_support.get_serialization_library_identifier().c_str(),
|
||||
rosidl_dynamic_type.serialization_support->serialization_library_identifier);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// GETTERS =========================================================================================
|
||||
const std::string
|
||||
DynamicMessageType::get_serialization_library_identifier() const
|
||||
{
|
||||
return std::string(
|
||||
get_rosidl_dynamic_type().serialization_support->serialization_library_identifier);
|
||||
}
|
||||
|
||||
|
||||
const std::string
|
||||
DynamicMessageType::get_name() const
|
||||
{
|
||||
size_t buf_length;
|
||||
const char * buf;
|
||||
rosidl_dynamic_typesupport_dynamic_type_get_name(&get_rosidl_dynamic_type(), &buf, &buf_length);
|
||||
return std::string(buf, buf_length);
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
DynamicMessageType::get_member_count() const
|
||||
{
|
||||
size_t out;
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_get_member_count(
|
||||
&get_rosidl_dynamic_type(), &out);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("could not get member count: ") + rcl_get_error_string().str);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_type_t &
|
||||
DynamicMessageType::get_rosidl_dynamic_type()
|
||||
{
|
||||
return rosidl_dynamic_type_;
|
||||
}
|
||||
|
||||
|
||||
const rosidl_dynamic_typesupport_dynamic_type_t &
|
||||
DynamicMessageType::get_rosidl_dynamic_type() const
|
||||
{
|
||||
return rosidl_dynamic_type_;
|
||||
}
|
||||
|
||||
|
||||
DynamicSerializationSupport::SharedPtr
|
||||
DynamicMessageType::get_shared_dynamic_serialization_support()
|
||||
{
|
||||
return serialization_support_;
|
||||
}
|
||||
|
||||
|
||||
DynamicSerializationSupport::ConstSharedPtr
|
||||
DynamicMessageType::get_shared_dynamic_serialization_support() const
|
||||
{
|
||||
return serialization_support_;
|
||||
}
|
||||
|
||||
|
||||
// METHODS =========================================================================================
|
||||
DynamicMessageType
|
||||
DynamicMessageType::clone(rcl_allocator_t allocator)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_t rosidl_dynamic_type =
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type();
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_clone(
|
||||
&get_rosidl_dynamic_type(), &allocator, &rosidl_dynamic_type);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("could not clone dynamic type: ") + rcl_get_error_string().str);
|
||||
}
|
||||
return DynamicMessageType(
|
||||
get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_type));
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageType::SharedPtr
|
||||
DynamicMessageType::clone_shared(rcl_allocator_t allocator)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_t rosidl_dynamic_type =
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type();
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_clone(
|
||||
&get_rosidl_dynamic_type(), &allocator, &rosidl_dynamic_type);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("could not clone dynamic type: ") + rcl_get_error_string().str);
|
||||
}
|
||||
return DynamicMessageType::make_shared(
|
||||
get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_type));
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DynamicMessageType::equals(const DynamicMessageType & other) const
|
||||
{
|
||||
if (get_serialization_library_identifier() != other.get_serialization_library_identifier()) {
|
||||
throw std::runtime_error("library identifiers don't match");
|
||||
}
|
||||
bool out;
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_equals(
|
||||
&get_rosidl_dynamic_type(), &other.get_rosidl_dynamic_type(), &out);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("could not equate dynamic message types: ") + rcl_get_error_string().str);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage
|
||||
DynamicMessageType::build_dynamic_message(rcl_allocator_t allocator)
|
||||
{
|
||||
return DynamicMessage(shared_from_this(), allocator);
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage::SharedPtr
|
||||
DynamicMessageType::build_dynamic_message_shared(rcl_allocator_t allocator)
|
||||
{
|
||||
return DynamicMessage::make_shared(shared_from_this(), allocator);
|
||||
}
|
||||
@@ -0,0 +1,614 @@
|
||||
// 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;
|
||||
|
||||
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_TYPE_BUILDER_IMPL_HPP_
|
||||
// Template specialization implementations
|
||||
#include "rclcpp/dynamic_typesupport/detail/dynamic_message_type_builder_impl.hpp"
|
||||
#endif
|
||||
|
||||
// CONSTRUCTION ====================================================================================
|
||||
DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support, const std::string & name)
|
||||
: DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(
|
||||
serialization_support,
|
||||
name,
|
||||
serialization_support->get_rosidl_serialization_support().allocator) {}
|
||||
|
||||
|
||||
DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const std::string & name,
|
||||
rcl_allocator_t allocator)
|
||||
: serialization_support_(serialization_support),
|
||||
rosidl_dynamic_type_builder_(
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder())
|
||||
{
|
||||
init_from_serialization_support_(serialization_support, name, allocator);
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_t && rosidl_dynamic_type_builder)
|
||||
: serialization_support_(serialization_support),
|
||||
rosidl_dynamic_type_builder_(
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder())
|
||||
{
|
||||
if (!serialization_support) {
|
||||
throw std::runtime_error("serialization support cannot be nullptr!");
|
||||
}
|
||||
if (!match_serialization_support_(*serialization_support, rosidl_dynamic_type_builder)) {
|
||||
throw std::runtime_error(
|
||||
"serialization support library does not match dynamic type builder's!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const rosidl_runtime_c__type_description__TypeDescription & description,
|
||||
rcl_allocator_t allocator)
|
||||
: serialization_support_(serialization_support),
|
||||
rosidl_dynamic_type_builder_(
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder())
|
||||
{
|
||||
if (!serialization_support) {
|
||||
throw std::runtime_error("serialization support cannot be nullptr!");
|
||||
}
|
||||
init_from_description(description, allocator, serialization_support);
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(DynamicMessageTypeBuilder && other) noexcept
|
||||
: serialization_support_(std::exchange(other.serialization_support_, nullptr)),
|
||||
rosidl_dynamic_type_builder_(std::exchange(
|
||||
other.rosidl_dynamic_type_builder_,
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder())) {}
|
||||
|
||||
|
||||
DynamicMessageTypeBuilder &
|
||||
DynamicMessageTypeBuilder::operator=(DynamicMessageTypeBuilder && other) noexcept
|
||||
{
|
||||
std::swap(serialization_support_, other.serialization_support_);
|
||||
std::swap(rosidl_dynamic_type_builder_, other.rosidl_dynamic_type_builder_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageTypeBuilder::~DynamicMessageTypeBuilder()
|
||||
{
|
||||
if (rosidl_dynamic_typesupport_dynamic_type_builder_fini(&get_rosidl_dynamic_type_builder()) !=
|
||||
RCUTILS_RET_OK)
|
||||
{
|
||||
RCUTILS_LOG_ERROR("could not fini rosidl dynamic type builder");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::init_from_description(
|
||||
const rosidl_runtime_c__type_description__TypeDescription & description,
|
||||
rcl_allocator_t allocator,
|
||||
DynamicSerializationSupport::SharedPtr serialization_support)
|
||||
{
|
||||
if (serialization_support) {
|
||||
// Swap serialization support if serialization support is given
|
||||
serialization_support_ = serialization_support;
|
||||
}
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder();
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_builder_init_from_description(
|
||||
&serialization_support_->get_rosidl_serialization_support(),
|
||||
&description,
|
||||
&allocator,
|
||||
&rosidl_dynamic_type_builder);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error("could not init new dynamic type builder object");
|
||||
}
|
||||
|
||||
rosidl_dynamic_type_builder_ = std::move(rosidl_dynamic_type_builder);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::init_from_serialization_support_(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const std::string & name,
|
||||
rcl_allocator_t allocator)
|
||||
{
|
||||
if (!serialization_support) {
|
||||
throw std::runtime_error("serialization support cannot be nullptr!");
|
||||
}
|
||||
if (!&serialization_support->get_rosidl_serialization_support()) {
|
||||
throw std::runtime_error("serialization support raw pointer cannot be nullptr!");
|
||||
}
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder();
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_builder_init(
|
||||
&serialization_support->get_rosidl_serialization_support(),
|
||||
name.c_str(), name.size(),
|
||||
&allocator,
|
||||
&rosidl_dynamic_type_builder);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("could not init dynamic type builder: ") + rcl_get_error_string().str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DynamicMessageTypeBuilder::match_serialization_support_(
|
||||
const DynamicSerializationSupport & serialization_support,
|
||||
const rosidl_dynamic_typesupport_dynamic_type_builder_t & rosidl_dynamic_type_builder)
|
||||
{
|
||||
if (serialization_support.get_serialization_library_identifier() != std::string(
|
||||
rosidl_dynamic_type_builder.serialization_support->serialization_library_identifier))
|
||||
{
|
||||
RCUTILS_LOG_ERROR(
|
||||
"serialization support library identifier does not match dynamic type builder's");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// GETTERS =======================================================================================
|
||||
const std::string
|
||||
DynamicMessageTypeBuilder::get_serialization_library_identifier() const
|
||||
{
|
||||
return std::string(
|
||||
get_rosidl_dynamic_type_builder().serialization_support->serialization_library_identifier);
|
||||
}
|
||||
|
||||
|
||||
const std::string
|
||||
DynamicMessageTypeBuilder::get_name() const
|
||||
{
|
||||
size_t buf_length;
|
||||
const char * buf;
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_get_name(
|
||||
&get_rosidl_dynamic_type_builder(), &buf, &buf_length);
|
||||
return std::string(buf, buf_length);
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_t &
|
||||
DynamicMessageTypeBuilder::get_rosidl_dynamic_type_builder()
|
||||
{
|
||||
return rosidl_dynamic_type_builder_;
|
||||
}
|
||||
|
||||
|
||||
const rosidl_dynamic_typesupport_dynamic_type_builder_t &
|
||||
DynamicMessageTypeBuilder::get_rosidl_dynamic_type_builder() const
|
||||
{
|
||||
return rosidl_dynamic_type_builder_;
|
||||
}
|
||||
|
||||
|
||||
DynamicSerializationSupport::SharedPtr
|
||||
DynamicMessageTypeBuilder::get_shared_dynamic_serialization_support()
|
||||
{
|
||||
return serialization_support_;
|
||||
}
|
||||
|
||||
|
||||
DynamicSerializationSupport::ConstSharedPtr
|
||||
DynamicMessageTypeBuilder::get_shared_dynamic_serialization_support() const
|
||||
{
|
||||
return serialization_support_;
|
||||
}
|
||||
|
||||
|
||||
// METHODS =======================================================================================
|
||||
void
|
||||
DynamicMessageTypeBuilder::set_name(const std::string & name)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_set_name(
|
||||
&get_rosidl_dynamic_type_builder(), name.c_str(), name.size());
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageTypeBuilder
|
||||
DynamicMessageTypeBuilder::clone(rcl_allocator_t allocator)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder();
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_builder_clone(
|
||||
&get_rosidl_dynamic_type_builder(), &allocator, &rosidl_dynamic_type_builder);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("could not clone dynamic type builder: ") + rcl_get_error_string().str);
|
||||
}
|
||||
return DynamicMessageTypeBuilder(
|
||||
get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_type_builder));
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageTypeBuilder::SharedPtr
|
||||
DynamicMessageTypeBuilder::clone_shared(rcl_allocator_t allocator)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder();
|
||||
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_builder_clone(
|
||||
&get_rosidl_dynamic_type_builder(), &allocator, &rosidl_dynamic_type_builder);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("could not clone dynamic type builder: ") + rcl_get_error_string().str);
|
||||
}
|
||||
return DynamicMessageTypeBuilder::make_shared(
|
||||
get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_type_builder));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::clear(rcl_allocator_t allocator)
|
||||
{
|
||||
if (!serialization_support_) {
|
||||
throw std::runtime_error(
|
||||
"cannot call clear() on a dynamic type builder with uninitialized serialization support"
|
||||
);
|
||||
}
|
||||
|
||||
const std::string & name = get_name();
|
||||
init_from_serialization_support_(serialization_support_, name, allocator);
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage
|
||||
DynamicMessageTypeBuilder::build_dynamic_message(rcl_allocator_t allocator)
|
||||
{
|
||||
return DynamicMessage(shared_from_this(), allocator);
|
||||
}
|
||||
|
||||
|
||||
DynamicMessage::SharedPtr
|
||||
DynamicMessageTypeBuilder::build_dynamic_message_shared(rcl_allocator_t allocator)
|
||||
{
|
||||
return DynamicMessage::make_shared(shared_from_this(), allocator);
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageType
|
||||
DynamicMessageTypeBuilder::build_dynamic_message_type(rcl_allocator_t allocator)
|
||||
{
|
||||
return DynamicMessageType(shared_from_this(), allocator);
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageType::SharedPtr
|
||||
DynamicMessageTypeBuilder::build_dynamic_message_type_shared(rcl_allocator_t allocator)
|
||||
{
|
||||
return DynamicMessageType::make_shared(shared_from_this(), allocator);
|
||||
}
|
||||
|
||||
|
||||
// ADD MEMBERS =====================================================================================
|
||||
// Defined in "detail/dynamic_message_type_builder_impl.hpp"
|
||||
|
||||
|
||||
// ADD FIXED STRING MEMBERS ========================================================================
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_fixed_string_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_length,
|
||||
const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_string_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
string_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_fixed_wstring_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_length,
|
||||
const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_wstring_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
wstring_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_fixed_string_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t string_length, size_t array_length, const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_string_array_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
string_length, array_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_fixed_wstring_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t wstring_length, size_t array_length, const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_wstring_array_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
wstring_length, array_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_fixed_string_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_length,
|
||||
const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_string_unbounded_sequence_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
string_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_fixed_wstring_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_length,
|
||||
const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_wstring_unbounded_sequence_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
wstring_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_fixed_string_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t string_length, size_t sequence_bound, const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_string_bounded_sequence_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
string_length, sequence_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_fixed_wstring_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t wstring_length, size_t sequence_bound, const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_wstring_bounded_sequence_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
wstring_length, sequence_bound);
|
||||
}
|
||||
|
||||
|
||||
// ADD BOUNDED STRING MEMBERS ======================================================================
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_bounded_string_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_bound,
|
||||
const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
string_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_bounded_wstring_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound,
|
||||
const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
wstring_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_bounded_string_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t string_bound, size_t array_length, const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_array_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
string_bound, array_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_bounded_wstring_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t wstring_bound, size_t array_length, const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_array_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
wstring_bound, array_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_bounded_string_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_bound,
|
||||
const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_unbounded_sequence_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
string_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_bounded_wstring_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound,
|
||||
const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_unbounded_sequence_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
wstring_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_bounded_string_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t string_bound, size_t sequence_bound, const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_bounded_sequence_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
string_bound, sequence_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_bounded_wstring_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t wstring_bound, size_t sequence_bound, const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_bounded_sequence_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
wstring_bound, sequence_bound);
|
||||
}
|
||||
|
||||
|
||||
// ADD NESTED MEMBERS ==============================================================================
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_complex_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageType & nested_type, const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
&nested_type.get_rosidl_dynamic_type());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_complex_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageType & nested_type, size_t array_length, const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_array_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
&nested_type.get_rosidl_dynamic_type(), array_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_complex_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageType & nested_type, const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_unbounded_sequence_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
&nested_type.get_rosidl_dynamic_type());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_complex_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageType & nested_type, size_t sequence_bound, const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_bounded_sequence_member(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
&nested_type.get_rosidl_dynamic_type(), sequence_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_complex_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageTypeBuilder & nested_type_builder, const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_member_builder(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
&nested_type_builder.get_rosidl_dynamic_type_builder());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_complex_array_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageTypeBuilder & nested_type_builder, size_t array_length,
|
||||
const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_array_member_builder(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
&nested_type_builder.get_rosidl_dynamic_type_builder(), array_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_complex_unbounded_sequence_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageTypeBuilder & nested_type_builder, const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_unbounded_sequence_member_builder(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
&nested_type_builder.get_rosidl_dynamic_type_builder());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeBuilder::add_complex_bounded_sequence_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicMessageTypeBuilder & nested_type_builder, size_t sequence_bound,
|
||||
const std::string & default_value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_bounded_sequence_member_builder(
|
||||
&get_rosidl_dynamic_type_builder(),
|
||||
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
|
||||
&nested_type_builder.get_rosidl_dynamic_type_builder(), sequence_bound);
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
// 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;
|
||||
|
||||
// CONSTRUCTION ====================================================================================
|
||||
DynamicMessageTypeSupport::DynamicMessageTypeSupport(
|
||||
const rosidl_runtime_c__type_description__TypeDescription & description,
|
||||
const std::string & serialization_library_name,
|
||||
rcl_allocator_t allocator)
|
||||
: serialization_support_(nullptr),
|
||||
dynamic_message_type_(nullptr),
|
||||
dynamic_message_(nullptr),
|
||||
rosidl_message_type_support_(rosidl_get_zero_initialized_message_type_support_handle())
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
if (serialization_library_name.empty()) {
|
||||
ret = rcl_dynamic_message_type_support_handle_init(
|
||||
nullptr, &description, &allocator, &rosidl_message_type_support_);
|
||||
} else {
|
||||
ret = rcl_dynamic_message_type_support_handle_init(
|
||||
serialization_library_name.c_str(), &description, &allocator, &rosidl_message_type_support_);
|
||||
}
|
||||
if (ret != RCL_RET_OK) {
|
||||
std::string error_msg =
|
||||
std::string("error initializing rosidl message type support: ") + rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
throw std::runtime_error(error_msg);
|
||||
}
|
||||
if (rosidl_message_type_support_.typesupport_identifier !=
|
||||
rosidl_get_dynamic_typesupport_identifier())
|
||||
{
|
||||
throw std::runtime_error("rosidl message type support is of the wrong type");
|
||||
}
|
||||
|
||||
auto ts_impl = static_cast<rosidl_dynamic_message_type_support_impl_t *>(const_cast<void *>(
|
||||
rosidl_message_type_support_.data));
|
||||
|
||||
serialization_support_ = DynamicSerializationSupport::make_shared(
|
||||
std::move(ts_impl->serialization_support));
|
||||
|
||||
dynamic_message_type_ = DynamicMessageType::make_shared(
|
||||
get_shared_dynamic_serialization_support(), std::move(*ts_impl->dynamic_message_type));
|
||||
|
||||
dynamic_message_ = DynamicMessage::make_shared(
|
||||
get_shared_dynamic_serialization_support(), std::move(*ts_impl->dynamic_message));
|
||||
}
|
||||
|
||||
DynamicMessageTypeSupport::DynamicMessageTypeSupport(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const rosidl_runtime_c__type_description__TypeDescription & description,
|
||||
rcl_allocator_t allocator)
|
||||
: serialization_support_(serialization_support),
|
||||
dynamic_message_type_(nullptr),
|
||||
dynamic_message_(nullptr),
|
||||
rosidl_message_type_support_(rosidl_get_zero_initialized_message_type_support_handle())
|
||||
{
|
||||
// Check null
|
||||
if (!serialization_support) {
|
||||
throw std::runtime_error("serialization_support cannot be nullptr.");
|
||||
}
|
||||
|
||||
rosidl_type_hash_t type_hash;
|
||||
rcutils_ret_t hash_ret = rcl_calculate_type_hash(
|
||||
// TODO(methylDragon): Swap this out with the conversion function when it is ready
|
||||
reinterpret_cast<const type_description_interfaces__msg__TypeDescription *>(&description),
|
||||
&type_hash);
|
||||
if (hash_ret != RCL_RET_OK) {
|
||||
throw std::runtime_error("failed to get type hash");
|
||||
}
|
||||
|
||||
rcutils_ret_t ret = rosidl_dynamic_message_type_support_handle_init(
|
||||
&serialization_support->get_rosidl_serialization_support(),
|
||||
&type_hash, // type_hash
|
||||
&description, // type_description
|
||||
nullptr, // type_description_sources (not implemented for dynamic types)
|
||||
&allocator,
|
||||
&rosidl_message_type_support_);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
throw std::runtime_error("could not init rosidl message type support");
|
||||
}
|
||||
if (rosidl_message_type_support_.typesupport_identifier !=
|
||||
rosidl_get_dynamic_typesupport_identifier())
|
||||
{
|
||||
throw std::runtime_error("rosidl message type support is of the wrong type");
|
||||
}
|
||||
|
||||
auto ts_impl = static_cast<const rosidl_dynamic_message_type_support_impl_t *>(
|
||||
rosidl_message_type_support_.data);
|
||||
|
||||
dynamic_message_type_ = DynamicMessageType::make_shared(
|
||||
get_shared_dynamic_serialization_support(), std::move(*ts_impl->dynamic_message_type));
|
||||
|
||||
dynamic_message_ = DynamicMessage::make_shared(
|
||||
get_shared_dynamic_serialization_support(), std::move(*ts_impl->dynamic_message));
|
||||
}
|
||||
|
||||
DynamicMessageTypeSupport::DynamicMessageTypeSupport(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
DynamicMessageType::SharedPtr dynamic_message_type,
|
||||
DynamicMessage::SharedPtr dynamic_message,
|
||||
const rosidl_runtime_c__type_description__TypeDescription & description,
|
||||
rcl_allocator_t allocator)
|
||||
: serialization_support_(serialization_support),
|
||||
dynamic_message_type_(dynamic_message_type),
|
||||
dynamic_message_(dynamic_message),
|
||||
rosidl_message_type_support_(rosidl_get_zero_initialized_message_type_support_handle())
|
||||
{
|
||||
// Check null
|
||||
if (!serialization_support) {
|
||||
throw std::runtime_error("serialization_support cannot be nullptr.");
|
||||
}
|
||||
if (!dynamic_message_type) {
|
||||
throw std::runtime_error("dynamic_message_type cannot be nullptr.");
|
||||
}
|
||||
if (!dynamic_message) {
|
||||
throw std::runtime_error("dynamic_message cannot be nullptr.");
|
||||
}
|
||||
|
||||
// Check identifiers
|
||||
if (serialization_support->get_serialization_library_identifier() !=
|
||||
dynamic_message_type->get_serialization_library_identifier())
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"serialization support library identifier does not match "
|
||||
"dynamic message type library identifier.");
|
||||
}
|
||||
if (dynamic_message_type->get_serialization_library_identifier() !=
|
||||
dynamic_message->get_serialization_library_identifier())
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"dynamic message type library identifier does not match "
|
||||
"dynamic message library identifier.");
|
||||
}
|
||||
|
||||
rosidl_type_hash_t type_hash;
|
||||
rcutils_ret_t hash_ret = rcl_calculate_type_hash(
|
||||
// TODO(methylDragon): Swap this out with the conversion function when it is ready
|
||||
// from https://github.com/ros2/rcl/pull/1052
|
||||
reinterpret_cast<const type_description_interfaces__msg__TypeDescription *>(&description),
|
||||
&type_hash);
|
||||
if (hash_ret != RCL_RET_OK) {
|
||||
std::string error_msg = std::string("failed to get type hash: ") + rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
throw std::runtime_error(error_msg);
|
||||
}
|
||||
|
||||
auto ts_impl = static_cast<rosidl_dynamic_message_type_support_impl_t *>(
|
||||
allocator.zero_allocate(1, sizeof(rosidl_dynamic_message_type_support_impl_t), allocator.state)
|
||||
);
|
||||
if (!ts_impl) {
|
||||
throw std::runtime_error("could not allocate rosidl_message_type_support_t");
|
||||
}
|
||||
|
||||
ts_impl->allocator = allocator;
|
||||
ts_impl->type_hash = type_hash;
|
||||
if (!rosidl_runtime_c__type_description__TypeDescription__copy(
|
||||
&description, &ts_impl->type_description))
|
||||
{
|
||||
throw std::runtime_error("could not copy type description");
|
||||
}
|
||||
// ts_impl->type_description_sources = // Not used
|
||||
|
||||
ts_impl->serialization_support = serialization_support->get_rosidl_serialization_support();
|
||||
ts_impl->dynamic_message_type = &dynamic_message_type->get_rosidl_dynamic_type();
|
||||
ts_impl->dynamic_message = &dynamic_message->get_rosidl_dynamic_data();
|
||||
|
||||
rosidl_message_type_support_ = {
|
||||
rosidl_get_dynamic_typesupport_identifier(), // typesupport_identifier
|
||||
ts_impl, // data
|
||||
get_message_typesupport_handle_function, // func
|
||||
// get_type_hash_func
|
||||
rosidl_get_dynamic_message_type_support_type_hash_function,
|
||||
// get_type_description_func
|
||||
rosidl_get_dynamic_message_type_support_type_description_function,
|
||||
// get_type_description_sources_func
|
||||
rosidl_get_dynamic_message_type_support_type_description_sources_function
|
||||
};
|
||||
}
|
||||
|
||||
DynamicMessageTypeSupport::~DynamicMessageTypeSupport()
|
||||
{
|
||||
// These must go first
|
||||
serialization_support_.reset();
|
||||
dynamic_message_type_.reset();
|
||||
dynamic_message_.reset();
|
||||
|
||||
// Early return if type support isn't populated to avoid segfaults
|
||||
if (!rosidl_message_type_support_.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We only partially finalize the rosidl_message_type_support->data since its pointer members are
|
||||
// managed by their respective SharedPtr wrapper classes.
|
||||
auto ts_impl = static_cast<rosidl_dynamic_message_type_support_impl_t *>(
|
||||
const_cast<void *>(rosidl_message_type_support_.data)
|
||||
);
|
||||
rcutils_allocator_t allocator = ts_impl->allocator;
|
||||
|
||||
rosidl_runtime_c__type_description__TypeDescription__fini(&ts_impl->type_description);
|
||||
rosidl_runtime_c__type_description__TypeSource__Sequence__fini(
|
||||
&ts_impl->type_description_sources);
|
||||
allocator.deallocate(static_cast<void *>(ts_impl), allocator.state); // Always C allocated
|
||||
}
|
||||
|
||||
|
||||
// GETTERS =========================================================================================
|
||||
const std::string
|
||||
DynamicMessageTypeSupport::get_serialization_library_identifier() const
|
||||
{
|
||||
return serialization_support_->get_serialization_library_identifier();
|
||||
}
|
||||
|
||||
rosidl_message_type_support_t &
|
||||
DynamicMessageTypeSupport::get_rosidl_message_type_support()
|
||||
{
|
||||
return rosidl_message_type_support_;
|
||||
}
|
||||
|
||||
const rosidl_message_type_support_t &
|
||||
DynamicMessageTypeSupport::get_const_rosidl_message_type_support()
|
||||
{
|
||||
return rosidl_message_type_support_;
|
||||
}
|
||||
|
||||
const rosidl_message_type_support_t &
|
||||
DynamicMessageTypeSupport::get_const_rosidl_message_type_support() const
|
||||
{
|
||||
return rosidl_message_type_support_;
|
||||
}
|
||||
|
||||
const rosidl_runtime_c__type_description__TypeDescription &
|
||||
DynamicMessageTypeSupport::get_rosidl_runtime_c_type_description() const
|
||||
{
|
||||
auto ts_impl = static_cast<const rosidl_dynamic_message_type_support_impl_t *>(
|
||||
get_const_rosidl_message_type_support().data
|
||||
);
|
||||
return ts_impl->type_description;
|
||||
}
|
||||
|
||||
DynamicSerializationSupport::SharedPtr
|
||||
DynamicMessageTypeSupport::get_shared_dynamic_serialization_support()
|
||||
{
|
||||
return serialization_support_;
|
||||
}
|
||||
|
||||
DynamicSerializationSupport::ConstSharedPtr
|
||||
DynamicMessageTypeSupport::get_shared_dynamic_serialization_support() const
|
||||
{
|
||||
return serialization_support_;
|
||||
}
|
||||
|
||||
DynamicMessageType::SharedPtr
|
||||
DynamicMessageTypeSupport::get_shared_dynamic_message_type()
|
||||
{
|
||||
return dynamic_message_type_;
|
||||
}
|
||||
|
||||
DynamicMessageType::ConstSharedPtr
|
||||
DynamicMessageTypeSupport::get_shared_dynamic_message_type() const
|
||||
{
|
||||
return dynamic_message_type_;
|
||||
}
|
||||
|
||||
DynamicMessage::SharedPtr
|
||||
DynamicMessageTypeSupport::get_shared_dynamic_message()
|
||||
{
|
||||
return dynamic_message_;
|
||||
}
|
||||
|
||||
DynamicMessage::ConstSharedPtr
|
||||
DynamicMessageTypeSupport::get_shared_dynamic_message() const
|
||||
{
|
||||
return dynamic_message_;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// 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) {}
|
||||
|
||||
DynamicSerializationSupport::DynamicSerializationSupport(
|
||||
const std::string & serialization_library_name,
|
||||
rcl_allocator_t allocator)
|
||||
: rosidl_serialization_support_(
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_serialization_support())
|
||||
{
|
||||
rmw_ret_t ret = RMW_RET_ERROR;
|
||||
|
||||
if (serialization_library_name.empty()) {
|
||||
ret = rmw_serialization_support_init(NULL, &allocator, &rosidl_serialization_support_);
|
||||
} else {
|
||||
ret = rmw_serialization_support_init(
|
||||
serialization_library_name.c_str(), &allocator, &rosidl_serialization_support_);
|
||||
}
|
||||
if (ret != RCL_RET_OK) {
|
||||
std::string error_msg =
|
||||
std::string("could not initialize new serialization support object: ") +
|
||||
rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
throw std::runtime_error(error_msg);
|
||||
}
|
||||
}
|
||||
|
||||
DynamicSerializationSupport::DynamicSerializationSupport(
|
||||
rosidl_dynamic_typesupport_serialization_support_t && rosidl_serialization_support)
|
||||
: rosidl_serialization_support_(std::move(rosidl_serialization_support)) {}
|
||||
|
||||
DynamicSerializationSupport::DynamicSerializationSupport(
|
||||
DynamicSerializationSupport && other) noexcept
|
||||
: rosidl_serialization_support_(std::exchange(
|
||||
other.rosidl_serialization_support_,
|
||||
rosidl_dynamic_typesupport_get_zero_initialized_serialization_support())) {}
|
||||
|
||||
DynamicSerializationSupport &
|
||||
DynamicSerializationSupport::operator=(DynamicSerializationSupport && other) noexcept
|
||||
{
|
||||
std::swap(rosidl_serialization_support_, other.rosidl_serialization_support_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
DynamicSerializationSupport::~DynamicSerializationSupport()
|
||||
{
|
||||
rosidl_dynamic_typesupport_serialization_support_fini(&rosidl_serialization_support_);
|
||||
}
|
||||
|
||||
|
||||
// GETTERS =========================================================================================
|
||||
const std::string
|
||||
DynamicSerializationSupport::get_serialization_library_identifier() const
|
||||
{
|
||||
return std::string(
|
||||
rosidl_dynamic_typesupport_serialization_support_get_library_identifier(
|
||||
&rosidl_serialization_support_));
|
||||
}
|
||||
|
||||
rosidl_dynamic_typesupport_serialization_support_t &
|
||||
DynamicSerializationSupport::get_rosidl_serialization_support()
|
||||
{
|
||||
return rosidl_serialization_support_;
|
||||
}
|
||||
|
||||
const rosidl_dynamic_typesupport_serialization_support_t &
|
||||
DynamicSerializationSupport::get_rosidl_serialization_support() const
|
||||
{
|
||||
return rosidl_serialization_support_;
|
||||
}
|
||||
@@ -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,136 @@ 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 ========================================================================
|
||||
// If a subscription is dynamic, then it will use its serialization-specific dynamic data.
|
||||
//
|
||||
// Two cases:
|
||||
// - Dynamic type subscription using dynamic type stored in its own internal type support struct
|
||||
// - Non-dynamic type subscription with no stored dynamic type
|
||||
// - Subscriptions of this type must be able to lookup the local message description to
|
||||
// generate a dynamic type at runtime!
|
||||
// - TODO(methylDragon): I won't be handling this case yet
|
||||
|
||||
// Take dynamic message directly from the middleware
|
||||
case rclcpp::SubscriptionType::DYNAMIC_MESSAGE_DIRECT:
|
||||
{
|
||||
DynamicMessage::SharedPtr dynamic_message = subscription->create_dynamic_message();
|
||||
take_and_do_error_handling(
|
||||
"taking a dynamic message from topic",
|
||||
subscription->get_topic_name(),
|
||||
// This modifies the stored dynamic data in the DynamicMessage in-place
|
||||
[&]() {return subscription->take_dynamic_message(*dynamic_message, message_info);},
|
||||
[&]() {subscription->handle_dynamic_message(dynamic_message, message_info);});
|
||||
subscription->return_dynamic_message(dynamic_message);
|
||||
break;
|
||||
}
|
||||
|
||||
// Take serialized and then convert to dynamic message
|
||||
case rclcpp::SubscriptionType::DYNAMIC_MESSAGE_FROM_SERIALIZED:
|
||||
{
|
||||
std::shared_ptr<SerializedMessage> serialized_msg =
|
||||
subscription->create_serialized_message();
|
||||
|
||||
// NOTE(methylDragon): Is this clone necessary? If I'm following the pattern, it seems so.
|
||||
DynamicMessage::SharedPtr dynamic_message = subscription->create_dynamic_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);},
|
||||
[&]()
|
||||
{
|
||||
bool ret = dynamic_message->deserialize(serialized_msg->get_rcl_serialized_message());
|
||||
if (!ret) {
|
||||
throw_from_rcl_error(ret, "Couldn't convert serialized message to dynamic data!");
|
||||
}
|
||||
subscription->handle_dynamic_message(dynamic_message, message_info);
|
||||
}
|
||||
);
|
||||
subscription->return_serialized_message(serialized_msg);
|
||||
subscription->return_dynamic_message(dynamic_message);
|
||||
break;
|
||||
}
|
||||
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
|
||||
|
||||
@@ -1,327 +0,0 @@
|
||||
// Copyright 2023 iRobot Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/experimental/executors/events_executor/events_executor.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rcpputils/scope_exit.hpp"
|
||||
|
||||
#include "rclcpp/exceptions/exceptions.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
using rclcpp::experimental::executors::EventsExecutor;
|
||||
|
||||
EventsExecutor::EventsExecutor(
|
||||
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue,
|
||||
bool execute_timers_separate_thread,
|
||||
const rclcpp::ExecutorOptions & options)
|
||||
: rclcpp::Executor(options)
|
||||
{
|
||||
// Get ownership of the queue used to store events.
|
||||
if (!events_queue) {
|
||||
throw std::invalid_argument("events_queue can't be a null pointer");
|
||||
}
|
||||
events_queue_ = std::move(events_queue);
|
||||
|
||||
// Create timers manager
|
||||
std::function<void(void *)> timer_on_ready_cb = nullptr;
|
||||
if (execute_timers_separate_thread) {
|
||||
timer_on_ready_cb = [this](const void * timer_id) {
|
||||
ExecutorEvent event = {timer_id, -1, ExecutorEventType::TIMER_EVENT, 1};
|
||||
this->events_queue_->enqueue(event);
|
||||
};
|
||||
}
|
||||
timers_manager_ =
|
||||
std::make_shared<rclcpp::experimental::TimersManager>(context_, timer_on_ready_cb);
|
||||
|
||||
// Create entities collector
|
||||
entities_collector_ = std::make_shared<EventsExecutorEntitiesCollector>(this);
|
||||
entities_collector_->init();
|
||||
|
||||
// Setup the executor notifier to wake up the executor when some guard conditions are tiggered.
|
||||
// The added guard conditions are guaranteed to not go out of scope before the executor itself.
|
||||
executor_notifier_ = std::make_shared<EventsExecutorNotifyWaitable>();
|
||||
executor_notifier_->add_guard_condition(shutdown_guard_condition_.get());
|
||||
executor_notifier_->add_guard_condition(&interrupt_guard_condition_);
|
||||
|
||||
entities_collector_->add_waitable(executor_notifier_);
|
||||
}
|
||||
|
||||
EventsExecutor::~EventsExecutor()
|
||||
{
|
||||
spinning.store(false);
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::spin()
|
||||
{
|
||||
if (spinning.exchange(true)) {
|
||||
throw std::runtime_error("spin() called while already spinning");
|
||||
}
|
||||
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
|
||||
|
||||
timers_manager_->start();
|
||||
RCPPUTILS_SCOPE_EXIT(timers_manager_->stop(); );
|
||||
|
||||
while (rclcpp::ok(context_) && spinning.load()) {
|
||||
// Wait until we get an event
|
||||
ExecutorEvent event;
|
||||
bool has_event = events_queue_->dequeue(event);
|
||||
if (has_event) {
|
||||
this->execute_event(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::spin_some(std::chrono::nanoseconds max_duration)
|
||||
{
|
||||
return this->spin_some_impl(max_duration, false);
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::spin_all(std::chrono::nanoseconds max_duration)
|
||||
{
|
||||
if (max_duration <= 0ns) {
|
||||
throw std::invalid_argument("max_duration must be positive");
|
||||
}
|
||||
return this->spin_some_impl(max_duration, true);
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
|
||||
{
|
||||
if (spinning.exchange(true)) {
|
||||
throw std::runtime_error("spin_some() called while already spinning");
|
||||
}
|
||||
|
||||
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
auto max_duration_not_elapsed = [max_duration, start]() {
|
||||
if (std::chrono::nanoseconds(0) == max_duration) {
|
||||
// told to spin forever if need be
|
||||
return true;
|
||||
} else if (std::chrono::steady_clock::now() - start < max_duration) {
|
||||
// told to spin only for some maximum amount of time
|
||||
return true;
|
||||
}
|
||||
// spun too long
|
||||
return false;
|
||||
};
|
||||
|
||||
// Get the number of events and timers ready at start
|
||||
const size_t ready_events_at_start = events_queue_->size();
|
||||
size_t executed_events = 0;
|
||||
const size_t ready_timers_at_start = timers_manager_->get_number_ready_timers();
|
||||
size_t executed_timers = 0;
|
||||
|
||||
while (rclcpp::ok(context_) && spinning.load() && max_duration_not_elapsed()) {
|
||||
// Execute first ready event from queue if exists
|
||||
if (exhaustive || (executed_events < ready_events_at_start)) {
|
||||
bool has_event = !events_queue_->empty();
|
||||
|
||||
if (has_event) {
|
||||
ExecutorEvent event;
|
||||
bool ret = events_queue_->dequeue(event, std::chrono::nanoseconds(0));
|
||||
if (ret) {
|
||||
this->execute_event(event);
|
||||
executed_events++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute first timer if it is ready
|
||||
if (exhaustive || (executed_timers < ready_timers_at_start)) {
|
||||
bool timer_executed = timers_manager_->execute_head_timer();
|
||||
if (timer_executed) {
|
||||
executed_timers++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If there's no more work available, exit
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::spin_once_impl(std::chrono::nanoseconds timeout)
|
||||
{
|
||||
// In this context a negative input timeout means no timeout
|
||||
if (timeout < 0ns) {
|
||||
timeout = std::chrono::nanoseconds::max();
|
||||
}
|
||||
|
||||
// Select the smallest between input timeout and timer timeout
|
||||
bool is_timer_timeout = false;
|
||||
auto next_timer_timeout = timers_manager_->get_head_timeout();
|
||||
if (next_timer_timeout < timeout) {
|
||||
timeout = next_timer_timeout;
|
||||
is_timer_timeout = true;
|
||||
}
|
||||
|
||||
ExecutorEvent event;
|
||||
bool has_event = events_queue_->dequeue(event, timeout);
|
||||
|
||||
// If we wake up from the wait with an event, it means that it
|
||||
// arrived before any of the timers expired.
|
||||
if (has_event) {
|
||||
this->execute_event(event);
|
||||
} else if (is_timer_timeout) {
|
||||
timers_manager_->execute_head_timer();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::add_node(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
|
||||
{
|
||||
// This field is unused because we don't have to wake up the executor when a node is added.
|
||||
(void) notify;
|
||||
|
||||
// Add node to entities collector
|
||||
entities_collector_->add_node(node_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::add_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify)
|
||||
{
|
||||
this->add_node(node_ptr->get_node_base_interface(), notify);
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::remove_node(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
|
||||
{
|
||||
// This field is unused because we don't have to wake up the executor when a node is removed.
|
||||
(void)notify;
|
||||
|
||||
// Remove node from entities collector.
|
||||
// This will result in un-setting all the event callbacks from its entities.
|
||||
// After this function returns, this executor will not receive any more events associated
|
||||
// to these entities.
|
||||
entities_collector_->remove_node(node_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::remove_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify)
|
||||
{
|
||||
this->remove_node(node_ptr->get_node_base_interface(), notify);
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::execute_event(const ExecutorEvent & event)
|
||||
{
|
||||
switch (event.type) {
|
||||
case ExecutorEventType::CLIENT_EVENT:
|
||||
{
|
||||
auto client = entities_collector_->get_client(event.exec_entity_id);
|
||||
|
||||
if (client) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
execute_client(client);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExecutorEventType::SUBSCRIPTION_EVENT:
|
||||
{
|
||||
auto subscription = entities_collector_->get_subscription(event.exec_entity_id);
|
||||
|
||||
if (subscription) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
execute_subscription(subscription);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExecutorEventType::SERVICE_EVENT:
|
||||
{
|
||||
auto service = entities_collector_->get_service(event.exec_entity_id);
|
||||
|
||||
if (service) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
execute_service(service);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExecutorEventType::TIMER_EVENT:
|
||||
{
|
||||
timers_manager_->execute_ready_timer(event.exec_entity_id);
|
||||
break;
|
||||
}
|
||||
case ExecutorEventType::WAITABLE_EVENT:
|
||||
{
|
||||
auto waitable = entities_collector_->get_waitable(event.exec_entity_id);
|
||||
|
||||
if (waitable) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
auto data = waitable->take_data_by_entity_id(event.gen_entity_id);
|
||||
waitable->execute(data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::add_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
bool notify)
|
||||
{
|
||||
// This field is unused because we don't have to wake up
|
||||
// the executor when a callback group is added.
|
||||
(void)notify;
|
||||
entities_collector_->add_callback_group(group_ptr, node_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::remove_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr, bool notify)
|
||||
{
|
||||
// This field is unused because we don't have to wake up
|
||||
// the executor when a callback group is removed.
|
||||
(void)notify;
|
||||
entities_collector_->remove_callback_group(group_ptr);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
EventsExecutor::get_all_callback_groups()
|
||||
{
|
||||
return entities_collector_->get_all_callback_groups();
|
||||
}
|
||||
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
EventsExecutor::get_manually_added_callback_groups()
|
||||
{
|
||||
return entities_collector_->get_manually_added_callback_groups();
|
||||
}
|
||||
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
EventsExecutor::get_automatically_added_callback_groups_from_nodes()
|
||||
{
|
||||
return entities_collector_->get_automatically_added_callback_groups_from_nodes();
|
||||
}
|
||||
@@ -1,699 +0,0 @@
|
||||
// Copyright 2023 iRobot Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/experimental/executors/events_executor/events_executor_entities_collector.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/memory_strategy.hpp"
|
||||
#include "rclcpp/detail/add_guard_condition_to_rcl_wait_set.hpp"
|
||||
#include "rclcpp/experimental/executors/events_executor/events_executor.hpp"
|
||||
|
||||
using rclcpp::experimental::executors::ExecutorEvent;
|
||||
using rclcpp::experimental::executors::ExecutorEventType;
|
||||
using rclcpp::experimental::executors::EventsExecutorEntitiesCollector;
|
||||
|
||||
EventsExecutorEntitiesCollector::EventsExecutorEntitiesCollector(
|
||||
rclcpp::experimental::executors::EventsExecutor * executor)
|
||||
{
|
||||
if (executor == nullptr) {
|
||||
throw std::runtime_error("Received nullptr executor in EventsExecutorEntitiesCollector.");
|
||||
}
|
||||
|
||||
associated_executor_ = executor;
|
||||
timers_manager_ = associated_executor_->timers_manager_;
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutorEntitiesCollector::init()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(reentrant_mutex_);
|
||||
// Add the EventsExecutorEntitiesCollector shared_ptr to waitables map
|
||||
weak_waitables_map_.emplace(this, this->shared_from_this());
|
||||
}
|
||||
|
||||
EventsExecutorEntitiesCollector::~EventsExecutorEntitiesCollector()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(reentrant_mutex_);
|
||||
|
||||
// Disassociate all callback groups and thus nodes.
|
||||
for (const auto & pair : weak_groups_associated_with_executor_to_nodes_) {
|
||||
auto group = pair.first.lock();
|
||||
if (group) {
|
||||
std::atomic_bool & has_executor = group->get_associated_with_executor_atomic();
|
||||
has_executor.store(false);
|
||||
callback_group_removed_impl(group);
|
||||
}
|
||||
}
|
||||
for (const auto & pair : weak_groups_to_nodes_associated_with_executor_) {
|
||||
auto group = pair.first.lock();
|
||||
if (group) {
|
||||
std::atomic_bool & has_executor = group->get_associated_with_executor_atomic();
|
||||
has_executor.store(false);
|
||||
callback_group_removed_impl(group);
|
||||
}
|
||||
}
|
||||
// Disassociate all nodes
|
||||
for (const auto & weak_node : weak_nodes_) {
|
||||
auto node = weak_node.lock();
|
||||
if (node) {
|
||||
std::atomic_bool & has_executor = node->get_associated_with_executor_atomic();
|
||||
has_executor.store(false);
|
||||
node_removed_impl(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Unset callback group notify guard condition executor callback
|
||||
for (const auto & pair : weak_groups_to_guard_conditions_) {
|
||||
auto group = pair.first.lock();
|
||||
if (group) {
|
||||
auto & group_gc = pair.second;
|
||||
unset_guard_condition_callback(group_gc);
|
||||
}
|
||||
}
|
||||
|
||||
weak_clients_map_.clear();
|
||||
weak_services_map_.clear();
|
||||
weak_waitables_map_.clear();
|
||||
weak_subscriptions_map_.clear();
|
||||
weak_nodes_to_guard_conditions_.clear();
|
||||
weak_groups_to_guard_conditions_.clear();
|
||||
|
||||
weak_groups_associated_with_executor_to_nodes_.clear();
|
||||
weak_groups_to_nodes_associated_with_executor_.clear();
|
||||
weak_nodes_.clear();
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutorEntitiesCollector::execute(std::shared_ptr<void> & data)
|
||||
{
|
||||
// This function is called when the associated executor is notified that something changed.
|
||||
// We do not know if an entity has been added or removed so we have to rebuild everything.
|
||||
(void)data;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(reentrant_mutex_);
|
||||
timers_manager_->clear();
|
||||
|
||||
// If a registered node has a new callback group, register the group.
|
||||
add_callback_groups_from_nodes_associated_to_executor();
|
||||
|
||||
// For all groups registered in the executor, set their event callbacks.
|
||||
set_entities_event_callbacks_from_map(weak_groups_associated_with_executor_to_nodes_);
|
||||
set_entities_event_callbacks_from_map(weak_groups_to_nodes_associated_with_executor_);
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
EventsExecutorEntitiesCollector::take_data()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
EventsExecutorEntitiesCollector::take_data_by_entity_id(size_t id)
|
||||
{
|
||||
(void)id;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutorEntitiesCollector::add_to_wait_set(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
(void)wait_set;
|
||||
}
|
||||
|
||||
bool
|
||||
EventsExecutorEntitiesCollector::is_ready(rcl_wait_set_t * p_wait_set)
|
||||
{
|
||||
(void)p_wait_set;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
EventsExecutorEntitiesCollector::add_node(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
|
||||
{
|
||||
bool is_new_node = false;
|
||||
// If the node already has an executor
|
||||
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
|
||||
if (has_executor.exchange(true)) {
|
||||
throw std::runtime_error("Node has already been added to an executor.");
|
||||
}
|
||||
node_ptr->for_each_callback_group(
|
||||
[this, node_ptr, &is_new_node](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
if (
|
||||
!group_ptr->get_associated_with_executor_atomic().load() &&
|
||||
group_ptr->automatically_add_to_executor_with_node())
|
||||
{
|
||||
is_new_node = (add_callback_group(
|
||||
group_ptr,
|
||||
node_ptr,
|
||||
weak_groups_to_nodes_associated_with_executor_) ||
|
||||
is_new_node);
|
||||
}
|
||||
});
|
||||
weak_nodes_.push_back(node_ptr);
|
||||
return is_new_node;
|
||||
}
|
||||
|
||||
bool
|
||||
EventsExecutorEntitiesCollector::add_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes)
|
||||
{
|
||||
// If the callback_group already has an executor
|
||||
std::atomic_bool & has_executor = group_ptr->get_associated_with_executor_atomic();
|
||||
if (has_executor.exchange(true)) {
|
||||
throw std::runtime_error("Callback group has already been added to an executor.");
|
||||
}
|
||||
bool is_new_node = !has_node(node_ptr, weak_groups_associated_with_executor_to_nodes_) &&
|
||||
!has_node(node_ptr, weak_groups_to_nodes_associated_with_executor_);
|
||||
rclcpp::CallbackGroup::WeakPtr weak_group_ptr = group_ptr;
|
||||
auto insert_info = weak_groups_to_nodes.insert(
|
||||
std::make_pair(weak_group_ptr, node_ptr));
|
||||
bool was_inserted = insert_info.second;
|
||||
if (!was_inserted) {
|
||||
throw std::runtime_error("Callback group was already added to executor.");
|
||||
}
|
||||
|
||||
if (is_new_node) {
|
||||
node_added_impl(node_ptr);
|
||||
}
|
||||
|
||||
if (node_ptr->get_context()->is_valid()) {
|
||||
auto callback_group_guard_condition =
|
||||
group_ptr->get_notify_guard_condition(node_ptr->get_context());
|
||||
|
||||
rclcpp::CallbackGroup::WeakPtr weak_group_ptr = group_ptr;
|
||||
weak_groups_to_guard_conditions_[weak_group_ptr] = callback_group_guard_condition.get();
|
||||
}
|
||||
|
||||
callback_group_added_impl(group_ptr);
|
||||
|
||||
return is_new_node;
|
||||
}
|
||||
|
||||
bool
|
||||
EventsExecutorEntitiesCollector::add_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
|
||||
{
|
||||
return add_callback_group(group_ptr, node_ptr, weak_groups_associated_with_executor_to_nodes_);
|
||||
}
|
||||
|
||||
bool
|
||||
EventsExecutorEntitiesCollector::remove_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
return this->remove_callback_group_from_map(
|
||||
group_ptr,
|
||||
weak_groups_associated_with_executor_to_nodes_);
|
||||
}
|
||||
|
||||
bool
|
||||
EventsExecutorEntitiesCollector::remove_callback_group_from_map(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes)
|
||||
{
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr;
|
||||
rclcpp::CallbackGroup::WeakPtr weak_group_ptr = group_ptr;
|
||||
auto iter = weak_groups_to_nodes.find(weak_group_ptr);
|
||||
if (iter != weak_groups_to_nodes.end()) {
|
||||
node_ptr = iter->second.lock();
|
||||
if (node_ptr == nullptr) {
|
||||
throw std::runtime_error("Node must not be deleted before its callback group(s).");
|
||||
}
|
||||
weak_groups_to_nodes.erase(iter);
|
||||
callback_group_removed_impl(group_ptr);
|
||||
} else {
|
||||
throw std::runtime_error("Callback group needs to be associated with executor.");
|
||||
}
|
||||
// If the node was matched and removed, interrupt waiting.
|
||||
bool node_removed = false;
|
||||
if (!has_node(node_ptr, weak_groups_associated_with_executor_to_nodes_) &&
|
||||
!has_node(node_ptr, weak_groups_to_nodes_associated_with_executor_))
|
||||
{
|
||||
node_removed_impl(node_ptr);
|
||||
node_removed = true;
|
||||
}
|
||||
|
||||
weak_groups_to_guard_conditions_.erase(weak_group_ptr);
|
||||
|
||||
return node_removed;
|
||||
}
|
||||
|
||||
bool
|
||||
EventsExecutorEntitiesCollector::remove_node(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
|
||||
{
|
||||
if (!node_ptr->get_associated_with_executor_atomic().load()) {
|
||||
return false;
|
||||
}
|
||||
bool node_found = false;
|
||||
auto node_it = weak_nodes_.begin();
|
||||
while (node_it != weak_nodes_.end()) {
|
||||
bool matched = (node_it->lock() == node_ptr);
|
||||
if (matched) {
|
||||
weak_nodes_.erase(node_it);
|
||||
node_found = true;
|
||||
break;
|
||||
}
|
||||
++node_it;
|
||||
}
|
||||
if (!node_found) {
|
||||
return false;
|
||||
}
|
||||
std::vector<rclcpp::CallbackGroup::SharedPtr> found_group_ptrs;
|
||||
std::for_each(
|
||||
weak_groups_to_nodes_associated_with_executor_.begin(),
|
||||
weak_groups_to_nodes_associated_with_executor_.end(),
|
||||
[&found_group_ptrs, node_ptr](std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> key_value_pair) {
|
||||
auto & weak_node_ptr = key_value_pair.second;
|
||||
auto shared_node_ptr = weak_node_ptr.lock();
|
||||
auto group_ptr = key_value_pair.first.lock();
|
||||
if (shared_node_ptr == node_ptr) {
|
||||
found_group_ptrs.push_back(group_ptr);
|
||||
}
|
||||
});
|
||||
std::for_each(
|
||||
found_group_ptrs.begin(), found_group_ptrs.end(), [this]
|
||||
(rclcpp::CallbackGroup::SharedPtr group_ptr) {
|
||||
this->remove_callback_group_from_map(
|
||||
group_ptr,
|
||||
weak_groups_to_nodes_associated_with_executor_);
|
||||
});
|
||||
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
|
||||
has_executor.store(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true iff the weak_groups_to_nodes map has node_ptr as the value in any of its entry.
|
||||
bool
|
||||
EventsExecutorEntitiesCollector::has_node(
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &
|
||||
weak_groups_to_nodes) const
|
||||
{
|
||||
return std::find_if(
|
||||
weak_groups_to_nodes.begin(),
|
||||
weak_groups_to_nodes.end(),
|
||||
[&](const WeakCallbackGroupsToNodesMap::value_type & other) -> bool {
|
||||
auto other_ptr = other.second.lock();
|
||||
return other_ptr == node_ptr;
|
||||
}) != weak_groups_to_nodes.end();
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutorEntitiesCollector::add_callback_groups_from_nodes_associated_to_executor()
|
||||
{
|
||||
for (const auto & weak_node : weak_nodes_) {
|
||||
auto node = weak_node.lock();
|
||||
if (node) {
|
||||
node->for_each_callback_group(
|
||||
[this, node](rclcpp::CallbackGroup::SharedPtr shared_group_ptr)
|
||||
{
|
||||
if (shared_group_ptr->automatically_add_to_executor_with_node() &&
|
||||
!shared_group_ptr->get_associated_with_executor_atomic().load())
|
||||
{
|
||||
add_callback_group(
|
||||
shared_group_ptr,
|
||||
node,
|
||||
weak_groups_to_nodes_associated_with_executor_);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
EventsExecutorEntitiesCollector::get_all_callback_groups()
|
||||
{
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
|
||||
for (const auto & group_node_ptr : weak_groups_associated_with_executor_to_nodes_) {
|
||||
groups.push_back(group_node_ptr.first);
|
||||
}
|
||||
for (const auto & group_node_ptr : weak_groups_to_nodes_associated_with_executor_) {
|
||||
groups.push_back(group_node_ptr.first);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
EventsExecutorEntitiesCollector::get_manually_added_callback_groups()
|
||||
{
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
|
||||
for (const auto & group_node_ptr : weak_groups_associated_with_executor_to_nodes_) {
|
||||
groups.push_back(group_node_ptr.first);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
EventsExecutorEntitiesCollector::get_automatically_added_callback_groups_from_nodes()
|
||||
{
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
|
||||
for (const auto & group_node_ptr : weak_groups_to_nodes_associated_with_executor_) {
|
||||
groups.push_back(group_node_ptr.first);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutorEntitiesCollector::callback_group_added_impl(
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(reentrant_mutex_);
|
||||
|
||||
rclcpp::CallbackGroup::WeakPtr weak_group_ptr = group;
|
||||
|
||||
auto iter = weak_groups_to_guard_conditions_.find(weak_group_ptr);
|
||||
if (iter != weak_groups_to_guard_conditions_.end()) {
|
||||
// Set an event callback for the group's notify guard condition, so if new entities are added
|
||||
// or removed to this node we will receive an event.
|
||||
set_guard_condition_callback(iter->second);
|
||||
}
|
||||
// For all entities in the callback group, set their event callback
|
||||
set_callback_group_entities_callbacks(group);
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutorEntitiesCollector::node_added_impl(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(reentrant_mutex_);
|
||||
|
||||
auto notify_guard_condition = &(node->get_notify_guard_condition());
|
||||
// Set an event callback for the node's notify guard condition, so if new entities are added
|
||||
// or removed to this node we will receive an event.
|
||||
set_guard_condition_callback(notify_guard_condition);
|
||||
|
||||
// Store node's notify guard condition
|
||||
weak_nodes_to_guard_conditions_[node] = notify_guard_condition;
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutorEntitiesCollector::callback_group_removed_impl(
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(reentrant_mutex_);
|
||||
// For all the entities in the group, unset their callbacks
|
||||
unset_callback_group_entities_callbacks(group);
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutorEntitiesCollector::node_removed_impl(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(reentrant_mutex_);
|
||||
// Node doesn't have more callback groups associated to the executor.
|
||||
// Unset the event callback for the node's notify guard condition, to stop
|
||||
// receiving events if entities are added or removed to this node.
|
||||
unset_guard_condition_callback(&(node->get_notify_guard_condition()));
|
||||
|
||||
// Remove guard condition from list
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr weak_node_ptr(node);
|
||||
weak_nodes_to_guard_conditions_.erase(weak_node_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutorEntitiesCollector::set_entities_event_callbacks_from_map(
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes)
|
||||
{
|
||||
for (const auto & pair : weak_groups_to_nodes) {
|
||||
auto group = pair.first.lock();
|
||||
auto node = pair.second.lock();
|
||||
if (!node || !group || !group->can_be_taken_from().load()) {
|
||||
continue;
|
||||
}
|
||||
set_callback_group_entities_callbacks(group);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutorEntitiesCollector::set_callback_group_entities_callbacks(
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
// Timers are handled by the timers manager
|
||||
group->find_timer_ptrs_if(
|
||||
[this](const rclcpp::TimerBase::SharedPtr & timer) {
|
||||
if (timer) {
|
||||
timers_manager_->add_timer(timer);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Set callbacks for all other entity types
|
||||
group->find_subscription_ptrs_if(
|
||||
[this](const rclcpp::SubscriptionBase::SharedPtr & subscription) {
|
||||
if (subscription) {
|
||||
weak_subscriptions_map_.emplace(subscription.get(), subscription);
|
||||
|
||||
subscription->set_on_new_message_callback(
|
||||
create_entity_callback(subscription.get(), ExecutorEventType::SUBSCRIPTION_EVENT));
|
||||
}
|
||||
return false;
|
||||
});
|
||||
group->find_service_ptrs_if(
|
||||
[this](const rclcpp::ServiceBase::SharedPtr & service) {
|
||||
if (service) {
|
||||
weak_services_map_.emplace(service.get(), service);
|
||||
|
||||
service->set_on_new_request_callback(
|
||||
create_entity_callback(service.get(), ExecutorEventType::SERVICE_EVENT));
|
||||
}
|
||||
return false;
|
||||
});
|
||||
group->find_client_ptrs_if(
|
||||
[this](const rclcpp::ClientBase::SharedPtr & client) {
|
||||
if (client) {
|
||||
weak_clients_map_.emplace(client.get(), client);
|
||||
|
||||
client->set_on_new_response_callback(
|
||||
create_entity_callback(client.get(), ExecutorEventType::CLIENT_EVENT));
|
||||
}
|
||||
return false;
|
||||
});
|
||||
group->find_waitable_ptrs_if(
|
||||
[this](const rclcpp::Waitable::SharedPtr & waitable) {
|
||||
if (waitable) {
|
||||
weak_waitables_map_.emplace(waitable.get(), waitable);
|
||||
|
||||
waitable->set_on_ready_callback(
|
||||
create_waitable_callback(waitable.get()));
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutorEntitiesCollector::unset_callback_group_entities_callbacks(
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
auto iter = weak_groups_to_guard_conditions_.find(group);
|
||||
|
||||
if (iter != weak_groups_to_guard_conditions_.end()) {
|
||||
unset_guard_condition_callback(iter->second);
|
||||
}
|
||||
|
||||
// Timers are handled by the timers manager
|
||||
group->find_timer_ptrs_if(
|
||||
[this](const rclcpp::TimerBase::SharedPtr & timer) {
|
||||
if (timer) {
|
||||
timers_manager_->remove_timer(timer);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Unset callbacks for all other entity types
|
||||
group->find_subscription_ptrs_if(
|
||||
[this](const rclcpp::SubscriptionBase::SharedPtr & subscription) {
|
||||
if (subscription) {
|
||||
subscription->clear_on_new_message_callback();
|
||||
weak_subscriptions_map_.erase(subscription.get());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
group->find_service_ptrs_if(
|
||||
[this](const rclcpp::ServiceBase::SharedPtr & service) {
|
||||
if (service) {
|
||||
service->clear_on_new_request_callback();
|
||||
weak_services_map_.erase(service.get());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
group->find_client_ptrs_if(
|
||||
[this](const rclcpp::ClientBase::SharedPtr & client) {
|
||||
if (client) {
|
||||
client->clear_on_new_response_callback();
|
||||
weak_clients_map_.erase(client.get());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
group->find_waitable_ptrs_if(
|
||||
[this](const rclcpp::Waitable::SharedPtr & waitable) {
|
||||
if (waitable) {
|
||||
waitable->clear_on_ready_callback();
|
||||
weak_waitables_map_.erase(waitable.get());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutorEntitiesCollector::set_guard_condition_callback(
|
||||
rclcpp::GuardCondition * guard_condition)
|
||||
{
|
||||
auto gc_callback = [this](size_t num_events) {
|
||||
// Override num events (we don't care more than a single event)
|
||||
num_events = 1;
|
||||
int gc_id = -1;
|
||||
ExecutorEvent event = {this, gc_id, ExecutorEventType::WAITABLE_EVENT, num_events};
|
||||
associated_executor_->events_queue_->enqueue(event);
|
||||
};
|
||||
|
||||
guard_condition->set_on_trigger_callback(gc_callback);
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutorEntitiesCollector::unset_guard_condition_callback(
|
||||
rclcpp::GuardCondition * guard_condition)
|
||||
{
|
||||
guard_condition->set_on_trigger_callback(nullptr);
|
||||
}
|
||||
|
||||
rclcpp::SubscriptionBase::SharedPtr
|
||||
EventsExecutorEntitiesCollector::get_subscription(const void * subscription_id)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(reentrant_mutex_);
|
||||
|
||||
auto it = weak_subscriptions_map_.find(subscription_id);
|
||||
|
||||
if (it != weak_subscriptions_map_.end()) {
|
||||
auto subscription_weak_ptr = it->second;
|
||||
auto subscription_shared_ptr = subscription_weak_ptr.lock();
|
||||
|
||||
if (subscription_shared_ptr) {
|
||||
return subscription_shared_ptr;
|
||||
}
|
||||
|
||||
// The subscription expired, remove from map
|
||||
weak_subscriptions_map_.erase(it);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rclcpp::ClientBase::SharedPtr
|
||||
EventsExecutorEntitiesCollector::get_client(const void * client_id)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(reentrant_mutex_);
|
||||
|
||||
auto it = weak_clients_map_.find(client_id);
|
||||
|
||||
if (it != weak_clients_map_.end()) {
|
||||
auto client_weak_ptr = it->second;
|
||||
auto client_shared_ptr = client_weak_ptr.lock();
|
||||
|
||||
if (client_shared_ptr) {
|
||||
return client_shared_ptr;
|
||||
}
|
||||
|
||||
// The client expired, remove from map
|
||||
weak_clients_map_.erase(it);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rclcpp::ServiceBase::SharedPtr
|
||||
EventsExecutorEntitiesCollector::get_service(const void * service_id)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(reentrant_mutex_);
|
||||
|
||||
auto it = weak_services_map_.find(service_id);
|
||||
|
||||
if (it != weak_services_map_.end()) {
|
||||
auto service_weak_ptr = it->second;
|
||||
auto service_shared_ptr = service_weak_ptr.lock();
|
||||
|
||||
if (service_shared_ptr) {
|
||||
return service_shared_ptr;
|
||||
}
|
||||
|
||||
// The service expired, remove from map
|
||||
weak_services_map_.erase(it);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rclcpp::Waitable::SharedPtr
|
||||
EventsExecutorEntitiesCollector::get_waitable(const void * waitable_id)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(reentrant_mutex_);
|
||||
|
||||
auto it = weak_waitables_map_.find(waitable_id);
|
||||
|
||||
if (it != weak_waitables_map_.end()) {
|
||||
auto waitable_weak_ptr = it->second;
|
||||
auto waitable_shared_ptr = waitable_weak_ptr.lock();
|
||||
|
||||
if (waitable_shared_ptr) {
|
||||
return waitable_shared_ptr;
|
||||
}
|
||||
|
||||
// The waitable expired, remove from map
|
||||
weak_waitables_map_.erase(it);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutorEntitiesCollector::add_waitable(rclcpp::Waitable::SharedPtr waitable)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(reentrant_mutex_);
|
||||
|
||||
weak_waitables_map_.emplace(waitable.get(), waitable);
|
||||
|
||||
waitable->set_on_ready_callback(
|
||||
create_waitable_callback(waitable.get()));
|
||||
}
|
||||
|
||||
std::function<void(size_t)>
|
||||
EventsExecutorEntitiesCollector::create_entity_callback(
|
||||
void * exec_entity_id, ExecutorEventType event_type)
|
||||
{
|
||||
std::function<void(size_t)>
|
||||
callback = [this, exec_entity_id, event_type](size_t num_events) {
|
||||
ExecutorEvent event = {exec_entity_id, -1, event_type, num_events};
|
||||
associated_executor_->events_queue_->enqueue(event);
|
||||
};
|
||||
return callback;
|
||||
}
|
||||
|
||||
std::function<void(size_t, int)>
|
||||
EventsExecutorEntitiesCollector::create_waitable_callback(void * exec_entity_id)
|
||||
{
|
||||
std::function<void(size_t, int)>
|
||||
callback = [this, exec_entity_id](size_t num_events, int gen_entity_id) {
|
||||
ExecutorEvent event =
|
||||
{exec_entity_id, gen_entity_id, ExecutorEventType::WAITABLE_EVENT, num_events};
|
||||
associated_executor_->events_queue_->enqueue(event);
|
||||
};
|
||||
return callback;
|
||||
}
|
||||
@@ -1,311 +0,0 @@
|
||||
// Copyright 2023 iRobot Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/experimental/timers_manager.hpp"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
using rclcpp::experimental::TimersManager;
|
||||
|
||||
TimersManager::TimersManager(
|
||||
std::shared_ptr<rclcpp::Context> context,
|
||||
std::function<void(void *)> on_ready_callback)
|
||||
{
|
||||
context_ = context;
|
||||
on_ready_callback_ = on_ready_callback;
|
||||
}
|
||||
|
||||
TimersManager::~TimersManager()
|
||||
{
|
||||
// Remove all timers
|
||||
this->clear();
|
||||
|
||||
// Make sure timers thread is stopped before destroying this object
|
||||
this->stop();
|
||||
}
|
||||
|
||||
void TimersManager::add_timer(rclcpp::TimerBase::SharedPtr timer)
|
||||
{
|
||||
if (!timer) {
|
||||
throw std::invalid_argument("TimersManager::add_timer() trying to add nullptr timer");
|
||||
}
|
||||
|
||||
bool added = false;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(timers_mutex_);
|
||||
added = weak_timers_heap_.add_timer(timer);
|
||||
timers_updated_ = timers_updated_ || added;
|
||||
}
|
||||
|
||||
timer->set_on_reset_callback(
|
||||
[this](size_t arg) {
|
||||
{
|
||||
(void)arg;
|
||||
std::unique_lock<std::mutex> lock(timers_mutex_);
|
||||
timers_updated_ = true;
|
||||
}
|
||||
timers_cv_.notify_one();
|
||||
});
|
||||
|
||||
if (added) {
|
||||
// Notify that a timer has been added
|
||||
timers_cv_.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void TimersManager::start()
|
||||
{
|
||||
// Make sure that the thread is not already running
|
||||
if (running_.exchange(true)) {
|
||||
throw std::runtime_error("TimersManager::start() can't start timers thread as already running");
|
||||
}
|
||||
|
||||
timers_thread_ = std::thread(&TimersManager::run_timers, this);
|
||||
}
|
||||
|
||||
void TimersManager::stop()
|
||||
{
|
||||
// Lock stop() function to prevent race condition in destructor
|
||||
std::unique_lock<std::mutex> lock(stop_mutex_);
|
||||
running_ = false;
|
||||
|
||||
// Notify the timers manager thread to wake up
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(timers_mutex_);
|
||||
timers_updated_ = true;
|
||||
}
|
||||
timers_cv_.notify_one();
|
||||
|
||||
// Join timers thread if it's running
|
||||
if (timers_thread_.joinable()) {
|
||||
timers_thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds TimersManager::get_head_timeout()
|
||||
{
|
||||
// Do not allow to interfere with the thread running
|
||||
if (running_) {
|
||||
throw std::runtime_error(
|
||||
"get_head_timeout() can't be used while timers thread is running");
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(timers_mutex_);
|
||||
return this->get_head_timeout_unsafe();
|
||||
}
|
||||
|
||||
size_t TimersManager::get_number_ready_timers()
|
||||
{
|
||||
// Do not allow to interfere with the thread running
|
||||
if (running_) {
|
||||
throw std::runtime_error(
|
||||
"get_number_ready_timers() can't be used while timers thread is running");
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(timers_mutex_);
|
||||
TimersHeap locked_heap = weak_timers_heap_.validate_and_lock();
|
||||
return locked_heap.get_number_ready_timers();
|
||||
}
|
||||
|
||||
void TimersManager::execute_ready_timers()
|
||||
{
|
||||
// Do not allow to interfere with the thread running
|
||||
if (running_) {
|
||||
throw std::runtime_error(
|
||||
"execute_ready_timers() can't be used while timers thread is running");
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(timers_mutex_);
|
||||
this->execute_ready_timers_unsafe();
|
||||
}
|
||||
|
||||
bool TimersManager::execute_head_timer()
|
||||
{
|
||||
// Do not allow to interfere with the thread running
|
||||
if (running_) {
|
||||
throw std::runtime_error(
|
||||
"execute_head_timer() can't be used while timers thread is running");
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(timers_mutex_);
|
||||
|
||||
TimersHeap timers_heap = weak_timers_heap_.validate_and_lock();
|
||||
|
||||
// Nothing to do if we don't have any timer
|
||||
if (timers_heap.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TimerPtr head_timer = timers_heap.front();
|
||||
|
||||
const bool timer_ready = head_timer->is_ready();
|
||||
if (timer_ready) {
|
||||
head_timer->call();
|
||||
if (on_ready_callback_) {
|
||||
on_ready_callback_(head_timer.get());
|
||||
} else {
|
||||
head_timer->execute_callback();
|
||||
}
|
||||
timers_heap.heapify_root();
|
||||
weak_timers_heap_.store(timers_heap);
|
||||
}
|
||||
|
||||
return timer_ready;
|
||||
}
|
||||
|
||||
void TimersManager::execute_ready_timer(const void * timer_id)
|
||||
{
|
||||
TimerPtr ready_timer;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(timers_mutex_);
|
||||
ready_timer = weak_timers_heap_.get_timer(timer_id);
|
||||
}
|
||||
if (ready_timer) {
|
||||
ready_timer->execute_callback();
|
||||
}
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds TimersManager::get_head_timeout_unsafe()
|
||||
{
|
||||
// If we don't have any weak pointer, then we just return maximum timeout
|
||||
if (weak_timers_heap_.empty()) {
|
||||
return std::chrono::nanoseconds::max();
|
||||
}
|
||||
// Weak heap is not empty, so try to lock the first element.
|
||||
// If it is still a valid pointer, it is guaranteed to be the correct head
|
||||
TimerPtr head_timer = weak_timers_heap_.front().lock();
|
||||
|
||||
if (!head_timer) {
|
||||
// The first element has expired, we can't make other assumptions on the heap
|
||||
// and we need to entirely validate it.
|
||||
TimersHeap locked_heap = weak_timers_heap_.validate_and_lock();
|
||||
// NOTE: the following operations will not modify any element in the heap, so we
|
||||
// don't have to call `weak_timers_heap_.store(locked_heap)` at the end.
|
||||
|
||||
if (locked_heap.empty()) {
|
||||
return std::chrono::nanoseconds::max();
|
||||
}
|
||||
head_timer = locked_heap.front();
|
||||
}
|
||||
|
||||
return head_timer->time_until_trigger();
|
||||
}
|
||||
|
||||
void TimersManager::execute_ready_timers_unsafe()
|
||||
{
|
||||
// We start by locking the timers
|
||||
TimersHeap locked_heap = weak_timers_heap_.validate_and_lock();
|
||||
|
||||
// Nothing to do if we don't have any timer
|
||||
if (locked_heap.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep executing timers until they are ready and they were already ready when we started.
|
||||
// The two checks prevent this function from blocking indefinitely if the
|
||||
// time required for executing the timers is longer than their period.
|
||||
|
||||
TimerPtr head_timer = locked_heap.front();
|
||||
const size_t number_ready_timers = locked_heap.get_number_ready_timers();
|
||||
size_t executed_timers = 0;
|
||||
while (executed_timers < number_ready_timers && head_timer->is_ready()) {
|
||||
head_timer->call();
|
||||
if (on_ready_callback_) {
|
||||
on_ready_callback_(head_timer.get());
|
||||
} else {
|
||||
head_timer->execute_callback();
|
||||
}
|
||||
|
||||
executed_timers++;
|
||||
// Executing a timer will result in updating its time_until_trigger, so re-heapify
|
||||
locked_heap.heapify_root();
|
||||
// Get new head timer
|
||||
head_timer = locked_heap.front();
|
||||
}
|
||||
|
||||
// After having performed work on the locked heap we reflect the changes to weak one.
|
||||
// Timers will be already sorted the next time we need them if none went out of scope.
|
||||
weak_timers_heap_.store(locked_heap);
|
||||
}
|
||||
|
||||
void TimersManager::run_timers()
|
||||
{
|
||||
while (rclcpp::ok(context_) && running_) {
|
||||
// Lock mutex
|
||||
std::unique_lock<std::mutex> lock(timers_mutex_);
|
||||
|
||||
std::chrono::nanoseconds time_to_sleep = get_head_timeout_unsafe();
|
||||
|
||||
// No need to wait if a timer is already available
|
||||
if (time_to_sleep > std::chrono::nanoseconds::zero()) {
|
||||
if (time_to_sleep != std::chrono::nanoseconds::max()) {
|
||||
// Wait until timeout or notification that timers have been updated
|
||||
timers_cv_.wait_for(lock, time_to_sleep, [this]() {return timers_updated_;});
|
||||
} else {
|
||||
// Wait until notification that timers have been updated
|
||||
timers_cv_.wait(lock, [this]() {return timers_updated_;});
|
||||
}
|
||||
}
|
||||
|
||||
// Reset timers updated flag
|
||||
timers_updated_ = false;
|
||||
|
||||
// Execute timers
|
||||
this->execute_ready_timers_unsafe();
|
||||
}
|
||||
|
||||
// Make sure the running flag is set to false when we exit from this function
|
||||
// to allow restarting the timers thread.
|
||||
running_ = false;
|
||||
}
|
||||
|
||||
void TimersManager::clear()
|
||||
{
|
||||
{
|
||||
// Lock mutex and then clear all data structures
|
||||
std::unique_lock<std::mutex> lock(timers_mutex_);
|
||||
|
||||
TimersHeap locked_heap = weak_timers_heap_.validate_and_lock();
|
||||
locked_heap.clear_callbacks();
|
||||
|
||||
weak_timers_heap_.clear();
|
||||
|
||||
timers_updated_ = true;
|
||||
}
|
||||
|
||||
// Notify timers thread such that it can re-compute its timeout
|
||||
timers_cv_.notify_one();
|
||||
}
|
||||
|
||||
void TimersManager::remove_timer(TimerPtr timer)
|
||||
{
|
||||
bool removed = false;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(timers_mutex_);
|
||||
removed = weak_timers_heap_.remove_timer(timer);
|
||||
|
||||
timers_updated_ = timers_updated_ || removed;
|
||||
}
|
||||
|
||||
if (removed) {
|
||||
// Notify timers thread such that it can re-compute its timeout
|
||||
timers_cv_.notify_one();
|
||||
timer->clear_on_reset_callback();
|
||||
}
|
||||
}
|
||||
@@ -43,8 +43,7 @@ void GenericSubscription::handle_message(
|
||||
"handle_message is not implemented for GenericSubscription");
|
||||
}
|
||||
|
||||
void
|
||||
GenericSubscription::handle_serialized_message(
|
||||
void GenericSubscription::handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> & message,
|
||||
const rclcpp::MessageInfo &)
|
||||
{
|
||||
@@ -72,4 +71,55 @@ void GenericSubscription::return_serialized_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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#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"
|
||||
@@ -32,6 +33,8 @@
|
||||
#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) {
|
||||
@@ -255,10 +266,10 @@ SubscriptionBase::get_message_type_support_handle() const
|
||||
return type_support_;
|
||||
}
|
||||
|
||||
bool
|
||||
SubscriptionBase::is_serialized() const
|
||||
rclcpp::SubscriptionType
|
||||
SubscriptionBase::get_subscription_type() const
|
||||
{
|
||||
return is_serialized_;
|
||||
return subscription_type_;
|
||||
}
|
||||
|
||||
size_t
|
||||
@@ -442,8 +453,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),
|
||||
@@ -515,3 +525,24 @@ SubscriptionBase::get_content_filter() const
|
||||
|
||||
return ret_options;
|
||||
}
|
||||
|
||||
|
||||
// DYNAMIC TYPE ==================================================================================
|
||||
// TODO(methylDragon): Reorder later
|
||||
bool
|
||||
SubscriptionBase::take_dynamic_message(
|
||||
rclcpp::dynamic_typesupport::DynamicMessage & message_out,
|
||||
rclcpp::MessageInfo & message_info_out)
|
||||
{
|
||||
rcl_ret_t ret = rcl_take_dynamic_message(
|
||||
this->get_subscription_handle().get(),
|
||||
&message_out.get_rosidl_dynamic_data(),
|
||||
&message_info_out.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;
|
||||
}
|
||||
|
||||
@@ -618,14 +618,6 @@ if(TARGET test_timer)
|
||||
target_link_libraries(test_timer ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_timers_manager test_timers_manager.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_timers_manager)
|
||||
ament_target_dependencies(test_timers_manager
|
||||
"rcl")
|
||||
target_link_libraries(test_timers_manager ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_time_source test_time_source.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_time_source)
|
||||
@@ -696,22 +688,6 @@ if(TARGET test_static_executor_entities_collector)
|
||||
target_link_libraries(test_static_executor_entities_collector ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_events_executor executors/test_events_executor.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_events_executor)
|
||||
ament_target_dependencies(test_events_executor
|
||||
"test_msgs")
|
||||
target_link_libraries(test_events_executor ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_events_queue executors/test_events_queue.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_events_queue)
|
||||
ament_target_dependencies(test_events_queue
|
||||
"test_msgs")
|
||||
target_link_libraries(test_events_queue ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_guard_condition test_guard_condition.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_guard_condition)
|
||||
|
||||
@@ -1,499 +0,0 @@
|
||||
// Copyright 2023 iRobot Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/experimental/executors/events_executor/events_executor.hpp"
|
||||
|
||||
#include "test_msgs/srv/empty.hpp"
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
using rclcpp::experimental::executors::EventsExecutor;
|
||||
|
||||
class TestEventsExecutor : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestEventsExecutor, run_clients_servers)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
bool request_received = false;
|
||||
bool response_received = false;
|
||||
auto service =
|
||||
node->create_service<test_msgs::srv::Empty>(
|
||||
"service",
|
||||
[&request_received](
|
||||
const test_msgs::srv::Empty::Request::SharedPtr,
|
||||
test_msgs::srv::Empty::Response::SharedPtr)
|
||||
{
|
||||
request_received = true;
|
||||
});
|
||||
auto client = node->create_client<test_msgs::srv::Empty>("service");
|
||||
|
||||
EventsExecutor executor;
|
||||
executor.add_node(node);
|
||||
|
||||
bool spin_exited = false;
|
||||
std::thread spinner([&spin_exited, &executor, this]() {
|
||||
executor.spin();
|
||||
spin_exited = true;
|
||||
});
|
||||
|
||||
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
|
||||
client->async_send_request(
|
||||
request,
|
||||
[&response_received](rclcpp::Client<test_msgs::srv::Empty>::SharedFuture result_future) {
|
||||
(void)result_future;
|
||||
response_received = true;
|
||||
});
|
||||
|
||||
// Wait some time for the client-server to be invoked
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (
|
||||
!response_received &&
|
||||
!spin_exited &&
|
||||
(std::chrono::steady_clock::now() - start < 1s))
|
||||
{
|
||||
std::this_thread::sleep_for(5ms);
|
||||
}
|
||||
|
||||
executor.cancel();
|
||||
spinner.join();
|
||||
executor.remove_node(node);
|
||||
|
||||
EXPECT_TRUE(request_received);
|
||||
EXPECT_TRUE(response_received);
|
||||
EXPECT_TRUE(spin_exited);
|
||||
}
|
||||
|
||||
TEST_F(TestEventsExecutor, spin_once_max_duration_timeout)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
EventsExecutor executor;
|
||||
executor.add_node(node);
|
||||
|
||||
// Consume previous events so we have a fresh start
|
||||
executor.spin_all(1s);
|
||||
|
||||
size_t t_runs = 0;
|
||||
auto t = node->create_wall_timer(
|
||||
10s,
|
||||
[&]() {
|
||||
t_runs++;
|
||||
});
|
||||
|
||||
// This first spin_once takes care of the waitable event
|
||||
// generated by the addition of the timer to the node
|
||||
executor.spin_once(1s);
|
||||
EXPECT_EQ(0u, t_runs);
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
// This second spin_once should take care of the timer,
|
||||
executor.spin_once(10ms);
|
||||
|
||||
// but doesn't spin the time enough to call the timer callback.
|
||||
EXPECT_EQ(0u, t_runs);
|
||||
EXPECT_TRUE(std::chrono::steady_clock::now() - start < 200ms);
|
||||
}
|
||||
|
||||
// FIX THIS TEST! The entities collector is being called too many times!
|
||||
/*
|
||||
TEST_F(TestEventsExecutor, spin_once_max_duration_timer)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
EventsExecutor executor;
|
||||
executor.add_node(node);
|
||||
|
||||
// Consume previous events so we have a fresh start
|
||||
executor.spin_all(1s);
|
||||
|
||||
size_t t_runs = 0;
|
||||
auto t = node->create_wall_timer(
|
||||
10ms,
|
||||
[&]() {
|
||||
t_runs++;
|
||||
});
|
||||
|
||||
// This first spin_once takes care of the waitable event
|
||||
// generated by the addition of the timer to the node
|
||||
executor.spin_once(1s);
|
||||
EXPECT_EQ(0u, t_runs);
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
// This second spin_once should take care of the timer
|
||||
executor.spin_once(11ms);
|
||||
|
||||
EXPECT_EQ(1u, t_runs);
|
||||
EXPECT_TRUE(std::chrono::steady_clock::now() - start < 200ms);
|
||||
}
|
||||
*/
|
||||
|
||||
TEST_F(TestEventsExecutor, spin_some_max_duration)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
size_t t_runs = 0;
|
||||
auto t = node->create_wall_timer(
|
||||
10s,
|
||||
[&]() {
|
||||
t_runs++;
|
||||
});
|
||||
|
||||
EventsExecutor executor;
|
||||
executor.add_node(node);
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
executor.spin_some(10ms);
|
||||
|
||||
EXPECT_EQ(0u, t_runs);
|
||||
EXPECT_TRUE(std::chrono::steady_clock::now() - start < 200ms);
|
||||
}
|
||||
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
size_t t_runs = 0;
|
||||
auto t = node->create_wall_timer(
|
||||
10ms,
|
||||
[&]() {
|
||||
t_runs++;
|
||||
});
|
||||
|
||||
// Sleep some time for the timer to be ready when spin
|
||||
std::this_thread::sleep_for(10ms);
|
||||
|
||||
EventsExecutor executor;
|
||||
executor.add_node(node);
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
executor.spin_some(10s);
|
||||
|
||||
EXPECT_EQ(1u, t_runs);
|
||||
EXPECT_TRUE(std::chrono::steady_clock::now() - start < 200ms);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestEventsExecutor, spin_some_zero_duration)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
size_t t_runs = 0;
|
||||
auto t = node->create_wall_timer(
|
||||
20ms,
|
||||
[&]() {
|
||||
t_runs++;
|
||||
});
|
||||
|
||||
// Sleep some time for the timer to be ready when spin
|
||||
std::this_thread::sleep_for(20ms);
|
||||
|
||||
EventsExecutor executor;
|
||||
executor.add_node(node);
|
||||
executor.spin_some(0ms);
|
||||
|
||||
EXPECT_EQ(1u, t_runs);
|
||||
}
|
||||
|
||||
TEST_F(TestEventsExecutor, spin_all_max_duration)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
size_t t_runs = 0;
|
||||
auto t = node->create_wall_timer(
|
||||
10s,
|
||||
[&]() {
|
||||
t_runs++;
|
||||
});
|
||||
|
||||
EventsExecutor executor;
|
||||
executor.add_node(node);
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
executor.spin_all(10ms);
|
||||
|
||||
EXPECT_EQ(0u, t_runs);
|
||||
EXPECT_TRUE(std::chrono::steady_clock::now() - start < 200ms);
|
||||
}
|
||||
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
size_t t_runs = 0;
|
||||
auto t = node->create_wall_timer(
|
||||
10ms,
|
||||
[&]() {
|
||||
t_runs++;
|
||||
});
|
||||
|
||||
// Sleep some time for the timer to be ready when spin
|
||||
std::this_thread::sleep_for(10ms);
|
||||
|
||||
EventsExecutor executor;
|
||||
executor.add_node(node);
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
executor.spin_all(10s);
|
||||
|
||||
EXPECT_EQ(1u, t_runs);
|
||||
EXPECT_TRUE(std::chrono::steady_clock::now() - start < 200ms);
|
||||
}
|
||||
|
||||
EventsExecutor executor;
|
||||
EXPECT_THROW(executor.spin_all(0ms), std::invalid_argument);
|
||||
EXPECT_THROW(executor.spin_all(-5ms), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_F(TestEventsExecutor, cancel_while_timers_running)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
EventsExecutor executor;
|
||||
executor.add_node(node);
|
||||
|
||||
// Take care of previous events for a fresh start
|
||||
executor.spin_all(1s);
|
||||
|
||||
size_t t1_runs = 0;
|
||||
auto t1 = node->create_wall_timer(
|
||||
1ms,
|
||||
[&]() {
|
||||
t1_runs++;
|
||||
std::this_thread::sleep_for(50ms);
|
||||
});
|
||||
|
||||
size_t t2_runs = 0;
|
||||
auto t2 = node->create_wall_timer(
|
||||
1ms,
|
||||
[&]() {
|
||||
t2_runs++;
|
||||
std::this_thread::sleep_for(50ms);
|
||||
});
|
||||
|
||||
|
||||
std::thread spinner([&executor, this]() {executor.spin();});
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
// Call cancel while t1 callback is still being executed
|
||||
executor.cancel();
|
||||
spinner.join();
|
||||
|
||||
// Depending on the latency on the system, t2 may start to execute before cancel is signaled
|
||||
EXPECT_GE(1u, t1_runs);
|
||||
EXPECT_GE(1u, t2_runs);
|
||||
}
|
||||
|
||||
TEST_F(TestEventsExecutor, cancel_while_timers_waiting)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
size_t t1_runs = 0;
|
||||
auto t1 = node->create_wall_timer(
|
||||
100s,
|
||||
[&]() {
|
||||
t1_runs++;
|
||||
});
|
||||
|
||||
EventsExecutor executor;
|
||||
executor.add_node(node);
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
std::thread spinner([&executor, this]() {executor.spin();});
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
executor.cancel();
|
||||
spinner.join();
|
||||
|
||||
EXPECT_EQ(0u, t1_runs);
|
||||
EXPECT_TRUE(std::chrono::steady_clock::now() - start < 1s);
|
||||
}
|
||||
|
||||
TEST_F(TestEventsExecutor, destroy_entities)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
// Create a publisher node and start publishing messages
|
||||
auto node_pub = std::make_shared<rclcpp::Node>("node_pub");
|
||||
auto publisher = node_pub->create_publisher<test_msgs::msg::Empty>("topic", rclcpp::QoS(10));
|
||||
auto timer = node_pub->create_wall_timer(
|
||||
2ms, [&]() {publisher->publish(std::make_unique<test_msgs::msg::Empty>());});
|
||||
EventsExecutor executor_pub;
|
||||
executor_pub.add_node(node_pub);
|
||||
std::thread spinner([&executor_pub, this]() {executor_pub.spin();});
|
||||
|
||||
// Create a node with two different subscriptions to the topic
|
||||
auto node_sub = std::make_shared<rclcpp::Node>("node_sub");
|
||||
size_t callback_count_1 = 0;
|
||||
auto subscription_1 =
|
||||
node_sub->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", rclcpp::QoS(10), [&](test_msgs::msg::Empty::ConstSharedPtr) {callback_count_1++;});
|
||||
size_t callback_count_2 = 0;
|
||||
auto subscription_2 =
|
||||
node_sub->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", rclcpp::QoS(10), [&](test_msgs::msg::Empty::ConstSharedPtr) {callback_count_2++;});
|
||||
EventsExecutor executor_sub;
|
||||
executor_sub.add_node(node_sub);
|
||||
|
||||
// Wait some time while messages are published
|
||||
std::this_thread::sleep_for(10ms);
|
||||
|
||||
// Destroy one of the two subscriptions
|
||||
subscription_1.reset();
|
||||
|
||||
// Let subscriptions executor spin
|
||||
executor_sub.spin_some(10ms);
|
||||
|
||||
// The callback count of the destroyed subscription remained at 0
|
||||
EXPECT_EQ(0u, callback_count_1);
|
||||
EXPECT_LT(0u, callback_count_2);
|
||||
|
||||
executor_pub.cancel();
|
||||
spinner.join();
|
||||
}
|
||||
|
||||
/*
|
||||
Testing construction of a subscriptions with QoS event callback functions.
|
||||
*/
|
||||
std::string * g_pub_log_msg;
|
||||
std::string * g_sub_log_msg;
|
||||
std::promise<void> * g_log_msgs_promise;
|
||||
TEST_F(TestEventsExecutor, test_default_incompatible_qos_callbacks)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
rcutils_logging_output_handler_t original_output_handler = rcutils_logging_get_output_handler();
|
||||
|
||||
std::string pub_log_msg;
|
||||
std::string sub_log_msg;
|
||||
std::promise<void> log_msgs_promise;
|
||||
g_pub_log_msg = &pub_log_msg;
|
||||
g_sub_log_msg = &sub_log_msg;
|
||||
g_log_msgs_promise = &log_msgs_promise;
|
||||
auto logger_callback = [](
|
||||
const rcutils_log_location_t * /*location*/,
|
||||
int /*level*/, const char * /*name*/, rcutils_time_point_value_t /*timestamp*/,
|
||||
const char * format, va_list * args) -> void {
|
||||
char buffer[1024];
|
||||
vsnprintf(buffer, sizeof(buffer), format, *args);
|
||||
const std::string msg = buffer;
|
||||
if (msg.rfind("New subscription discovered on topic '/test_topic'", 0) == 0) {
|
||||
*g_pub_log_msg = buffer;
|
||||
} else if (msg.rfind("New publisher discovered on topic '/test_topic'", 0) == 0) {
|
||||
*g_sub_log_msg = buffer;
|
||||
}
|
||||
|
||||
if (!g_pub_log_msg->empty() && !g_sub_log_msg->empty()) {
|
||||
g_log_msgs_promise->set_value();
|
||||
}
|
||||
};
|
||||
rcutils_logging_set_output_handler(logger_callback);
|
||||
|
||||
std::shared_future<void> log_msgs_future = log_msgs_promise.get_future();
|
||||
|
||||
rclcpp::QoS qos_profile_publisher(10);
|
||||
qos_profile_publisher.durability(RMW_QOS_POLICY_DURABILITY_VOLATILE);
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>(
|
||||
"test_topic", qos_profile_publisher);
|
||||
|
||||
rclcpp::QoS qos_profile_subscription(10);
|
||||
qos_profile_subscription.durability(RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL);
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"test_topic", qos_profile_subscription, [&](test_msgs::msg::Empty::ConstSharedPtr) {});
|
||||
|
||||
EventsExecutor ex;
|
||||
ex.add_node(node->get_node_base_interface());
|
||||
|
||||
const auto timeout = std::chrono::seconds(10);
|
||||
ex.spin_until_future_complete(log_msgs_future, timeout);
|
||||
|
||||
EXPECT_EQ(
|
||||
"New subscription discovered on topic '/test_topic', requesting incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
pub_log_msg);
|
||||
EXPECT_EQ(
|
||||
"New publisher discovered on topic '/test_topic', offering incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
sub_log_msg);
|
||||
|
||||
rcutils_logging_set_output_handler(original_output_handler);
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// Copyright 2023 iRobot Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/experimental/executors/events_executor/events_executor_event_types.hpp"
|
||||
#include "rclcpp/experimental/executors/events_executor/simple_events_queue.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
TEST(TestEventsQueue, SimpleQueueTest)
|
||||
{
|
||||
// Create a SimpleEventsQueue and a local queue
|
||||
auto simple_queue = std::make_unique<rclcpp::experimental::executors::SimpleEventsQueue>();
|
||||
rclcpp::experimental::executors::ExecutorEvent event {};
|
||||
bool ret = false;
|
||||
|
||||
// Make sure the queue is empty at startup
|
||||
EXPECT_TRUE(simple_queue->empty());
|
||||
EXPECT_EQ(simple_queue->size(), 0u);
|
||||
|
||||
// Push 11 messages
|
||||
for (uint32_t i = 1; i < 11; i++) {
|
||||
rclcpp::experimental::executors::ExecutorEvent stub_event {};
|
||||
stub_event.num_events = 1;
|
||||
simple_queue->enqueue(stub_event);
|
||||
|
||||
EXPECT_FALSE(simple_queue->empty());
|
||||
EXPECT_EQ(simple_queue->size(), i);
|
||||
}
|
||||
|
||||
// Pop one message
|
||||
ret = simple_queue->dequeue(event);
|
||||
EXPECT_TRUE(ret);
|
||||
EXPECT_FALSE(simple_queue->empty());
|
||||
EXPECT_EQ(simple_queue->size(), 9u);
|
||||
|
||||
// Pop one message
|
||||
ret = simple_queue->dequeue(event, std::chrono::nanoseconds(0));
|
||||
EXPECT_TRUE(ret);
|
||||
EXPECT_FALSE(simple_queue->empty());
|
||||
EXPECT_EQ(simple_queue->size(), 8u);
|
||||
|
||||
while (!simple_queue->empty()) {
|
||||
ret = simple_queue->dequeue(event);
|
||||
EXPECT_TRUE(ret);
|
||||
}
|
||||
|
||||
EXPECT_TRUE(simple_queue->empty());
|
||||
EXPECT_EQ(simple_queue->size(), 0u);
|
||||
|
||||
ret = simple_queue->dequeue(event, std::chrono::nanoseconds(0));
|
||||
EXPECT_FALSE(ret);
|
||||
|
||||
// Lets push an event into the queue and get it back
|
||||
rclcpp::experimental::executors::ExecutorEvent push_event = {
|
||||
simple_queue.get(),
|
||||
99,
|
||||
rclcpp::experimental::executors::ExecutorEventType::SUBSCRIPTION_EVENT,
|
||||
1};
|
||||
|
||||
simple_queue->enqueue(push_event);
|
||||
ret = simple_queue->dequeue(event);
|
||||
EXPECT_TRUE(ret);
|
||||
EXPECT_EQ(push_event.exec_entity_id, event.exec_entity_id);
|
||||
EXPECT_EQ(push_event.gen_entity_id, event.gen_entity_id);
|
||||
EXPECT_EQ(push_event.type, event.type);
|
||||
EXPECT_EQ(push_event.num_events, event.num_events);
|
||||
}
|
||||
@@ -92,8 +92,7 @@ using ExecutorTypes =
|
||||
::testing::Types<
|
||||
rclcpp::executors::SingleThreadedExecutor,
|
||||
rclcpp::executors::MultiThreadedExecutor,
|
||||
rclcpp::executors::StaticSingleThreadedExecutor,
|
||||
rclcpp::experimental::executors::EventsExecutor>;
|
||||
rclcpp::executors::StaticSingleThreadedExecutor>;
|
||||
|
||||
class ExecutorTypeNames
|
||||
{
|
||||
@@ -114,10 +113,6 @@ public:
|
||||
return "StaticSingleThreadedExecutor";
|
||||
}
|
||||
|
||||
if (std::is_same<T, rclcpp::experimental::executors::EventsExecutor>()) {
|
||||
return "EventsExecutor";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
};
|
||||
@@ -131,22 +126,12 @@ TYPED_TEST_SUITE(TestExecutors, ExecutorTypes, ExecutorTypeNames);
|
||||
using StandardExecutors =
|
||||
::testing::Types<
|
||||
rclcpp::executors::SingleThreadedExecutor,
|
||||
rclcpp::executors::MultiThreadedExecutor,
|
||||
rclcpp::experimental::executors::EventsExecutor>;
|
||||
rclcpp::executors::MultiThreadedExecutor>;
|
||||
TYPED_TEST_SUITE(TestExecutorsStable, StandardExecutors, ExecutorTypeNames);
|
||||
|
||||
// Make sure that executors detach from nodes when destructing
|
||||
TYPED_TEST(TestExecutors, detachOnDestruction)
|
||||
{
|
||||
TYPED_TEST(TestExecutors, detachOnDestruction) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
{
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
@@ -160,17 +145,8 @@ TYPED_TEST(TestExecutors, detachOnDestruction)
|
||||
// Make sure that the executor can automatically remove expired nodes correctly
|
||||
// Currently fails for StaticSingleThreadedExecutor so it is being skipped, see:
|
||||
// https://github.com/ros2/rclcpp/issues/1231
|
||||
TYPED_TEST(TestExecutorsStable, addTemporaryNode)
|
||||
{
|
||||
TYPED_TEST(TestExecutorsStable, addTemporaryNode) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
|
||||
{
|
||||
@@ -188,17 +164,8 @@ TYPED_TEST(TestExecutorsStable, addTemporaryNode)
|
||||
}
|
||||
|
||||
// Check executor throws properly if the same node is added a second time
|
||||
TYPED_TEST(TestExecutors, addNodeTwoExecutors)
|
||||
{
|
||||
TYPED_TEST(TestExecutors, addNodeTwoExecutors) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor1;
|
||||
ExecutorType executor2;
|
||||
EXPECT_NO_THROW(executor1.add_node(this->node));
|
||||
@@ -207,17 +174,8 @@ TYPED_TEST(TestExecutors, addNodeTwoExecutors)
|
||||
}
|
||||
|
||||
// Check simple spin example
|
||||
TYPED_TEST(TestExecutors, spinWithTimer)
|
||||
{
|
||||
TYPED_TEST(TestExecutors, spinWithTimer) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
|
||||
bool timer_completed = false;
|
||||
@@ -238,17 +196,8 @@ TYPED_TEST(TestExecutors, spinWithTimer)
|
||||
executor.remove_node(this->node, true);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutors, spinWhileAlreadySpinning)
|
||||
{
|
||||
TYPED_TEST(TestExecutors, spinWhileAlreadySpinning) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
|
||||
@@ -273,17 +222,8 @@ TYPED_TEST(TestExecutors, spinWhileAlreadySpinning)
|
||||
}
|
||||
|
||||
// Check executor exits immediately if future is complete.
|
||||
TYPED_TEST(TestExecutors, testSpinUntilFutureComplete)
|
||||
{
|
||||
TYPED_TEST(TestExecutors, testSpinUntilFutureComplete) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
|
||||
@@ -304,17 +244,8 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureComplete)
|
||||
}
|
||||
|
||||
// Same test, but uses a shared future.
|
||||
TYPED_TEST(TestExecutors, testSpinUntilSharedFutureComplete)
|
||||
{
|
||||
TYPED_TEST(TestExecutors, testSpinUntilSharedFutureComplete) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
|
||||
@@ -336,17 +267,8 @@ TYPED_TEST(TestExecutors, testSpinUntilSharedFutureComplete)
|
||||
}
|
||||
|
||||
// For a longer running future that should require several iterations of spin_once
|
||||
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteNoTimeout)
|
||||
{
|
||||
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteNoTimeout) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
|
||||
@@ -391,17 +313,8 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteNoTimeout)
|
||||
}
|
||||
|
||||
// Check spin_until_future_complete timeout works as expected
|
||||
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteWithTimeout)
|
||||
{
|
||||
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteWithTimeout) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
|
||||
@@ -467,13 +380,6 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
take_data_by_entity_id(size_t id) override
|
||||
{
|
||||
(void) id;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
execute(std::shared_ptr<void> & data) override
|
||||
{
|
||||
@@ -482,21 +388,6 @@ public:
|
||||
std::this_thread::sleep_for(3ms);
|
||||
}
|
||||
|
||||
void
|
||||
set_on_ready_callback(std::function<void(size_t, int)> callback) override
|
||||
{
|
||||
auto gc_callback = [callback](size_t count) {
|
||||
callback(count, 0);
|
||||
};
|
||||
gc_.set_on_trigger_callback(gc_callback);
|
||||
}
|
||||
|
||||
void
|
||||
clear_on_ready_callback() override
|
||||
{
|
||||
gc_.set_on_trigger_callback(nullptr);
|
||||
}
|
||||
|
||||
size_t
|
||||
get_number_of_ready_guard_conditions() override {return 1;}
|
||||
|
||||
@@ -511,17 +402,8 @@ private:
|
||||
rclcpp::GuardCondition gc_;
|
||||
};
|
||||
|
||||
TYPED_TEST(TestExecutors, spinAll)
|
||||
{
|
||||
TYPED_TEST(TestExecutors, spinAll) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
auto waitable_interfaces = this->node->get_node_waitables_interface();
|
||||
auto my_waitable = std::make_shared<TestWaitable>();
|
||||
@@ -561,17 +443,8 @@ TYPED_TEST(TestExecutors, spinAll)
|
||||
spinner.join();
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutors, spinSome)
|
||||
{
|
||||
TYPED_TEST(TestExecutors, spinSome) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
auto waitable_interfaces = this->node->get_node_waitables_interface();
|
||||
auto my_waitable = std::make_shared<TestWaitable>();
|
||||
@@ -610,17 +483,8 @@ TYPED_TEST(TestExecutors, spinSome)
|
||||
}
|
||||
|
||||
// Check spin_node_until_future_complete with node base pointer
|
||||
TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodeBasePtr)
|
||||
{
|
||||
TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodeBasePtr) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
|
||||
std::promise<bool> promise;
|
||||
@@ -634,17 +498,8 @@ TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodeBasePtr)
|
||||
}
|
||||
|
||||
// Check spin_node_until_future_complete with node pointer
|
||||
TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodePtr)
|
||||
{
|
||||
TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodePtr) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
|
||||
std::promise<bool> promise;
|
||||
@@ -658,17 +513,8 @@ TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodePtr)
|
||||
}
|
||||
|
||||
// Check spin_until_future_complete can be properly interrupted.
|
||||
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted)
|
||||
{
|
||||
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
|
||||
@@ -710,8 +556,7 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted)
|
||||
}
|
||||
|
||||
// Check spin_until_future_complete with node base pointer (instantiates its own executor)
|
||||
TEST(TestExecutors, testSpinUntilFutureCompleteNodeBasePtr)
|
||||
{
|
||||
TEST(TestExecutors, testSpinUntilFutureCompleteNodeBasePtr) {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
{
|
||||
@@ -731,8 +576,7 @@ TEST(TestExecutors, testSpinUntilFutureCompleteNodeBasePtr)
|
||||
}
|
||||
|
||||
// Check spin_until_future_complete with node pointer (instantiates its own executor)
|
||||
TEST(TestExecutors, testSpinUntilFutureCompleteNodePtr)
|
||||
{
|
||||
TEST(TestExecutors, testSpinUntilFutureCompleteNodePtr) {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -46,15 +46,11 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class TestAddCallbackGroupsToExecutorStable : public TestAddCallbackGroupsToExecutor<T> {};
|
||||
|
||||
using ExecutorTypes =
|
||||
::testing::Types<
|
||||
rclcpp::executors::SingleThreadedExecutor,
|
||||
rclcpp::executors::MultiThreadedExecutor,
|
||||
rclcpp::executors::StaticSingleThreadedExecutor,
|
||||
rclcpp::experimental::executors::EventsExecutor>;
|
||||
rclcpp::executors::StaticSingleThreadedExecutor>;
|
||||
|
||||
class ExecutorTypeNames
|
||||
{
|
||||
@@ -75,38 +71,17 @@ public:
|
||||
return "StaticSingleThreadedExecutor";
|
||||
}
|
||||
|
||||
if (std::is_same<T, rclcpp::experimental::executors::EventsExecutor>()) {
|
||||
return "EventsExecutor";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutor, ExecutorTypes, ExecutorTypeNames);
|
||||
|
||||
// StaticSingleThreadedExecutor is not included in these tests for now
|
||||
using StandardExecutors =
|
||||
::testing::Types<
|
||||
rclcpp::executors::SingleThreadedExecutor,
|
||||
rclcpp::executors::MultiThreadedExecutor,
|
||||
rclcpp::experimental::executors::EventsExecutor>;
|
||||
TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutorStable, StandardExecutors, ExecutorTypeNames);
|
||||
|
||||
/*
|
||||
* Test adding callback groups.
|
||||
*/
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups)
|
||||
{
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto timer_callback = []() {};
|
||||
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(
|
||||
@@ -152,17 +127,8 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups)
|
||||
/*
|
||||
* Test removing callback groups.
|
||||
*/
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_groups)
|
||||
{
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_groups) {
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto timer_callback = []() {};
|
||||
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(
|
||||
@@ -192,16 +158,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_groups)
|
||||
*/
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_duplicate_callback_groups)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
rclcpp::executors::MultiThreadedExecutor executor;
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto timer_callback = []() {};
|
||||
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(
|
||||
@@ -219,16 +176,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_duplicate_callback_groups)
|
||||
*/
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups_after_add_node_to_executor)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
rclcpp::executors::MultiThreadedExecutor executor;
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
executor.add_node(node->get_node_base_interface());
|
||||
ASSERT_EQ(executor.get_all_callback_groups().size(), 1u);
|
||||
@@ -262,22 +210,13 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups_after_add_node_t
|
||||
*/
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_unallowable_callback_groups)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto timer_callback = []() {};
|
||||
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive, false);
|
||||
rclcpp::TimerBase::SharedPtr timer_ = node->create_wall_timer(
|
||||
2s, timer_callback, cb_grp);
|
||||
rclcpp::executors::MultiThreadedExecutor executor;
|
||||
executor.add_callback_group(cb_grp, node->get_node_base_interface());
|
||||
ASSERT_EQ(executor.get_all_callback_groups().size(), 1u);
|
||||
|
||||
@@ -306,23 +245,14 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_unallowable_callback_groups)
|
||||
*/
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, one_node_many_callback_groups_many_executors)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType timer_executor;
|
||||
ExecutorType sub_executor;
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto timer_callback = []() {};
|
||||
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive, false);
|
||||
rclcpp::TimerBase::SharedPtr timer_ = node->create_wall_timer(
|
||||
2s, timer_callback, cb_grp);
|
||||
rclcpp::executors::MultiThreadedExecutor timer_executor;
|
||||
rclcpp::executors::MultiThreadedExecutor sub_executor;
|
||||
timer_executor.add_callback_group(cb_grp, node->get_node_base_interface());
|
||||
const rclcpp::QoS qos(10);
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
@@ -352,23 +282,14 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, one_node_many_callback_groups_many_e
|
||||
* because the executor can't be triggered while a subscriber created, see
|
||||
* https://github.com/ros2/rclcpp/issues/1611
|
||||
*/
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutorStable, subscriber_triggered_to_receive_message)
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, subscriber_triggered_to_receive_message)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
|
||||
// create a thread running an executor with a new callback group for a coming subscriber
|
||||
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive, false);
|
||||
ExecutorType cb_grp_executor;
|
||||
rclcpp::executors::SingleThreadedExecutor cb_grp_executor;
|
||||
|
||||
std::promise<bool> received_message_promise;
|
||||
auto received_message_future = received_message_promise.get_future();
|
||||
@@ -408,7 +329,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutorStable, subscriber_triggered_to_receiv
|
||||
timer_promise.set_value();
|
||||
};
|
||||
|
||||
ExecutorType timer_executor;
|
||||
rclcpp::executors::SingleThreadedExecutor timer_executor;
|
||||
timer = node->create_wall_timer(100ms, timer_callback);
|
||||
timer_executor.add_node(node);
|
||||
auto future = timer_promise.get_future();
|
||||
@@ -425,17 +346,8 @@ TYPED_TEST(TestAddCallbackGroupsToExecutorStable, subscriber_triggered_to_receiv
|
||||
* because the executor can't be triggered while a subscriber created, see
|
||||
* https://github.com/ros2/rclcpp/issues/2067
|
||||
*/
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutorStable, callback_group_create_after_spin)
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, callback_group_create_after_spin)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
|
||||
// create a publisher to send data
|
||||
@@ -445,7 +357,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutorStable, callback_group_create_after_sp
|
||||
publisher->publish(test_msgs::msg::Empty());
|
||||
|
||||
// create a thread running an executor
|
||||
ExecutorType executor;
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
executor.add_node(node);
|
||||
std::promise<bool> received_message_promise;
|
||||
auto received_message_future = received_message_promise.get_future();
|
||||
@@ -480,16 +392,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutorStable, callback_group_create_after_sp
|
||||
*/
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_group)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
rclcpp::executors::MultiThreadedExecutor executor;
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto timer_callback = []() {};
|
||||
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(
|
||||
|
||||
@@ -89,9 +89,9 @@ public:
|
||||
T2 message;
|
||||
write_message(data, message);
|
||||
|
||||
rclcpp::Serialization<T2> ser;
|
||||
rclcpp::Serialization<T2> serialization_support;
|
||||
SerializedMessage result;
|
||||
ser.serialize_message(&message, &result);
|
||||
serialization_support.serialize_message(&message, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -1,426 +0,0 @@
|
||||
// Copyright 2023 iRobot Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/experimental/timers_manager.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
using rclcpp::experimental::TimersManager;
|
||||
|
||||
using CallbackT = std::function<void ()>;
|
||||
using TimerT = rclcpp::WallTimer<CallbackT>;
|
||||
|
||||
class TestTimersManager : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestTimersManager, empty_manager)
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
EXPECT_EQ(std::chrono::nanoseconds::max(), timers_manager->get_head_timeout());
|
||||
EXPECT_FALSE(timers_manager->execute_head_timer());
|
||||
EXPECT_NO_THROW(timers_manager->execute_ready_timers());
|
||||
EXPECT_NO_THROW(timers_manager->clear());
|
||||
EXPECT_NO_THROW(timers_manager->start());
|
||||
EXPECT_NO_THROW(timers_manager->stop());
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, add_run_remove_timer)
|
||||
{
|
||||
size_t t_runs = 0;
|
||||
auto t = TimerT::make_shared(
|
||||
1ms,
|
||||
[&t_runs]() {
|
||||
t_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
std::weak_ptr<TimerT> t_weak = t;
|
||||
|
||||
// Add the timer to the timers manager
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
timers_manager->add_timer(t);
|
||||
|
||||
// Sleep for more 3 times the timer period
|
||||
std::this_thread::sleep_for(3ms);
|
||||
|
||||
// The timer is executed only once, even if we slept 3 times the period
|
||||
timers_manager->execute_ready_timers();
|
||||
EXPECT_EQ(1u, t_runs);
|
||||
|
||||
// Remove the timer from the manager
|
||||
timers_manager->remove_timer(t);
|
||||
|
||||
t.reset();
|
||||
// The timer is now not valid anymore
|
||||
EXPECT_FALSE(t_weak.lock() != nullptr);
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, clear)
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
auto t1 = TimerT::make_shared(1ms, CallbackT(), rclcpp::contexts::get_global_default_context());
|
||||
std::weak_ptr<TimerT> t1_weak = t1;
|
||||
auto t2 = TimerT::make_shared(1ms, CallbackT(), rclcpp::contexts::get_global_default_context());
|
||||
std::weak_ptr<TimerT> t2_weak = t2;
|
||||
|
||||
timers_manager->add_timer(t1);
|
||||
timers_manager->add_timer(t2);
|
||||
|
||||
EXPECT_TRUE(t1_weak.lock() != nullptr);
|
||||
EXPECT_TRUE(t2_weak.lock() != nullptr);
|
||||
|
||||
timers_manager->clear();
|
||||
|
||||
t1.reset();
|
||||
t2.reset();
|
||||
|
||||
EXPECT_FALSE(t1_weak.lock() != nullptr);
|
||||
EXPECT_FALSE(t2_weak.lock() != nullptr);
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, remove_not_existing_timer)
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
// Try to remove a nullptr timer
|
||||
EXPECT_NO_THROW(timers_manager->remove_timer(nullptr));
|
||||
|
||||
auto t = TimerT::make_shared(1ms, CallbackT(), rclcpp::contexts::get_global_default_context());
|
||||
timers_manager->add_timer(t);
|
||||
|
||||
// Remove twice the same timer
|
||||
timers_manager->remove_timer(t);
|
||||
EXPECT_NO_THROW(timers_manager->remove_timer(t));
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, timers_thread_exclusive_usage)
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
timers_manager->start();
|
||||
|
||||
EXPECT_THROW(timers_manager->start(), std::exception);
|
||||
EXPECT_THROW(timers_manager->get_head_timeout(), std::exception);
|
||||
EXPECT_THROW(timers_manager->execute_ready_timers(), std::exception);
|
||||
EXPECT_THROW(timers_manager->execute_head_timer(), std::exception);
|
||||
|
||||
timers_manager->stop();
|
||||
|
||||
EXPECT_NO_THROW(timers_manager->get_head_timeout());
|
||||
EXPECT_NO_THROW(timers_manager->execute_ready_timers());
|
||||
EXPECT_NO_THROW(timers_manager->execute_head_timer());
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, add_timer_twice)
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
auto t = TimerT::make_shared(1ms, CallbackT(), rclcpp::contexts::get_global_default_context());
|
||||
|
||||
timers_manager->add_timer(t);
|
||||
EXPECT_NO_THROW(timers_manager->add_timer(t));
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, add_nullptr)
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
EXPECT_THROW(timers_manager->add_timer(nullptr), std::exception);
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, head_not_ready)
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t_runs = 0;
|
||||
auto t = TimerT::make_shared(
|
||||
10s,
|
||||
[&t_runs]() {
|
||||
t_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
timers_manager->add_timer(t);
|
||||
|
||||
// Timer will take 10s to get ready, so nothing to execute here
|
||||
bool ret = timers_manager->execute_head_timer();
|
||||
EXPECT_FALSE(ret);
|
||||
EXPECT_EQ(0u, t_runs);
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, timers_order)
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t1_runs = 0;
|
||||
auto t1 = TimerT::make_shared(
|
||||
10ms,
|
||||
[&t1_runs]() {
|
||||
t1_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t2_runs = 0;
|
||||
auto t2 = TimerT::make_shared(
|
||||
30ms,
|
||||
[&t2_runs]() {
|
||||
t2_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t3_runs = 0;
|
||||
auto t3 = TimerT::make_shared(
|
||||
100ms,
|
||||
[&t3_runs]() {
|
||||
t3_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
// Add timers in a random order
|
||||
timers_manager->add_timer(t2);
|
||||
timers_manager->add_timer(t3);
|
||||
timers_manager->add_timer(t1);
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
timers_manager->execute_ready_timers();
|
||||
EXPECT_EQ(1u, t1_runs);
|
||||
EXPECT_EQ(0u, t2_runs);
|
||||
EXPECT_EQ(0u, t3_runs);
|
||||
|
||||
std::this_thread::sleep_for(30ms);
|
||||
timers_manager->execute_ready_timers();
|
||||
EXPECT_EQ(2u, t1_runs);
|
||||
EXPECT_EQ(1u, t2_runs);
|
||||
EXPECT_EQ(0u, t3_runs);
|
||||
|
||||
std::this_thread::sleep_for(100ms);
|
||||
timers_manager->execute_ready_timers();
|
||||
EXPECT_EQ(3u, t1_runs);
|
||||
EXPECT_EQ(2u, t2_runs);
|
||||
EXPECT_EQ(1u, t3_runs);
|
||||
|
||||
timers_manager->remove_timer(t1);
|
||||
|
||||
std::this_thread::sleep_for(30ms);
|
||||
timers_manager->execute_ready_timers();
|
||||
EXPECT_EQ(3u, t1_runs);
|
||||
EXPECT_EQ(3u, t2_runs);
|
||||
EXPECT_EQ(1u, t3_runs);
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, start_stop_timers_thread)
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
auto t = TimerT::make_shared(1ms, []() {}, rclcpp::contexts::get_global_default_context());
|
||||
timers_manager->add_timer(t);
|
||||
|
||||
// Calling start multiple times will throw an error
|
||||
EXPECT_NO_THROW(timers_manager->start());
|
||||
EXPECT_THROW(timers_manager->start(), std::exception);
|
||||
|
||||
// Calling stop multiple times does not throw an error
|
||||
EXPECT_NO_THROW(timers_manager->stop());
|
||||
EXPECT_NO_THROW(timers_manager->stop());
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, timers_thread)
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t1_runs = 0;
|
||||
auto t1 = TimerT::make_shared(
|
||||
1ms,
|
||||
[&t1_runs]() {
|
||||
t1_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t2_runs = 0;
|
||||
auto t2 = TimerT::make_shared(
|
||||
1ms,
|
||||
[&t2_runs]() {
|
||||
t2_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
// Add timers
|
||||
timers_manager->add_timer(t1);
|
||||
timers_manager->add_timer(t2);
|
||||
|
||||
// Run timers thread for a while
|
||||
timers_manager->start();
|
||||
std::this_thread::sleep_for(5ms);
|
||||
timers_manager->stop();
|
||||
|
||||
EXPECT_LT(1u, t1_runs);
|
||||
EXPECT_LT(1u, t2_runs);
|
||||
EXPECT_EQ(t1_runs, t2_runs);
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, destructor)
|
||||
{
|
||||
size_t t_runs = 0;
|
||||
auto t = TimerT::make_shared(
|
||||
1ms,
|
||||
[&t_runs]() {
|
||||
t_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
std::weak_ptr<TimerT> t_weak = t;
|
||||
|
||||
// When the timers manager is destroyed, it will stop the thread
|
||||
// and clear the timers
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
timers_manager->add_timer(t);
|
||||
|
||||
timers_manager->start();
|
||||
std::this_thread::sleep_for(100ms);
|
||||
|
||||
EXPECT_LT(1u, t_runs);
|
||||
}
|
||||
|
||||
// The thread is not running anymore, so this value does not increase
|
||||
size_t runs = t_runs;
|
||||
std::this_thread::sleep_for(100ms);
|
||||
EXPECT_EQ(runs, t_runs);
|
||||
t.reset();
|
||||
EXPECT_FALSE(t_weak.lock() != nullptr);
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, add_remove_while_thread_running)
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t1_runs = 0;
|
||||
auto t1 = TimerT::make_shared(
|
||||
1ms,
|
||||
[&t1_runs]() {
|
||||
t1_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t2_runs = 0;
|
||||
auto t2 = TimerT::make_shared(
|
||||
1ms,
|
||||
[&t2_runs]() {
|
||||
t2_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
// Add timers
|
||||
timers_manager->add_timer(t1);
|
||||
|
||||
// Start timers thread
|
||||
timers_manager->start();
|
||||
|
||||
// After a while remove t1 and add t2
|
||||
std::this_thread::sleep_for(5ms);
|
||||
timers_manager->remove_timer(t1);
|
||||
size_t tmp_t1 = t1_runs;
|
||||
timers_manager->add_timer(t2);
|
||||
|
||||
// Wait some more time and then stop
|
||||
std::this_thread::sleep_for(5ms);
|
||||
timers_manager->stop();
|
||||
|
||||
// t1 has stopped running
|
||||
EXPECT_EQ(tmp_t1, t1_runs);
|
||||
// t2 is correctly running
|
||||
EXPECT_LT(1u, t2_runs);
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, infinite_loop)
|
||||
{
|
||||
// This test makes sure that even if timers have a period shorter than the duration
|
||||
// of their callback the functions never block indefinitely.
|
||||
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t1_runs = 0;
|
||||
auto t1 = TimerT::make_shared(
|
||||
1ms,
|
||||
[&t1_runs]() {
|
||||
t1_runs++;
|
||||
std::this_thread::sleep_for(5ms);
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t2_runs = 0;
|
||||
auto t2 = TimerT::make_shared(
|
||||
1ms,
|
||||
[&t2_runs]() {
|
||||
t2_runs++;
|
||||
std::this_thread::sleep_for(5ms);
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
timers_manager->add_timer(t1);
|
||||
timers_manager->add_timer(t2);
|
||||
|
||||
// Sleep for enough time to trigger timers
|
||||
std::this_thread::sleep_for(3ms);
|
||||
timers_manager->execute_ready_timers();
|
||||
EXPECT_EQ(1u, t1_runs);
|
||||
EXPECT_EQ(1u, t2_runs);
|
||||
|
||||
// Due to the long execution of timer callbacks, timers are already ready
|
||||
bool ret = timers_manager->execute_head_timer();
|
||||
EXPECT_TRUE(ret);
|
||||
EXPECT_EQ(3u, t1_runs + t2_runs);
|
||||
|
||||
// Start a timers thread
|
||||
timers_manager->start();
|
||||
std::this_thread::sleep_for(10ms);
|
||||
timers_manager->stop();
|
||||
|
||||
EXPECT_LT(3u, t1_runs + t2_runs);
|
||||
EXPECT_LT(1u, t1_runs);
|
||||
EXPECT_LT(1u, t2_runs);
|
||||
}
|
||||
Reference in New Issue
Block a user