Compare commits
5 Commits
irobot/eve
...
runtime_in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4b03f9b7f | ||
|
|
9c1371d2c5 | ||
|
|
df964aa858 | ||
|
|
d10e6b279e | ||
|
|
bd9788a948 |
@@ -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_data.cpp
|
||||
src/rclcpp/dynamic_typesupport/dynamic_message_type_support.cpp
|
||||
src/rclcpp/dynamic_typesupport/dynamic_serialization_support.cpp
|
||||
src/rclcpp/dynamic_typesupport/dynamic_type.cpp
|
||||
src/rclcpp/dynamic_typesupport/dynamic_type_builder.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
|
||||
|
||||
@@ -0,0 +1,326 @@
|
||||
// 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_DATA_IMPL_HPP_
|
||||
#define RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_DATA_IMPL_HPP_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <rosidl_dynamic_typesupport/types.h>
|
||||
#include <rosidl_dynamic_typesupport/api/dynamic_data.h>
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
|
||||
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_DATA_HPP_
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_data.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
#define __DYNAMIC_DATA_GET_VALUE_BY_ID_FN(ValueT, FunctionT) \
|
||||
template<> \
|
||||
ValueT \
|
||||
DynamicData::get_value<ValueT>(rosidl_dynamic_typesupport_member_id_t id) \
|
||||
{ \
|
||||
ValueT out; \
|
||||
rosidl_dynamic_typesupport_dynamic_data_get_ ## FunctionT ## _value( \
|
||||
rosidl_dynamic_data_.get(), id, &out); \
|
||||
return out; \
|
||||
}
|
||||
|
||||
#define __DYNAMIC_DATA_GET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
|
||||
template<> \
|
||||
ValueT \
|
||||
DynamicData::get_value<ValueT>(const std::string & name) \
|
||||
{ \
|
||||
return get_value<ValueT>(get_member_id(name)); \
|
||||
}
|
||||
|
||||
#define __DYNAMIC_DATA_SET_VALUE_BY_ID_FN(ValueT, FunctionT) \
|
||||
template<> \
|
||||
void \
|
||||
DynamicData::set_value<ValueT>(rosidl_dynamic_typesupport_member_id_t id, ValueT value) \
|
||||
{ \
|
||||
rosidl_dynamic_typesupport_dynamic_data_set_ ## FunctionT ## _value( \
|
||||
rosidl_dynamic_data_.get(), id, value); \
|
||||
}
|
||||
|
||||
#define __DYNAMIC_DATA_SET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
|
||||
template<> \
|
||||
void \
|
||||
DynamicData::set_value<ValueT>(const std::string & name, ValueT value) \
|
||||
{ \
|
||||
set_value<ValueT>(get_member_id(name), value); \
|
||||
}
|
||||
|
||||
#define __DYNAMIC_DATA_INSERT_VALUE(ValueT, FunctionT) \
|
||||
template<> \
|
||||
rosidl_dynamic_typesupport_member_id_t \
|
||||
DynamicData::insert_value<ValueT>(ValueT value) \
|
||||
{ \
|
||||
rosidl_dynamic_typesupport_member_id_t out; \
|
||||
rosidl_dynamic_typesupport_dynamic_data_insert_ ## FunctionT ## _value( \
|
||||
rosidl_dynamic_data_.get(), value, &out); \
|
||||
return out; \
|
||||
}
|
||||
|
||||
#define DYNAMIC_DATA_DEFINITIONS(ValueT, FunctionT) \
|
||||
__DYNAMIC_DATA_GET_VALUE_BY_ID_FN(ValueT, FunctionT) \
|
||||
__DYNAMIC_DATA_GET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
|
||||
__DYNAMIC_DATA_SET_VALUE_BY_ID_FN(ValueT, FunctionT) \
|
||||
__DYNAMIC_DATA_SET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
|
||||
__DYNAMIC_DATA_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_DATA_DEFINITIONS(bool, bool);
|
||||
// DYNAMIC_DATA_DEFINITIONS(std::byte, byte);
|
||||
DYNAMIC_DATA_DEFINITIONS(char, char);
|
||||
DYNAMIC_DATA_DEFINITIONS(float, float32);
|
||||
DYNAMIC_DATA_DEFINITIONS(double, float64);
|
||||
DYNAMIC_DATA_DEFINITIONS(int8_t, int8);
|
||||
DYNAMIC_DATA_DEFINITIONS(int16_t, int16);
|
||||
DYNAMIC_DATA_DEFINITIONS(int32_t, int32);
|
||||
DYNAMIC_DATA_DEFINITIONS(int64_t, int64);
|
||||
DYNAMIC_DATA_DEFINITIONS(uint8_t, uint8);
|
||||
DYNAMIC_DATA_DEFINITIONS(uint16_t, uint16);
|
||||
DYNAMIC_DATA_DEFINITIONS(uint32_t, uint32);
|
||||
DYNAMIC_DATA_DEFINITIONS(uint64_t, uint64);
|
||||
// DYNAMIC_DATA_DEFINITIONS(std::string, std::string);
|
||||
// DYNAMIC_DATA_DEFINITIONS(std::u16string, std::u16string);
|
||||
|
||||
// Byte and String getters have a different implementation and are defined below
|
||||
|
||||
|
||||
// BYTE ============================================================================================
|
||||
template<>
|
||||
std::byte
|
||||
DynamicData::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
|
||||
DynamicData::get_value<std::byte>(const std::string & name)
|
||||
{
|
||||
return get_value<std::byte>(get_member_id(name));
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
void
|
||||
DynamicData::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_.get(), id, static_cast<unsigned char>(value));
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
void
|
||||
DynamicData::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
|
||||
DynamicData::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_.get(), static_cast<unsigned char>(value), &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// STRINGS =========================================================================================
|
||||
template<>
|
||||
std::string
|
||||
DynamicData::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);
|
||||
free(buf);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
std::u16string
|
||||
DynamicData::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);
|
||||
free(buf);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
std::string
|
||||
DynamicData::get_value<std::string>(const std::string & name)
|
||||
{
|
||||
return get_value<std::string>(get_member_id(name));
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
std::u16string
|
||||
DynamicData::get_value<std::u16string>(const std::string & name)
|
||||
{
|
||||
return get_value<std::u16string>(get_member_id(name));
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
void
|
||||
DynamicData::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_.get(), id, value.c_str(), value.size());
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
void
|
||||
DynamicData::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_.get(), id, value.c_str(), value.size());
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
void
|
||||
DynamicData::set_value<std::string>(const std::string & name, const std::string value)
|
||||
{
|
||||
set_value<std::string>(get_member_id(name), value);
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
void
|
||||
DynamicData::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
|
||||
DynamicData::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_.get(), value.c_str(), value.size(), &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicData::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_.get(), value.c_str(), value.size(), &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// THROW FOR UNSUPPORTED TYPES =====================================================================
|
||||
template<typename ValueT>
|
||||
ValueT
|
||||
DynamicData::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
|
||||
DynamicData::get_value(const std::string & name)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError("get_value is not implemented for input type");
|
||||
}
|
||||
|
||||
|
||||
template<typename ValueT>
|
||||
void
|
||||
DynamicData::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
|
||||
DynamicData::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
|
||||
DynamicData::insert_value(ValueT value)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError("insert_value is not implemented for input type");
|
||||
}
|
||||
|
||||
|
||||
} // namespace dynamic_typesupport
|
||||
} // namespace rclcpp
|
||||
|
||||
#undef __DYNAMIC_DATA_GET_VALUE_BY_ID_FN
|
||||
#undef __DYNAMIC_DATA_GET_VALUE_BY_NAME_FN
|
||||
#undef __DYNAMIC_DATA_SET_VALUE_BY_ID_FN
|
||||
#undef __DYNAMIC_DATA_SET_VALUE_BY_NAME_FN
|
||||
#undef __DYNAMIC_DATA_INSERT_VALUE
|
||||
#undef DYNAMIC_DATA_DEFINITIONS
|
||||
|
||||
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_DATA_IMPL_HPP_
|
||||
@@ -0,0 +1,162 @@
|
||||
// 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_TYPE_BUILDER_IMPL_HPP_
|
||||
#define RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_TYPE_BUILDER_IMPL_HPP_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <rosidl_dynamic_typesupport/types.h>
|
||||
#include <rosidl_dynamic_typesupport/api/dynamic_type.h>
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
|
||||
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_TYPE_BUILDER_HPP_
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_type_builder.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
#define __DYNAMIC_TYPE_BUILDER_ADD_MEMBER_FN(MemberT, FunctionT) \
|
||||
template<> \
|
||||
void \
|
||||
DynamicTypeBuilder::add_member<MemberT>( \
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name) \
|
||||
{ \
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## _member( \
|
||||
rosidl_dynamic_type_builder_.get(), id, name.c_str(), name.size()); \
|
||||
}
|
||||
|
||||
#define __DYNAMIC_TYPE_BUILDER_ADD_ARRAY_MEMBER_FN(MemberT, FunctionT) \
|
||||
template<> \
|
||||
void \
|
||||
DynamicTypeBuilder::add_array_member<MemberT>( \
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t array_length) \
|
||||
{ \
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## _array_member( \
|
||||
rosidl_dynamic_type_builder_.get(), id, name.c_str(), name.size(), array_length); \
|
||||
}
|
||||
|
||||
#define __DYNAMIC_TYPE_BUILDER_ADD_UNBOUNDED_SEQUENCE_MEMBER_FN(MemberT, FunctionT) \
|
||||
template<> \
|
||||
void \
|
||||
DynamicTypeBuilder::add_unbounded_sequence_member<MemberT>( \
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name) \
|
||||
{ \
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## _unbounded_sequence_member( \
|
||||
rosidl_dynamic_type_builder_.get(), id, name.c_str(), name.size()); \
|
||||
}
|
||||
|
||||
#define __DYNAMIC_TYPE_BUILDER_ADD_BOUNDED_SEQUENCE_MEMBER_FN(MemberT, FunctionT) \
|
||||
template<> \
|
||||
void \
|
||||
DynamicTypeBuilder::add_bounded_sequence_member<MemberT>( \
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t sequence_bound) \
|
||||
{ \
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## _bounded_sequence_member( \
|
||||
rosidl_dynamic_type_builder_.get(), id, name.c_str(), name.size(), sequence_bound); \
|
||||
}
|
||||
|
||||
#define DYNAMIC_TYPE_BUILDER_DEFINITIONS(MemberT, FunctionT) \
|
||||
__DYNAMIC_TYPE_BUILDER_ADD_MEMBER_FN(MemberT, FunctionT) \
|
||||
__DYNAMIC_TYPE_BUILDER_ADD_ARRAY_MEMBER_FN(MemberT, FunctionT) \
|
||||
__DYNAMIC_TYPE_BUILDER_ADD_UNBOUNDED_SEQUENCE_MEMBER_FN(MemberT, FunctionT) \
|
||||
__DYNAMIC_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_TYPE_BUILDER_DEFINITIONS(bool, bool);
|
||||
DYNAMIC_TYPE_BUILDER_DEFINITIONS(std::byte, byte);
|
||||
DYNAMIC_TYPE_BUILDER_DEFINITIONS(char, char);
|
||||
DYNAMIC_TYPE_BUILDER_DEFINITIONS(float, float32);
|
||||
DYNAMIC_TYPE_BUILDER_DEFINITIONS(double, float64);
|
||||
DYNAMIC_TYPE_BUILDER_DEFINITIONS(int8_t, int8);
|
||||
DYNAMIC_TYPE_BUILDER_DEFINITIONS(int16_t, int16);
|
||||
DYNAMIC_TYPE_BUILDER_DEFINITIONS(int32_t, int32);
|
||||
DYNAMIC_TYPE_BUILDER_DEFINITIONS(int64_t, int64);
|
||||
DYNAMIC_TYPE_BUILDER_DEFINITIONS(uint8_t, uint8);
|
||||
DYNAMIC_TYPE_BUILDER_DEFINITIONS(uint16_t, uint16);
|
||||
DYNAMIC_TYPE_BUILDER_DEFINITIONS(uint32_t, uint32);
|
||||
DYNAMIC_TYPE_BUILDER_DEFINITIONS(uint64_t, uint64);
|
||||
DYNAMIC_TYPE_BUILDER_DEFINITIONS(std::string, string);
|
||||
DYNAMIC_TYPE_BUILDER_DEFINITIONS(std::u16string, wstring);
|
||||
|
||||
|
||||
// THROW FOR UNSUPPORTED TYPES =====================================================================
|
||||
template<typename MemberT>
|
||||
void
|
||||
DynamicTypeBuilder::add_member(rosidl_dynamic_typesupport_member_id_t id, const std::string & name)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"add_member is not implemented for input type");
|
||||
}
|
||||
|
||||
|
||||
template<typename MemberT>
|
||||
void
|
||||
DynamicTypeBuilder::add_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t array_length)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"add_array_member is not implemented for input type");
|
||||
}
|
||||
|
||||
|
||||
template<typename MemberT>
|
||||
void
|
||||
DynamicTypeBuilder::add_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"add_unbounded_sequence_member is not implemented for input type");
|
||||
}
|
||||
|
||||
|
||||
template<typename MemberT>
|
||||
void
|
||||
DynamicTypeBuilder::add_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t sequence_bound)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"add_bounded_sequence_member is not implemented for input type");
|
||||
}
|
||||
|
||||
|
||||
} // namespace dynamic_typesupport
|
||||
} // namespace rclcpp
|
||||
|
||||
#undef __DYNAMIC_TYPE_BUILDER_ADD_MEMBER_FN
|
||||
#undef __DYNAMIC_TYPE_BUILDER_ADD_ARRAY_MEMBER_FN
|
||||
#undef __DYNAMIC_TYPE_BUILDER_ADD_UNBOUNDED_SEQUENCE_MEMBER_FN
|
||||
#undef __DYNAMIC_TYPE_BUILDER_ADD_BOUNDED_SEQUENCE_MEMBER_FN
|
||||
#undef DYNAMIC_TYPE_BUILDER_DEFINITIONS
|
||||
|
||||
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_TYPE_BUILDER_IMPL_HPP_
|
||||
374
rclcpp/include/rclcpp/dynamic_typesupport/dynamic_data.hpp
Normal file
374
rclcpp/include/rclcpp/dynamic_typesupport/dynamic_data.hpp
Normal file
@@ -0,0 +1,374 @@
|
||||
// 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_DATA_HPP_
|
||||
#define RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_DATA_HPP_
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include <rcl/types.h>
|
||||
#include <rosidl_dynamic_typesupport/types.h>
|
||||
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace dynamic_typesupport
|
||||
{
|
||||
|
||||
|
||||
class DynamicType;
|
||||
class DynamicTypeBuilder;
|
||||
|
||||
/// Utility wrapper class for rosidl_dynamic_typesupport_dynamic_data_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!
|
||||
*/
|
||||
class DynamicData : public std::enable_shared_from_this<DynamicData>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicData)
|
||||
|
||||
// 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 DynamicData with the provided dynamic type builder
|
||||
RCLCPP_PUBLIC
|
||||
explicit DynamicData(std::shared_ptr<DynamicTypeBuilder> dynamic_type_builder);
|
||||
|
||||
/// Construct a new DynamicData with the provided dynamic type
|
||||
RCLCPP_PUBLIC
|
||||
explicit DynamicData(std::shared_ptr<DynamicType> dynamic_type);
|
||||
|
||||
/// Assume ownership of raw pointer
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
rosidl_dynamic_typesupport_dynamic_data_t * rosidl_dynamic_data);
|
||||
|
||||
/// Copy shared pointer
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_dynamic_data_t> rosidl_dynamic_data);
|
||||
|
||||
/// Loaning constructor
|
||||
/// Must only be called with raw ptr obtained from loaning!
|
||||
// NOTE(methylDragon): I'd put this in protected, but I need this exposed to
|
||||
// enable_shared_from_this...
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData(
|
||||
DynamicData::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
|
||||
|
||||
/// Copy constructor
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData(const DynamicData & other);
|
||||
|
||||
/// Move constructor
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData(DynamicData && other) noexcept;
|
||||
|
||||
/// Copy assignment
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData & operator=(const DynamicData & other);
|
||||
|
||||
/// Move assignment
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData & operator=(DynamicData && other) noexcept;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~DynamicData();
|
||||
|
||||
|
||||
// GETTERS =======================================================================================
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_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
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_dynamic_data_t>
|
||||
get_shared_rosidl_dynamic_data();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<const rosidl_dynamic_typesupport_dynamic_data_t>
|
||||
get_shared_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
|
||||
DynamicData
|
||||
clone() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData::SharedPtr
|
||||
clone_shared() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
equals(const DynamicData & other) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData::SharedPtr
|
||||
loan_value(rosidl_dynamic_typesupport_member_id_t id);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData::SharedPtr
|
||||
loan_value(const std::string & name);
|
||||
|
||||
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
|
||||
void
|
||||
print() const;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
// 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
|
||||
DynamicData
|
||||
get_complex_value(rosidl_dynamic_typesupport_member_id_t id);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData
|
||||
get_complex_value(const std::string & name);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData::SharedPtr
|
||||
get_complex_value_shared(rosidl_dynamic_typesupport_member_id_t id);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData::SharedPtr
|
||||
get_complex_value_shared(const std::string & name);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_complex_value(rosidl_dynamic_typesupport_member_id_t id, DynamicData & value);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_complex_value(const std::string & name, DynamicData & value);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
insert_complex_value_copy(const DynamicData & value);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
insert_complex_value(DynamicData & 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_;
|
||||
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_dynamic_data_t> rosidl_dynamic_data_;
|
||||
|
||||
bool is_loaned_;
|
||||
DynamicData::SharedPtr parent_data_; // Used for returning the loaned value, and lifetime management
|
||||
|
||||
private:
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData();
|
||||
|
||||
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_DATA_HPP_
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2023 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_HPP_
|
||||
#define RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_HPP_
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_data.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_type.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_type_builder.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include <rosidl_dynamic_typesupport/types.h>
|
||||
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace dynamic_typesupport
|
||||
{
|
||||
|
||||
|
||||
// NOTE(methylDragon): We just alias the type in this case...
|
||||
// I'd have made a wrapper class but then I'd need to redirect every single
|
||||
// method (or dynamic cast everywhere else), so.. no thanks.
|
||||
using DynamicMessage = DynamicData;
|
||||
|
||||
} // namespace dynamic_typesupport
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_HPP_
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_type.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_type_builder.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include <rosidl_dynamic_typesupport/types.h>
|
||||
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace dynamic_typesupport
|
||||
{
|
||||
|
||||
|
||||
// NOTE(methylDragon): We just alias the type in this case...
|
||||
// I'd have made a wrapper class but then I'd need to redirect every single
|
||||
// method (or dynamic cast everywhere else), so.. no thanks.
|
||||
using DynamicMessageType = DynamicType;
|
||||
|
||||
|
||||
} // namespace dynamic_typesupport
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_HPP_
|
||||
@@ -0,0 +1,195 @@
|
||||
// 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 <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_message_type.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#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>
|
||||
|
||||
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_get_dynamic_message_typesupport_handle,
|
||||
* because this class will manage the lifetimes for you.
|
||||
*
|
||||
* Do NOT call rcl_dynamic_message_typesupport_handle_fini!!
|
||||
*
|
||||
* This class:
|
||||
* - Manages the lifetime of the raw pointer.
|
||||
* - Exposes getter methods to get the raw pointer and shared pointers
|
||||
* - 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
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeSupport(
|
||||
rosidl_runtime_c__type_description__TypeDescription * description,
|
||||
const std::string & serialization_library_name = "");
|
||||
|
||||
/// From description, for provided serialization support
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeSupport(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
rosidl_runtime_c__type_description__TypeDescription * description);
|
||||
|
||||
/// Assume ownership of managed types
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeSupport(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
DynamicMessageType::SharedPtr dynamic_message_type,
|
||||
DynamicMessage::SharedPtr dynamic_message,
|
||||
rosidl_runtime_c__type_description__TypeDescription * description = nullptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~DynamicMessageTypeSupport();
|
||||
|
||||
|
||||
// GETTERS =======================================================================================
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_library_identifier() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_message_type_support_t *
|
||||
get_rosidl_message_type_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_message_type_support_t *
|
||||
get_rosidl_message_type_support() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<rosidl_message_type_support_t>
|
||||
get_shared_rosidl_message_type_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<const rosidl_message_type_support_t>
|
||||
get_shared_rosidl_message_type_support() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_runtime_c__type_description__TypeDescription *
|
||||
get_rosidl_runtime_c_type_description();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_runtime_c__type_description__TypeDescription *
|
||||
get_rosidl_runtime_c_type_description() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<rosidl_runtime_c__type_description__TypeDescription>
|
||||
get_shared_rosidl_runtime_c_type_description();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<const rosidl_runtime_c__type_description__TypeDescription>
|
||||
get_shared_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;
|
||||
|
||||
|
||||
// METHODS =======================================================================================
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
print_description() const;
|
||||
|
||||
protected:
|
||||
RCLCPP_DISABLE_COPY(DynamicMessageTypeSupport)
|
||||
|
||||
DynamicSerializationSupport::SharedPtr serialization_support_;
|
||||
DynamicMessageType::SharedPtr dynamic_message_type_;
|
||||
DynamicMessage::SharedPtr dynamic_message_;
|
||||
std::shared_ptr<rosidl_runtime_c__type_description__TypeDescription> description_;
|
||||
|
||||
std::shared_ptr<rosidl_message_type_support_t> rosidl_message_type_support_;
|
||||
|
||||
private:
|
||||
RCLCPP_PUBLIC
|
||||
DynamicMessageTypeSupport();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init_serialization_support_(const std::string & serialization_library_name);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init_dynamic_message_type_(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const rosidl_runtime_c__type_description__TypeDescription * description);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init_dynamic_message_(DynamicType::SharedPtr dynamic_type);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init_rosidl_message_type_support_(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
DynamicMessageType::SharedPtr dynamic_message_type,
|
||||
DynamicMessage::SharedPtr dynamic_message,
|
||||
rosidl_runtime_c__type_description__TypeDescription * description);
|
||||
};
|
||||
|
||||
|
||||
} // namespace dynamic_typesupport
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_SUPPORT_HPP_
|
||||
@@ -0,0 +1,110 @@
|
||||
// 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 <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include <rosidl_dynamic_typesupport/types.h>
|
||||
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace dynamic_typesupport
|
||||
{
|
||||
|
||||
/// Utility wrapper class for rosidl_dynamic_typesupport_serialization_support_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, 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 ==================================================================================
|
||||
/// Get the rmw middleware implementation specific serialization support (configured by name)
|
||||
RCLCPP_PUBLIC
|
||||
explicit DynamicSerializationSupport(const std::string & serialization_library_name = "");
|
||||
|
||||
/// Assume ownership of raw pointer
|
||||
RCLCPP_PUBLIC
|
||||
explicit DynamicSerializationSupport(
|
||||
rosidl_dynamic_typesupport_serialization_support_t * rosidl_serialization_support);
|
||||
|
||||
/// Copy shared pointer
|
||||
RCLCPP_PUBLIC
|
||||
DynamicSerializationSupport(
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_serialization_support_t> 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_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;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_serialization_support_t>
|
||||
get_shared_rosidl_serialization_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<const rosidl_dynamic_typesupport_serialization_support_t>
|
||||
get_shared_rosidl_serialization_support() const;
|
||||
|
||||
protected:
|
||||
RCLCPP_DISABLE_COPY(DynamicSerializationSupport)
|
||||
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_serialization_support_t> rosidl_serialization_support_;
|
||||
|
||||
private:
|
||||
RCLCPP_PUBLIC
|
||||
DynamicSerializationSupport();
|
||||
};
|
||||
|
||||
|
||||
} // namespace dynamic_typesupport
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_SERIALIZATION_SUPPORT_HPP_
|
||||
196
rclcpp/include/rclcpp/dynamic_typesupport/dynamic_type.hpp
Normal file
196
rclcpp/include/rclcpp/dynamic_typesupport/dynamic_type.hpp
Normal file
@@ -0,0 +1,196 @@
|
||||
// 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_TYPE_HPP_
|
||||
#define RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_TYPE_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include <rosidl_dynamic_typesupport/types.h>
|
||||
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace dynamic_typesupport
|
||||
{
|
||||
|
||||
|
||||
class DynamicData;
|
||||
class DynamicTypeBuilder;
|
||||
|
||||
/// Utility wrapper class for rosidl_dynamic_typesupport_dynamic_type_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!
|
||||
*/
|
||||
class DynamicType : public std::enable_shared_from_this<DynamicType>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicType)
|
||||
|
||||
// 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 DynamicType with the provided dynamic type builder
|
||||
RCLCPP_PUBLIC
|
||||
explicit DynamicType(std::shared_ptr<DynamicTypeBuilder> dynamic_type_builder);
|
||||
|
||||
/// Assume ownership of raw pointer
|
||||
RCLCPP_PUBLIC
|
||||
DynamicType(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
rosidl_dynamic_typesupport_dynamic_type_t * rosidl_dynamic_type);
|
||||
|
||||
/// Copy shared pointer
|
||||
RCLCPP_PUBLIC
|
||||
DynamicType(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_dynamic_type_t> rosidl_dynamic_type);
|
||||
|
||||
/// From description
|
||||
RCLCPP_PUBLIC
|
||||
DynamicType(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const rosidl_runtime_c__type_description__TypeDescription * description);
|
||||
|
||||
/// Copy constructor
|
||||
RCLCPP_PUBLIC
|
||||
DynamicType(const DynamicType & other);
|
||||
|
||||
/// Move constructor
|
||||
RCLCPP_PUBLIC
|
||||
DynamicType(DynamicType && other) noexcept;
|
||||
|
||||
/// Copy assignment
|
||||
RCLCPP_PUBLIC
|
||||
DynamicType & operator=(const DynamicType & other);
|
||||
|
||||
/// Move assignment
|
||||
RCLCPP_PUBLIC
|
||||
DynamicType & operator=(DynamicType && other) noexcept;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~DynamicType();
|
||||
|
||||
/// Swaps the serialization support if serialization_support is populated
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init_from_description(
|
||||
const rosidl_runtime_c__type_description__TypeDescription * description,
|
||||
DynamicSerializationSupport::SharedPtr serialization_support = nullptr);
|
||||
|
||||
// GETTERS =======================================================================================
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_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
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_dynamic_type_t>
|
||||
get_shared_rosidl_dynamic_type();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<const rosidl_dynamic_typesupport_dynamic_type_t>
|
||||
get_shared_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
|
||||
DynamicType
|
||||
clone() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicType::SharedPtr
|
||||
clone_shared() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
equals(const DynamicType & other) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData
|
||||
build_data();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<DynamicData>
|
||||
build_data_shared();
|
||||
|
||||
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_;
|
||||
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_dynamic_type_t> rosidl_dynamic_type_;
|
||||
|
||||
private:
|
||||
RCLCPP_PUBLIC
|
||||
DynamicType();
|
||||
|
||||
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_TYPE_HPP_
|
||||
@@ -0,0 +1,342 @@
|
||||
// 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_TYPE_BUILDER_HPP_
|
||||
#define RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_TYPE_BUILDER_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include <rosidl_dynamic_typesupport/types.h>
|
||||
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace dynamic_typesupport
|
||||
{
|
||||
|
||||
class DynamicData;
|
||||
class DynamicType;
|
||||
|
||||
/// 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!
|
||||
*/
|
||||
class DynamicTypeBuilder : public std::enable_shared_from_this<DynamicTypeBuilder>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicTypeBuilder)
|
||||
|
||||
// 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 DynamicTypeBuilder with the provided serialization support
|
||||
RCLCPP_PUBLIC
|
||||
DynamicTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const std::string & name);
|
||||
|
||||
/// Assume ownership of raw pointer
|
||||
RCLCPP_PUBLIC
|
||||
DynamicTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_t * dynamic_type_builder);
|
||||
|
||||
/// Copy shared pointer
|
||||
RCLCPP_PUBLIC
|
||||
DynamicTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_dynamic_type_builder_t> dynamic_type_builder);
|
||||
|
||||
/// Copy constructor
|
||||
RCLCPP_PUBLIC
|
||||
DynamicTypeBuilder(const DynamicTypeBuilder & other);
|
||||
|
||||
/// Move constructor
|
||||
RCLCPP_PUBLIC
|
||||
DynamicTypeBuilder(DynamicTypeBuilder && other) noexcept;
|
||||
|
||||
/// Copy assignment
|
||||
RCLCPP_PUBLIC
|
||||
DynamicTypeBuilder & operator=(const DynamicTypeBuilder & other);
|
||||
|
||||
/// Move assignment
|
||||
RCLCPP_PUBLIC
|
||||
DynamicTypeBuilder & operator=(DynamicTypeBuilder && other) noexcept;
|
||||
|
||||
/// From description
|
||||
RCLCPP_PUBLIC
|
||||
DynamicTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const rosidl_runtime_c__type_description__TypeDescription * description);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~DynamicTypeBuilder();
|
||||
|
||||
/// Swaps the serialization support if serialization_support is populated
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init_from_description(
|
||||
const rosidl_runtime_c__type_description__TypeDescription * description,
|
||||
DynamicSerializationSupport::SharedPtr serialization_support = nullptr);
|
||||
|
||||
|
||||
// GETTERS =======================================================================================
|
||||
RCLCPP_PUBLIC
|
||||
const std::string
|
||||
get_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
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_dynamic_type_builder_t>
|
||||
get_shared_rosidl_dynamic_type_builder();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<const rosidl_dynamic_typesupport_dynamic_type_builder_t>
|
||||
get_shared_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
|
||||
DynamicTypeBuilder
|
||||
clone() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicTypeBuilder::SharedPtr
|
||||
clone_shared() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
clear();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicData
|
||||
build_data();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<DynamicData>
|
||||
build_data_shared();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DynamicType
|
||||
build_type();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<DynamicType>
|
||||
build_type_shared();
|
||||
|
||||
|
||||
// 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);
|
||||
|
||||
template<typename MemberT>
|
||||
void
|
||||
add_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t array_length);
|
||||
|
||||
template<typename MemberT>
|
||||
void
|
||||
add_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name);
|
||||
|
||||
template<typename MemberT>
|
||||
void
|
||||
add_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t sequence_bound);
|
||||
|
||||
|
||||
// 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);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_bounded_wstring_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_bounded_string_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_bound);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_bounded_wstring_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
|
||||
// ADD NESTED MEMBERS ============================================================================
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicType & nested_type);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicType & nested_type, size_t array_length);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicType & nested_type);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicType & nested_type, size_t sequence_bound);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicTypeBuilder & nested_type_builder);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_array_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicTypeBuilder & nested_type_builder, size_t array_length);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_unbounded_sequence_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicTypeBuilder & nested_type_builder);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_complex_bounded_sequence_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicTypeBuilder & nested_type_builder, size_t sequence_bound);
|
||||
|
||||
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_;
|
||||
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_dynamic_type_builder_t> rosidl_dynamic_type_builder_;
|
||||
|
||||
private:
|
||||
RCLCPP_PUBLIC
|
||||
DynamicTypeBuilder();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init_from_serialization_support_(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const std::string & name);
|
||||
|
||||
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_TYPE_BUILDER_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_
|
||||
@@ -117,6 +117,22 @@
|
||||
* - Allocator related items:
|
||||
* - rclcpp/allocator/allocator_common.hpp
|
||||
* - rclcpp/allocator/allocator_deleter.hpp
|
||||
* - Dynamic typesupport wrappers
|
||||
* - rclcpp::dynamic_typesupport::DynamicData
|
||||
* - rclcpp::dynamic_typesupport::DynamicMessage
|
||||
* - rclcpp::dynamic_typesupport::DynamicMessageType
|
||||
* - rclcpp::dynamic_typesupport::DynamicSerializationSupport
|
||||
* - rclcpp::dynamic_typesupport::DynamicTypeBuilder
|
||||
* - rclcpp::dynamic_typesupport::DynamicType
|
||||
* - rclcpp/dynamic_typesupport/dynamic_data.hpp
|
||||
* - rclcpp/dynamic_typesupport/dynamic_message.hpp
|
||||
* - rclcpp/dynamic_typesupport/dynamic_message_type.hpp
|
||||
* - rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp
|
||||
* - rclcpp/dynamic_typesupport/dynamic_type_builder.hpp
|
||||
* - rclcpp/dynamic_typesupport/dynamic_type.hpp
|
||||
* - Dynamic typesupport
|
||||
* - rclcpp::dynamic_typesupport::DynamicMessageTypeSupport
|
||||
* - rclcpp/dynamic_typesupport/dynamic_message_type_support.hpp
|
||||
* - Generic publisher
|
||||
* - rclcpp::Node::create_generic_publisher()
|
||||
* - rclcpp::GenericPublisher
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
641
rclcpp/src/rclcpp/dynamic_typesupport/dynamic_data.cpp
Normal file
641
rclcpp/src/rclcpp/dynamic_typesupport/dynamic_data.cpp
Normal file
@@ -0,0 +1,641 @@
|
||||
// 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 <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_data.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_type.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_type_builder.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rcl/types.h"
|
||||
#include "rcutils/logging_macros.h"
|
||||
|
||||
#include <rosidl_dynamic_typesupport/api/dynamic_type.h>
|
||||
#include <rosidl_dynamic_typesupport/api/dynamic_data.h>
|
||||
#include <rosidl_dynamic_typesupport/types.h>
|
||||
|
||||
using rclcpp::dynamic_typesupport::DynamicData;
|
||||
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
|
||||
using rclcpp::dynamic_typesupport::DynamicType;
|
||||
using rclcpp::dynamic_typesupport::DynamicTypeBuilder;
|
||||
|
||||
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_DATA_IMPL_HPP_
|
||||
// Template specialization implementations
|
||||
#include "rclcpp/dynamic_typesupport/detail/dynamic_data_impl.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
// CONSTRUCTION ==================================================================================
|
||||
DynamicData::DynamicData(const DynamicTypeBuilder::SharedPtr dynamic_type_builder)
|
||||
: serialization_support_(dynamic_type_builder->get_shared_dynamic_serialization_support()),
|
||||
rosidl_dynamic_data_(nullptr),
|
||||
is_loaned_(false),
|
||||
parent_data_(nullptr)
|
||||
{
|
||||
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();
|
||||
if (!rosidl_dynamic_type_builder) {
|
||||
throw std::runtime_error("dynamic type builder cannot be nullptr!");
|
||||
}
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_data_t * rosidl_dynamic_data = nullptr;
|
||||
rosidl_dynamic_data = rosidl_dynamic_typesupport_dynamic_data_init_from_dynamic_type_builder(
|
||||
rosidl_dynamic_type_builder);
|
||||
if (!rosidl_dynamic_data) {
|
||||
throw std::runtime_error("could not create new dynamic data object");
|
||||
}
|
||||
|
||||
rosidl_dynamic_data_.reset(
|
||||
rosidl_dynamic_data,
|
||||
// Custom deleter
|
||||
[](rosidl_dynamic_typesupport_dynamic_data_t * rosidl_dynamic_data)->void {
|
||||
rosidl_dynamic_typesupport_dynamic_data_fini(rosidl_dynamic_data);
|
||||
free(rosidl_dynamic_data);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
DynamicData::DynamicData(const DynamicType::SharedPtr dynamic_type)
|
||||
: serialization_support_(dynamic_type->get_shared_dynamic_serialization_support()),
|
||||
rosidl_dynamic_data_(nullptr),
|
||||
is_loaned_(false),
|
||||
parent_data_(nullptr)
|
||||
{
|
||||
if (!serialization_support_) {
|
||||
throw std::runtime_error("dynamic type could not bind serialization support!");
|
||||
}
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_type_t * rosidl_dynamic_type =
|
||||
dynamic_type->get_rosidl_dynamic_type();
|
||||
if (!rosidl_dynamic_type) {
|
||||
throw std::runtime_error("dynamic type cannot be nullptr!");
|
||||
}
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_data_t * rosidl_dynamic_data = nullptr;
|
||||
rosidl_dynamic_data = rosidl_dynamic_typesupport_dynamic_data_init_from_dynamic_type(
|
||||
rosidl_dynamic_type);
|
||||
if (!rosidl_dynamic_data) {
|
||||
throw std::runtime_error("could not create new dynamic data object");
|
||||
}
|
||||
|
||||
rosidl_dynamic_data_.reset(
|
||||
rosidl_dynamic_data,
|
||||
// Custom deleter
|
||||
[](rosidl_dynamic_typesupport_dynamic_data_t * rosidl_dynamic_data)->void {
|
||||
rosidl_dynamic_typesupport_dynamic_data_fini(rosidl_dynamic_data);
|
||||
free(rosidl_dynamic_data);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
DynamicData::DynamicData(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
rosidl_dynamic_typesupport_dynamic_data_t * rosidl_dynamic_data)
|
||||
: serialization_support_(serialization_support),
|
||||
rosidl_dynamic_data_(nullptr),
|
||||
is_loaned_(false),
|
||||
parent_data_(nullptr)
|
||||
{
|
||||
if (!rosidl_dynamic_data) {
|
||||
throw std::runtime_error("rosidl dynamic data cannot be 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!");
|
||||
}
|
||||
}
|
||||
|
||||
rosidl_dynamic_data_.reset(
|
||||
rosidl_dynamic_data,
|
||||
// Custom deleter
|
||||
[](rosidl_dynamic_typesupport_dynamic_data_t * rosidl_dynamic_data)->void {
|
||||
rosidl_dynamic_typesupport_dynamic_data_fini(rosidl_dynamic_data);
|
||||
free(rosidl_dynamic_data);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
DynamicData::DynamicData(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_dynamic_data_t> rosidl_dynamic_data)
|
||||
: serialization_support_(serialization_support),
|
||||
rosidl_dynamic_data_(rosidl_dynamic_data),
|
||||
is_loaned_(false),
|
||||
parent_data_(nullptr)
|
||||
{
|
||||
if (!rosidl_dynamic_data) {
|
||||
throw std::runtime_error("rosidl dynamic data cannot be 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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DynamicData::DynamicData(
|
||||
DynamicData::SharedPtr parent_data,
|
||||
rosidl_dynamic_typesupport_dynamic_data_t * rosidl_loaned_data)
|
||||
: DynamicData(parent_data->get_shared_dynamic_serialization_support(), rosidl_loaned_data)
|
||||
{
|
||||
if (!parent_data) {
|
||||
throw std::runtime_error("parent dynamic data cannot be nullptr!");
|
||||
}
|
||||
if (!rosidl_loaned_data) {
|
||||
throw std::runtime_error("loaned rosidl dynamic data cannot be nullptr!");
|
||||
}
|
||||
|
||||
parent_data_ = parent_data;
|
||||
is_loaned_ = true;
|
||||
}
|
||||
|
||||
|
||||
DynamicData::DynamicData(const DynamicData & other)
|
||||
: enable_shared_from_this(),
|
||||
serialization_support_(nullptr),
|
||||
rosidl_dynamic_data_(nullptr),
|
||||
is_loaned_(false),
|
||||
parent_data_(nullptr)
|
||||
{
|
||||
DynamicData out = other.clone();
|
||||
// We don't copy is_loaned_ or parent_data_ because it's a fresh copy now
|
||||
std::swap(serialization_support_, out.serialization_support_);
|
||||
std::swap(rosidl_dynamic_data_, out.rosidl_dynamic_data_);
|
||||
}
|
||||
|
||||
|
||||
DynamicData::DynamicData(DynamicData && other) noexcept
|
||||
: serialization_support_(std::exchange(other.serialization_support_, nullptr)),
|
||||
rosidl_dynamic_data_(std::exchange(other.rosidl_dynamic_data_, nullptr)),
|
||||
is_loaned_(other.is_loaned_),
|
||||
parent_data_(std::exchange(other.parent_data_, nullptr))
|
||||
{}
|
||||
|
||||
|
||||
DynamicData &
|
||||
DynamicData::operator=(const DynamicData & other)
|
||||
{
|
||||
return *this = DynamicData(other);
|
||||
}
|
||||
|
||||
|
||||
DynamicData &
|
||||
DynamicData::operator=(DynamicData && 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;
|
||||
}
|
||||
|
||||
|
||||
DynamicData::~DynamicData()
|
||||
{
|
||||
if (is_loaned_) {
|
||||
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
|
||||
DynamicData::match_serialization_support_(
|
||||
const DynamicSerializationSupport & serialization_support,
|
||||
const rosidl_dynamic_typesupport_dynamic_data_t & rosidl_dynamic_type_data)
|
||||
{
|
||||
bool out = true;
|
||||
|
||||
if (serialization_support.get_library_identifier() != std::string(
|
||||
rosidl_dynamic_type_data.serialization_support->library_identifier))
|
||||
{
|
||||
RCUTILS_LOG_ERROR("serialization support library identifier does not match dynamic data's");
|
||||
out = false;
|
||||
}
|
||||
|
||||
// TODO(methylDragon): Can I do this?? Is it portable?
|
||||
if (serialization_support.get_rosidl_serialization_support() !=
|
||||
rosidl_dynamic_type_data.serialization_support)
|
||||
{
|
||||
RCUTILS_LOG_ERROR("serialization support pointer does not match dynamic data's");
|
||||
out = false;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// GETTERS =======================================================================================
|
||||
const std::string
|
||||
DynamicData::get_library_identifier() const
|
||||
{
|
||||
return std::string(rosidl_dynamic_data_->serialization_support->library_identifier);
|
||||
}
|
||||
|
||||
|
||||
const std::string
|
||||
DynamicData::get_name() const
|
||||
{
|
||||
size_t buf_length;
|
||||
const char * buf = rosidl_dynamic_typesupport_dynamic_data_get_name(
|
||||
get_rosidl_dynamic_data(), &buf_length);
|
||||
return std::string(buf, buf_length);
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_data_t *
|
||||
DynamicData::get_rosidl_dynamic_data()
|
||||
{
|
||||
return rosidl_dynamic_data_.get();
|
||||
}
|
||||
|
||||
|
||||
const rosidl_dynamic_typesupport_dynamic_data_t *
|
||||
DynamicData::get_rosidl_dynamic_data() const
|
||||
{
|
||||
return rosidl_dynamic_data_.get();
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_dynamic_data_t>
|
||||
DynamicData::get_shared_rosidl_dynamic_data()
|
||||
{
|
||||
return std::shared_ptr<rosidl_dynamic_typesupport_dynamic_data_t>(
|
||||
shared_from_this(), rosidl_dynamic_data_.get());
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<const rosidl_dynamic_typesupport_dynamic_data_t>
|
||||
DynamicData::get_shared_rosidl_dynamic_data() const
|
||||
{
|
||||
return std::shared_ptr<rosidl_dynamic_typesupport_dynamic_data_t>(
|
||||
shared_from_this(), rosidl_dynamic_data_.get());
|
||||
}
|
||||
|
||||
|
||||
DynamicSerializationSupport::SharedPtr
|
||||
DynamicData::get_shared_dynamic_serialization_support()
|
||||
{
|
||||
return serialization_support_;
|
||||
}
|
||||
|
||||
|
||||
DynamicSerializationSupport::ConstSharedPtr
|
||||
DynamicData::get_shared_dynamic_serialization_support() const
|
||||
{
|
||||
return serialization_support_;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
DynamicData::get_item_count() const
|
||||
{
|
||||
return rosidl_dynamic_typesupport_dynamic_data_get_item_count(get_rosidl_dynamic_data());
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicData::get_member_id(size_t index) const
|
||||
{
|
||||
return rosidl_dynamic_typesupport_dynamic_data_get_member_id_at_index(
|
||||
get_rosidl_dynamic_data(), index);
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicData::get_member_id(const std::string & name) const
|
||||
{
|
||||
return rosidl_dynamic_typesupport_dynamic_data_get_member_id_by_name(
|
||||
get_rosidl_dynamic_data(), name.c_str(), name.size());
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicData::get_array_index(size_t index) const
|
||||
{
|
||||
return rosidl_dynamic_typesupport_dynamic_data_get_array_index(
|
||||
get_rosidl_dynamic_data(), index);
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicData::get_array_index(const std::string & name) const
|
||||
{
|
||||
return get_array_index(get_member_id(name));
|
||||
}
|
||||
|
||||
|
||||
// METHODS =======================================================================================
|
||||
DynamicData
|
||||
DynamicData::clone() const
|
||||
{
|
||||
return DynamicData(
|
||||
serialization_support_,
|
||||
rosidl_dynamic_typesupport_dynamic_data_clone(get_rosidl_dynamic_data()));
|
||||
}
|
||||
|
||||
|
||||
DynamicData::SharedPtr
|
||||
DynamicData::clone_shared() const
|
||||
{
|
||||
return DynamicData::make_shared(
|
||||
serialization_support_,
|
||||
rosidl_dynamic_typesupport_dynamic_data_clone(get_rosidl_dynamic_data()));
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DynamicData::equals(const DynamicData & other) const
|
||||
{
|
||||
if (get_library_identifier() != other.get_library_identifier()) {
|
||||
throw std::runtime_error("library identifiers don't match");
|
||||
}
|
||||
return rosidl_dynamic_typesupport_dynamic_data_equals(
|
||||
get_rosidl_dynamic_data(), other.get_rosidl_dynamic_data());
|
||||
}
|
||||
|
||||
|
||||
DynamicData::SharedPtr
|
||||
DynamicData::loan_value(rosidl_dynamic_typesupport_member_id_t id)
|
||||
{
|
||||
return DynamicData::make_shared(
|
||||
shared_from_this(),
|
||||
rosidl_dynamic_typesupport_dynamic_data_loan_value(
|
||||
get_rosidl_dynamic_data(), id));
|
||||
}
|
||||
|
||||
|
||||
DynamicData::SharedPtr
|
||||
DynamicData::loan_value(const std::string & name)
|
||||
{
|
||||
return loan_value(get_member_id(name));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicData::clear_all_values()
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_clear_all_values(get_rosidl_dynamic_data());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicData::clear_nonkey_values()
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_clear_nonkey_values(get_rosidl_dynamic_data());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicData::clear_value(rosidl_dynamic_typesupport_member_id_t id)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_clear_value(get_rosidl_dynamic_data(), id);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicData::clear_value(const std::string & name)
|
||||
{
|
||||
clear_value(get_member_id(name));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicData::clear_sequence()
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_clear_sequence_data(get_rosidl_dynamic_data());
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicData::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
|
||||
DynamicData::remove_sequence_data(rosidl_dynamic_typesupport_member_id_t index)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_remove_sequence_data(
|
||||
get_rosidl_dynamic_data(), index);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicData::print() const
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_print(get_rosidl_dynamic_data());
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DynamicData::serialize(rcl_serialized_message_t * buffer)
|
||||
{
|
||||
return rosidl_dynamic_typesupport_dynamic_data_serialize(get_rosidl_dynamic_data(), buffer);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DynamicData::deserialize(rcl_serialized_message_t * buffer)
|
||||
{
|
||||
return rosidl_dynamic_typesupport_dynamic_data_deserialize(get_rosidl_dynamic_data(), buffer);
|
||||
}
|
||||
|
||||
|
||||
// MEMBER ACCESS ===================================================================================
|
||||
// Defined in "detail/dynamic_data_impl.hpp"
|
||||
|
||||
|
||||
// BOUNDED STRING MEMBER ACCESS ====================================================================
|
||||
const std::string
|
||||
DynamicData::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);
|
||||
free(buf);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
const std::string
|
||||
DynamicData::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
|
||||
DynamicData::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);
|
||||
free(buf);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
const std::u16string
|
||||
DynamicData::get_bounded_wstring_value(const std::string & name, size_t wstring_bound)
|
||||
{
|
||||
return get_bounded_wstring_value(get_member_id(name), wstring_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicData::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
|
||||
DynamicData::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
|
||||
DynamicData::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
|
||||
DynamicData::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
|
||||
DynamicData::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
|
||||
DynamicData::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 ============================================================================
|
||||
DynamicData
|
||||
DynamicData::get_complex_value(rosidl_dynamic_typesupport_member_id_t id)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_t * out_ptr = nullptr;
|
||||
rosidl_dynamic_typesupport_dynamic_data_get_complex_value(
|
||||
get_rosidl_dynamic_data(), id, &out_ptr);
|
||||
return DynamicData(get_shared_dynamic_serialization_support(), out_ptr);
|
||||
}
|
||||
|
||||
|
||||
DynamicData
|
||||
DynamicData::get_complex_value(const std::string & name)
|
||||
{
|
||||
return get_complex_value(get_member_id(name));
|
||||
}
|
||||
|
||||
|
||||
DynamicData::SharedPtr
|
||||
DynamicData::get_complex_value_shared(rosidl_dynamic_typesupport_member_id_t id)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_t * out_ptr = nullptr;
|
||||
rosidl_dynamic_typesupport_dynamic_data_get_complex_value(
|
||||
get_rosidl_dynamic_data(), id, &out_ptr);
|
||||
return DynamicData::make_shared(get_shared_dynamic_serialization_support(), out_ptr);
|
||||
}
|
||||
|
||||
|
||||
DynamicData::SharedPtr
|
||||
DynamicData::get_complex_value_shared(const std::string & name)
|
||||
{
|
||||
return get_complex_value_shared(get_member_id(name));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicData::set_complex_value(
|
||||
rosidl_dynamic_typesupport_member_id_t id, DynamicData & value)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_data_set_complex_value(
|
||||
get_rosidl_dynamic_data(), id, value.get_rosidl_dynamic_data());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicData::set_complex_value(const std::string & name, DynamicData & value)
|
||||
{
|
||||
set_complex_value(get_member_id(name), value);
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_member_id_t
|
||||
DynamicData::insert_complex_value_copy(const DynamicData & 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
|
||||
DynamicData::insert_complex_value(DynamicData & 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;
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
// 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 <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rmw/dynamic_typesupport.h"
|
||||
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_message_type_support.hpp"
|
||||
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_message_type.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rcutils/logging_macros.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>
|
||||
|
||||
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
|
||||
using rclcpp::dynamic_typesupport::DynamicMessage;
|
||||
using rclcpp::dynamic_typesupport::DynamicMessageType;
|
||||
using rclcpp::dynamic_typesupport::DynamicMessageTypeSupport;
|
||||
|
||||
|
||||
// CONSTRUCTION ====================================================================================
|
||||
DynamicMessageTypeSupport::DynamicMessageTypeSupport(
|
||||
rosidl_runtime_c__type_description__TypeDescription * description,
|
||||
const std::string & serialization_library_name)
|
||||
: serialization_support_(nullptr),
|
||||
dynamic_message_type_(nullptr),
|
||||
dynamic_message_(nullptr),
|
||||
description_(nullptr),
|
||||
rosidl_message_type_support_(nullptr)
|
||||
{
|
||||
if (!description) {
|
||||
throw std::runtime_error("description cannot be nullptr!");
|
||||
}
|
||||
description_.reset(
|
||||
description,
|
||||
[](rosidl_runtime_c__type_description__TypeDescription * description) -> void {
|
||||
rosidl_runtime_c__type_description__TypeDescription__destroy(description);
|
||||
});
|
||||
|
||||
init_serialization_support_(serialization_library_name);
|
||||
if (!serialization_support_) {
|
||||
throw std::runtime_error("could not init dynamic serialization support!");
|
||||
}
|
||||
|
||||
init_dynamic_message_type_(serialization_support_, description);
|
||||
if (!dynamic_message_type_) {
|
||||
throw std::runtime_error("could not init dynamic message type!");
|
||||
}
|
||||
|
||||
init_dynamic_message_(dynamic_message_type_);
|
||||
if (!dynamic_message_) {
|
||||
throw std::runtime_error("could not init dynamic message!");
|
||||
}
|
||||
|
||||
init_rosidl_message_type_support_(
|
||||
serialization_support_, dynamic_message_type_, dynamic_message_, description_.get());
|
||||
if (!rosidl_message_type_support_) {
|
||||
throw std::runtime_error("could not init rosidl message type support!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageTypeSupport::DynamicMessageTypeSupport(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
rosidl_runtime_c__type_description__TypeDescription * description)
|
||||
: serialization_support_(serialization_support),
|
||||
dynamic_message_type_(nullptr),
|
||||
dynamic_message_(nullptr),
|
||||
description_(nullptr),
|
||||
rosidl_message_type_support_(nullptr)
|
||||
{
|
||||
// Check null
|
||||
if (!serialization_support) {
|
||||
throw std::runtime_error("serialization_support cannot be nullptr!");
|
||||
}
|
||||
if (!description) {
|
||||
throw std::runtime_error("description cannot be nullptr!");
|
||||
}
|
||||
description_.reset(
|
||||
description,
|
||||
[](rosidl_runtime_c__type_description__TypeDescription * description) -> void {
|
||||
rosidl_runtime_c__type_description__TypeDescription__destroy(description);
|
||||
});
|
||||
|
||||
// Init
|
||||
init_dynamic_message_type_(serialization_support_, description);
|
||||
if (!dynamic_message_type_) {
|
||||
throw std::runtime_error("could not init dynamic message type!");
|
||||
}
|
||||
|
||||
init_dynamic_message_(dynamic_message_type_);
|
||||
if (!dynamic_message_) {
|
||||
throw std::runtime_error("could not init dynamic message!");
|
||||
}
|
||||
|
||||
init_rosidl_message_type_support_(
|
||||
serialization_support_, dynamic_message_type_, dynamic_message_, description_.get());
|
||||
if (!rosidl_message_type_support_) {
|
||||
throw std::runtime_error("could not init rosidl message type support!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageTypeSupport::DynamicMessageTypeSupport(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
DynamicMessageType::SharedPtr dynamic_message_type,
|
||||
DynamicMessage::SharedPtr dynamic_message,
|
||||
rosidl_runtime_c__type_description__TypeDescription * description)
|
||||
: serialization_support_(serialization_support),
|
||||
dynamic_message_type_(dynamic_message_type),
|
||||
dynamic_message_(dynamic_message),
|
||||
description_(nullptr),
|
||||
rosidl_message_type_support_(nullptr)
|
||||
{
|
||||
// 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!");
|
||||
}
|
||||
|
||||
if (description) {
|
||||
description_.reset(
|
||||
description,
|
||||
[](rosidl_runtime_c__type_description__TypeDescription * description) -> void {
|
||||
rosidl_runtime_c__type_description__TypeDescription__destroy(description);
|
||||
});
|
||||
}
|
||||
|
||||
// Check identifiers
|
||||
if (serialization_support->get_library_identifier() !=
|
||||
dynamic_message_type->get_library_identifier())
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"serialization support library identifier does not match "
|
||||
"dynamic message type library identifier!");
|
||||
}
|
||||
if (dynamic_message_type->get_library_identifier() != dynamic_message->get_library_identifier()) {
|
||||
throw std::runtime_error(
|
||||
"dynamic message type library identifier does not match "
|
||||
"dynamic message library identifier!");
|
||||
}
|
||||
|
||||
// Check pointers
|
||||
/* *INDENT-OFF* */
|
||||
if (serialization_support->get_rosidl_serialization_support() !=
|
||||
dynamic_message_type
|
||||
->get_shared_dynamic_serialization_support()
|
||||
->get_rosidl_serialization_support())
|
||||
{
|
||||
throw std::runtime_error("serialization support pointer dynamic message type's");
|
||||
}
|
||||
if (dynamic_message_type
|
||||
->get_shared_dynamic_serialization_support()
|
||||
->get_rosidl_serialization_support() !=
|
||||
dynamic_message_type
|
||||
->get_shared_dynamic_serialization_support()
|
||||
->get_rosidl_serialization_support())
|
||||
{
|
||||
throw std::runtime_error("serialization support pointer dynamic message type's");
|
||||
}
|
||||
/* *INDENT-ON* */
|
||||
|
||||
init_rosidl_message_type_support_(
|
||||
serialization_support_, dynamic_message_type_, dynamic_message_, description_.get());
|
||||
if (!rosidl_message_type_support_) {
|
||||
throw std::runtime_error("could not init rosidl message type support!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DynamicMessageTypeSupport::~DynamicMessageTypeSupport() {}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeSupport::init_serialization_support_(
|
||||
const std::string & serialization_library_name)
|
||||
{
|
||||
serialization_support_ = DynamicSerializationSupport::make_shared(serialization_library_name);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeSupport::init_dynamic_message_type_(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const rosidl_runtime_c__type_description__TypeDescription * description)
|
||||
{
|
||||
dynamic_message_type_ = DynamicMessageType::make_shared(serialization_support, description);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeSupport::init_dynamic_message_(DynamicType::SharedPtr dynamic_type)
|
||||
{
|
||||
dynamic_message_ = DynamicMessage::make_shared(dynamic_type);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicMessageTypeSupport::init_rosidl_message_type_support_(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
DynamicMessageType::SharedPtr dynamic_message_type,
|
||||
DynamicMessage::SharedPtr dynamic_message,
|
||||
rosidl_runtime_c__type_description__TypeDescription * description)
|
||||
{
|
||||
bool middleware_supports_type_discovery =
|
||||
rmw_feature_supported(RMW_MIDDLEWARE_SUPPORTS_TYPE_DISCOVERY);
|
||||
bool middleware_can_take_dynamic_data =
|
||||
rmw_feature_supported(RMW_MIDDLEWARE_CAN_TAKE_DYNAMIC_DATA);
|
||||
|
||||
if (!middleware_supports_type_discovery && !description) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
rmw_dynamic_typesupport_c__identifier,
|
||||
"Middleware does not support type discovery! Deferred dynamic type"
|
||||
"message type support will never be populated! You must provide a type "
|
||||
"description!");
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE(methylDragon): We don't finalize the rosidl_message_type_support->data since its members
|
||||
// are managed by the passed in SharedPtr wrapper classes
|
||||
rosidl_message_type_support_.reset(
|
||||
new rosidl_message_type_support_t(),
|
||||
[](rosidl_message_type_support_t * ts) -> void {free(const_cast<void *>(ts->data));}
|
||||
);
|
||||
|
||||
if (!rosidl_message_type_support_) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
rmw_dynamic_typesupport_c__identifier,
|
||||
"Could not allocate rosidl_message_type_support_t struct");
|
||||
return;
|
||||
}
|
||||
|
||||
rosidl_message_type_support_->typesupport_identifier = rmw_dynamic_typesupport_c__identifier;
|
||||
|
||||
// NOTE(methylDragon): To populate dynamic_type and description if deferred, OUTSIDE
|
||||
rosidl_message_type_support_->data = calloc(1, sizeof(rmw_dynamic_typesupport_impl_t));
|
||||
if (!rosidl_message_type_support_->data) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
rmw_dynamic_typesupport_c__identifier,
|
||||
"Could not allocate rmw_dynamic_typesupport_impl_t struct");
|
||||
rosidl_message_type_support_.reset();
|
||||
}
|
||||
|
||||
rmw_dynamic_typesupport_impl_t * ts_impl =
|
||||
(rmw_dynamic_typesupport_impl_t *)rosidl_message_type_support_->data;
|
||||
|
||||
ts_impl->take_dynamic_data = middleware_can_take_dynamic_data;
|
||||
ts_impl->serialization_support = serialization_support->get_rosidl_serialization_support();
|
||||
ts_impl->dynamic_type = dynamic_message_type->get_rosidl_dynamic_type();
|
||||
ts_impl->dynamic_data = dynamic_message->get_rosidl_dynamic_data();
|
||||
ts_impl->description = description;
|
||||
rosidl_message_type_support_->func = get_message_typesupport_handle_function;
|
||||
}
|
||||
|
||||
|
||||
// GETTERS =========================================================================================
|
||||
const std::string
|
||||
DynamicMessageTypeSupport::get_library_identifier() const
|
||||
{
|
||||
return serialization_support_->get_library_identifier();
|
||||
}
|
||||
|
||||
|
||||
rosidl_message_type_support_t *
|
||||
DynamicMessageTypeSupport::get_rosidl_message_type_support()
|
||||
{
|
||||
return rosidl_message_type_support_.get();
|
||||
}
|
||||
|
||||
|
||||
const rosidl_message_type_support_t *
|
||||
DynamicMessageTypeSupport::get_rosidl_message_type_support() const
|
||||
{
|
||||
return rosidl_message_type_support_.get();
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<rosidl_message_type_support_t>
|
||||
DynamicMessageTypeSupport::get_shared_rosidl_message_type_support()
|
||||
{
|
||||
return rosidl_message_type_support_;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<const rosidl_message_type_support_t>
|
||||
DynamicMessageTypeSupport::get_shared_rosidl_message_type_support() const
|
||||
{
|
||||
return rosidl_message_type_support_;
|
||||
}
|
||||
|
||||
|
||||
rosidl_runtime_c__type_description__TypeDescription *
|
||||
DynamicMessageTypeSupport::get_rosidl_runtime_c_type_description()
|
||||
{
|
||||
return description_.get();
|
||||
}
|
||||
|
||||
|
||||
const rosidl_runtime_c__type_description__TypeDescription *
|
||||
DynamicMessageTypeSupport::get_rosidl_runtime_c_type_description() const
|
||||
{
|
||||
return description_.get();
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<rosidl_runtime_c__type_description__TypeDescription>
|
||||
DynamicMessageTypeSupport::get_shared_rosidl_runtime_c_type_description()
|
||||
{
|
||||
return description_;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<const rosidl_runtime_c__type_description__TypeDescription>
|
||||
DynamicMessageTypeSupport::get_shared_rosidl_runtime_c_type_description() const
|
||||
{
|
||||
return 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_;
|
||||
}
|
||||
|
||||
|
||||
// METHODS =========================================================================================
|
||||
void
|
||||
DynamicMessageTypeSupport::print_description() const
|
||||
{
|
||||
if (!description_) {
|
||||
RCUTILS_LOG_ERROR("Can't print description, no bound description!");
|
||||
}
|
||||
rosidl_runtime_c_type_description_utils_print_type_description(description_.get());
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
// 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 <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rcutils/logging_macros.h"
|
||||
#include "rmw/dynamic_typesupport.h"
|
||||
|
||||
#include <rosidl_dynamic_typesupport/api/serialization_support.h>
|
||||
|
||||
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
|
||||
|
||||
// CONSTRUCTION ====================================================================================
|
||||
DynamicSerializationSupport::DynamicSerializationSupport(
|
||||
const std::string & serialization_library_name)
|
||||
: rosidl_serialization_support_(nullptr)
|
||||
{
|
||||
rosidl_dynamic_typesupport_serialization_support_t * rosidl_serialization_support = nullptr;
|
||||
|
||||
if (serialization_library_name.empty()) {
|
||||
rosidl_serialization_support = rmw_get_serialization_support(NULL);
|
||||
} else {
|
||||
rosidl_serialization_support =
|
||||
rmw_get_serialization_support(serialization_library_name.c_str());
|
||||
}
|
||||
|
||||
if (!rosidl_serialization_support) {
|
||||
throw std::runtime_error("could not create new serialization support object");
|
||||
}
|
||||
|
||||
rosidl_serialization_support_.reset(
|
||||
rosidl_serialization_support,
|
||||
// Custom deleter
|
||||
[](rosidl_dynamic_typesupport_serialization_support_t * rosidl_serialization_support) -> void {
|
||||
rosidl_dynamic_typesupport_serialization_support_destroy(rosidl_serialization_support);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
DynamicSerializationSupport::DynamicSerializationSupport(
|
||||
rosidl_dynamic_typesupport_serialization_support_t * rosidl_serialization_support)
|
||||
: rosidl_serialization_support_(nullptr)
|
||||
{
|
||||
if (!rosidl_serialization_support) {
|
||||
throw std::runtime_error("serialization support cannot be nullptr!");
|
||||
}
|
||||
|
||||
// Custom deleter
|
||||
rosidl_serialization_support_.reset(
|
||||
rosidl_serialization_support,
|
||||
[](rosidl_dynamic_typesupport_serialization_support_t * rosidl_serialization_support) -> void {
|
||||
rosidl_dynamic_typesupport_serialization_support_destroy(rosidl_serialization_support);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
DynamicSerializationSupport::DynamicSerializationSupport(
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_serialization_support_t> rosidl_serialization_support)
|
||||
: rosidl_serialization_support_(rosidl_serialization_support) {}
|
||||
|
||||
|
||||
DynamicSerializationSupport::DynamicSerializationSupport(
|
||||
DynamicSerializationSupport && other) noexcept
|
||||
: rosidl_serialization_support_(std::exchange(other.rosidl_serialization_support_, nullptr)) {}
|
||||
|
||||
|
||||
DynamicSerializationSupport &
|
||||
DynamicSerializationSupport::operator=(DynamicSerializationSupport && other) noexcept
|
||||
{
|
||||
std::swap(rosidl_serialization_support_, other.rosidl_serialization_support_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
DynamicSerializationSupport::~DynamicSerializationSupport() {}
|
||||
|
||||
|
||||
// GETTERS =========================================================================================
|
||||
const std::string
|
||||
DynamicSerializationSupport::get_library_identifier() const
|
||||
{
|
||||
return std::string(
|
||||
rosidl_dynamic_typesupport_serialization_support_get_library_identifier(
|
||||
rosidl_serialization_support_.get()));
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_serialization_support_t *
|
||||
DynamicSerializationSupport::get_rosidl_serialization_support()
|
||||
{
|
||||
return rosidl_serialization_support_.get();
|
||||
}
|
||||
|
||||
|
||||
const rosidl_dynamic_typesupport_serialization_support_t *
|
||||
DynamicSerializationSupport::get_rosidl_serialization_support() const
|
||||
{
|
||||
return rosidl_serialization_support_.get();
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_serialization_support_t>
|
||||
DynamicSerializationSupport::get_shared_rosidl_serialization_support()
|
||||
{
|
||||
return std::shared_ptr<rosidl_dynamic_typesupport_serialization_support_t>(
|
||||
shared_from_this(), rosidl_serialization_support_.get());
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<const rosidl_dynamic_typesupport_serialization_support_t>
|
||||
DynamicSerializationSupport::get_shared_rosidl_serialization_support() const
|
||||
{
|
||||
return std::shared_ptr<const rosidl_dynamic_typesupport_serialization_support_t>(
|
||||
shared_from_this(), rosidl_serialization_support_.get());
|
||||
}
|
||||
320
rclcpp/src/rclcpp/dynamic_typesupport/dynamic_type.cpp
Normal file
320
rclcpp/src/rclcpp/dynamic_typesupport/dynamic_type.cpp
Normal file
@@ -0,0 +1,320 @@
|
||||
// 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 <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_data.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_type.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_type_builder.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rcutils/logging_macros.h"
|
||||
|
||||
#include <rosidl_dynamic_typesupport/api/dynamic_data.h>
|
||||
#include <rosidl_dynamic_typesupport/api/dynamic_type.h>
|
||||
#include <rosidl_dynamic_typesupport/types.h>
|
||||
|
||||
using rclcpp::dynamic_typesupport::DynamicData;
|
||||
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
|
||||
using rclcpp::dynamic_typesupport::DynamicType;
|
||||
using rclcpp::dynamic_typesupport::DynamicTypeBuilder;
|
||||
|
||||
|
||||
// CONSTRUCTION ====================================================================================
|
||||
DynamicType::DynamicType(DynamicTypeBuilder::SharedPtr dynamic_type_builder)
|
||||
: serialization_support_(dynamic_type_builder->get_shared_dynamic_serialization_support()),
|
||||
rosidl_dynamic_type_(nullptr)
|
||||
{
|
||||
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();
|
||||
if (!rosidl_dynamic_type_builder) {
|
||||
throw std::runtime_error("dynamic type builder cannot be nullptr!");
|
||||
}
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_type_t * rosidl_dynamic_type = nullptr;
|
||||
rosidl_dynamic_type = rosidl_dynamic_typesupport_dynamic_type_init_from_dynamic_type_builder(
|
||||
rosidl_dynamic_type_builder);
|
||||
if (!rosidl_dynamic_type) {
|
||||
throw std::runtime_error("could not create new dynamic type object");
|
||||
}
|
||||
|
||||
rosidl_dynamic_type_.reset(
|
||||
rosidl_dynamic_type,
|
||||
// Custom deleter
|
||||
[](rosidl_dynamic_typesupport_dynamic_type_t * rosidl_dynamic_type)->void {
|
||||
rosidl_dynamic_typesupport_dynamic_type_fini(rosidl_dynamic_type);
|
||||
free(rosidl_dynamic_type);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
DynamicType::DynamicType(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
rosidl_dynamic_typesupport_dynamic_type_t * rosidl_dynamic_type)
|
||||
: serialization_support_(serialization_support), rosidl_dynamic_type_(nullptr)
|
||||
{
|
||||
if (!rosidl_dynamic_type) {
|
||||
throw std::runtime_error("rosidl dynamic type cannot be nullptr!");
|
||||
}
|
||||
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!");
|
||||
}
|
||||
}
|
||||
|
||||
rosidl_dynamic_type_.reset(
|
||||
rosidl_dynamic_type,
|
||||
// Custom deleter
|
||||
[](rosidl_dynamic_typesupport_dynamic_type_t * rosidl_dynamic_type)->void {
|
||||
rosidl_dynamic_typesupport_dynamic_type_fini(rosidl_dynamic_type);
|
||||
free(rosidl_dynamic_type);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
DynamicType::DynamicType(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_dynamic_type_t> rosidl_dynamic_type)
|
||||
: serialization_support_(serialization_support), rosidl_dynamic_type_(rosidl_dynamic_type)
|
||||
{
|
||||
if (!rosidl_dynamic_type) {
|
||||
throw std::runtime_error("rosidl dynamic type cannot be nullptr!");
|
||||
}
|
||||
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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DynamicType::DynamicType(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const rosidl_runtime_c__type_description__TypeDescription * description)
|
||||
: serialization_support_(serialization_support), rosidl_dynamic_type_(nullptr)
|
||||
{
|
||||
init_from_description(description, serialization_support);
|
||||
}
|
||||
|
||||
|
||||
DynamicType::DynamicType(const DynamicType & other)
|
||||
: enable_shared_from_this(), serialization_support_(nullptr), rosidl_dynamic_type_(nullptr)
|
||||
{
|
||||
DynamicType out = other.clone();
|
||||
std::swap(serialization_support_, out.serialization_support_);
|
||||
std::swap(rosidl_dynamic_type_, out.rosidl_dynamic_type_);
|
||||
}
|
||||
|
||||
|
||||
DynamicType::DynamicType(DynamicType && other) noexcept
|
||||
: serialization_support_(std::exchange(other.serialization_support_, nullptr)),
|
||||
rosidl_dynamic_type_(std::exchange(other.rosidl_dynamic_type_, nullptr)) {}
|
||||
|
||||
|
||||
DynamicType &
|
||||
DynamicType::operator=(const DynamicType & other)
|
||||
{
|
||||
return *this = DynamicType(other);
|
||||
}
|
||||
|
||||
|
||||
DynamicType &
|
||||
DynamicType::operator=(DynamicType && other) noexcept
|
||||
{
|
||||
std::swap(serialization_support_, other.serialization_support_);
|
||||
std::swap(rosidl_dynamic_type_, other.rosidl_dynamic_type_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
DynamicType::~DynamicType() {}
|
||||
|
||||
|
||||
void
|
||||
DynamicType::init_from_description(
|
||||
const rosidl_runtime_c__type_description__TypeDescription * description,
|
||||
DynamicSerializationSupport::SharedPtr serialization_support)
|
||||
{
|
||||
if (!description) {
|
||||
throw std::runtime_error("description cannot be nullptr!");
|
||||
}
|
||||
if (serialization_support) {
|
||||
// Swap serialization support if serialization support is given
|
||||
serialization_support_ = serialization_support;
|
||||
}
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_type_t * rosidl_dynamic_type = nullptr;
|
||||
rosidl_dynamic_type =
|
||||
rosidl_dynamic_typesupport_dynamic_type_init_from_description(
|
||||
serialization_support_->get_rosidl_serialization_support(), description);
|
||||
if (!rosidl_dynamic_type) {
|
||||
throw std::runtime_error("could not create new dynamic type object");
|
||||
}
|
||||
|
||||
rosidl_dynamic_type_.reset(
|
||||
rosidl_dynamic_type,
|
||||
// Custom deleter
|
||||
[](rosidl_dynamic_typesupport_dynamic_type_t * rosidl_dynamic_type)->void {
|
||||
rosidl_dynamic_typesupport_dynamic_type_fini(rosidl_dynamic_type);
|
||||
free(rosidl_dynamic_type);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DynamicType::match_serialization_support_(
|
||||
const DynamicSerializationSupport & serialization_support,
|
||||
const rosidl_dynamic_typesupport_dynamic_type_t & rosidl_dynamic_type)
|
||||
{
|
||||
bool out = true;
|
||||
|
||||
if (serialization_support.get_library_identifier() != std::string(
|
||||
rosidl_dynamic_type.serialization_support->library_identifier))
|
||||
{
|
||||
RCUTILS_LOG_ERROR(
|
||||
"serialization support library identifier does not match dynamic type's");
|
||||
out = false;
|
||||
}
|
||||
|
||||
// TODO(methylDragon): Can I do this?? Is it portable?
|
||||
if (serialization_support.get_rosidl_serialization_support() !=
|
||||
rosidl_dynamic_type.serialization_support)
|
||||
{
|
||||
RCUTILS_LOG_ERROR(
|
||||
"serialization support pointer does not match dynamic type's");
|
||||
out = false;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// GETTERS =========================================================================================
|
||||
const std::string
|
||||
DynamicType::get_library_identifier() const
|
||||
{
|
||||
return std::string(rosidl_dynamic_type_->serialization_support->library_identifier);
|
||||
}
|
||||
|
||||
|
||||
const std::string
|
||||
DynamicType::get_name() const
|
||||
{
|
||||
size_t buf_length;
|
||||
const char * buf = rosidl_dynamic_typesupport_dynamic_type_get_name(
|
||||
get_rosidl_dynamic_type(), &buf_length);
|
||||
return std::string(buf, buf_length);
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
DynamicType::get_member_count() const
|
||||
{
|
||||
return rosidl_dynamic_typesupport_dynamic_type_get_member_count(rosidl_dynamic_type_.get());
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_type_t *
|
||||
DynamicType::get_rosidl_dynamic_type()
|
||||
{
|
||||
return rosidl_dynamic_type_.get();
|
||||
}
|
||||
|
||||
|
||||
const rosidl_dynamic_typesupport_dynamic_type_t *
|
||||
DynamicType::get_rosidl_dynamic_type() const
|
||||
{
|
||||
return rosidl_dynamic_type_.get();
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_dynamic_type_t>
|
||||
DynamicType::get_shared_rosidl_dynamic_type()
|
||||
{
|
||||
return std::shared_ptr<rosidl_dynamic_typesupport_dynamic_type_t>(
|
||||
shared_from_this(), rosidl_dynamic_type_.get());
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<const rosidl_dynamic_typesupport_dynamic_type_t>
|
||||
DynamicType::get_shared_rosidl_dynamic_type() const
|
||||
{
|
||||
return std::shared_ptr<rosidl_dynamic_typesupport_dynamic_type_t>(
|
||||
shared_from_this(), rosidl_dynamic_type_.get());
|
||||
}
|
||||
|
||||
|
||||
DynamicSerializationSupport::SharedPtr
|
||||
DynamicType::get_shared_dynamic_serialization_support()
|
||||
{
|
||||
return serialization_support_;
|
||||
}
|
||||
|
||||
|
||||
DynamicSerializationSupport::ConstSharedPtr
|
||||
DynamicType::get_shared_dynamic_serialization_support() const
|
||||
{
|
||||
return serialization_support_;
|
||||
}
|
||||
|
||||
|
||||
// METHODS =========================================================================================
|
||||
DynamicType
|
||||
DynamicType::clone() const
|
||||
{
|
||||
return DynamicType(
|
||||
serialization_support_,
|
||||
rosidl_dynamic_typesupport_dynamic_type_clone(get_rosidl_dynamic_type()));
|
||||
}
|
||||
|
||||
|
||||
DynamicType::SharedPtr
|
||||
DynamicType::clone_shared() const
|
||||
{
|
||||
return DynamicType::make_shared(
|
||||
serialization_support_,
|
||||
rosidl_dynamic_typesupport_dynamic_type_clone(get_rosidl_dynamic_type()));
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DynamicType::equals(const DynamicType & other) const
|
||||
{
|
||||
if (get_library_identifier() != other.get_library_identifier()) {
|
||||
throw std::runtime_error("library identifiers don't match");
|
||||
}
|
||||
return rosidl_dynamic_typesupport_dynamic_type_equals(
|
||||
get_rosidl_dynamic_type(), other.get_rosidl_dynamic_type());
|
||||
}
|
||||
|
||||
|
||||
DynamicData
|
||||
DynamicType::build_data()
|
||||
{
|
||||
return DynamicData(shared_from_this());
|
||||
}
|
||||
|
||||
|
||||
DynamicData::SharedPtr
|
||||
DynamicType::build_data_shared()
|
||||
{
|
||||
return DynamicData::make_shared(shared_from_this());
|
||||
}
|
||||
536
rclcpp/src/rclcpp/dynamic_typesupport/dynamic_type_builder.cpp
Normal file
536
rclcpp/src/rclcpp/dynamic_typesupport/dynamic_type_builder.cpp
Normal file
@@ -0,0 +1,536 @@
|
||||
// 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 <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_data.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_type.hpp"
|
||||
#include "rclcpp/dynamic_typesupport/dynamic_type_builder.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rcutils/logging_macros.h"
|
||||
|
||||
#include <rosidl_dynamic_typesupport/api/dynamic_data.h>
|
||||
#include <rosidl_dynamic_typesupport/api/dynamic_type.h>
|
||||
#include <rosidl_dynamic_typesupport/types.h>
|
||||
|
||||
using rclcpp::dynamic_typesupport::DynamicData;
|
||||
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
|
||||
using rclcpp::dynamic_typesupport::DynamicType;
|
||||
using rclcpp::dynamic_typesupport::DynamicTypeBuilder;
|
||||
|
||||
|
||||
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_TYPE_BUILDER_IMPL_HPP_
|
||||
// Template specialization implementations
|
||||
#include "rclcpp/dynamic_typesupport/detail/dynamic_type_builder_impl.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
// CONSTRUCTION ==================================================================================
|
||||
DynamicTypeBuilder::DynamicTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support, const std::string & name)
|
||||
: serialization_support_(serialization_support), rosidl_dynamic_type_builder_(nullptr)
|
||||
{
|
||||
init_from_serialization_support_(serialization_support, name);
|
||||
if (!rosidl_dynamic_type_builder_) {
|
||||
throw std::runtime_error("could not create new dynamic type builder object");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DynamicTypeBuilder::DynamicTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_t * rosidl_dynamic_type_builder)
|
||||
: serialization_support_(serialization_support), rosidl_dynamic_type_builder_(nullptr)
|
||||
{
|
||||
if (!serialization_support) {
|
||||
throw std::runtime_error("serialization support cannot be nullptr!");
|
||||
}
|
||||
if (!rosidl_dynamic_type_builder) {
|
||||
throw std::runtime_error("rosidl dynamic type builder 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!");
|
||||
}
|
||||
|
||||
rosidl_dynamic_type_builder_.reset(
|
||||
rosidl_dynamic_type_builder,
|
||||
// Custom deleter
|
||||
[](rosidl_dynamic_typesupport_dynamic_type_builder_t * rosidl_dynamic_type_builder)->void {
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_fini(rosidl_dynamic_type_builder);
|
||||
free(rosidl_dynamic_type_builder);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
DynamicTypeBuilder::DynamicTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_dynamic_type_builder_t> rosidl_dynamic_type_builder)
|
||||
: serialization_support_(serialization_support),
|
||||
rosidl_dynamic_type_builder_(rosidl_dynamic_type_builder)
|
||||
{
|
||||
if (!serialization_support) {
|
||||
throw std::runtime_error("serialization support cannot be nullptr!");
|
||||
}
|
||||
if (!rosidl_dynamic_type_builder) {
|
||||
throw std::runtime_error("rosidl dynamic type builder cannot be nullptr!");
|
||||
}
|
||||
if (!match_serialization_support_(*serialization_support, *rosidl_dynamic_type_builder.get())) {
|
||||
throw std::runtime_error(
|
||||
"serialization support library does not match dynamic type builder's!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DynamicTypeBuilder::DynamicTypeBuilder(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const rosidl_runtime_c__type_description__TypeDescription * description)
|
||||
: serialization_support_(serialization_support),
|
||||
rosidl_dynamic_type_builder_(nullptr)
|
||||
{
|
||||
if (!serialization_support) {
|
||||
throw std::runtime_error("serialization support cannot be nullptr!");
|
||||
}
|
||||
init_from_description(description, serialization_support);
|
||||
}
|
||||
|
||||
|
||||
DynamicTypeBuilder::DynamicTypeBuilder(const DynamicTypeBuilder & other)
|
||||
: enable_shared_from_this(), serialization_support_(nullptr), rosidl_dynamic_type_builder_(nullptr)
|
||||
{
|
||||
DynamicTypeBuilder out = other.clone();
|
||||
std::swap(serialization_support_, out.serialization_support_);
|
||||
std::swap(rosidl_dynamic_type_builder_, out.rosidl_dynamic_type_builder_);
|
||||
}
|
||||
|
||||
|
||||
DynamicTypeBuilder::DynamicTypeBuilder(DynamicTypeBuilder && other) noexcept
|
||||
: serialization_support_(std::exchange(other.serialization_support_, nullptr)),
|
||||
rosidl_dynamic_type_builder_(std::exchange(other.rosidl_dynamic_type_builder_, nullptr)) {}
|
||||
|
||||
|
||||
DynamicTypeBuilder &
|
||||
DynamicTypeBuilder::operator=(const DynamicTypeBuilder & other)
|
||||
{
|
||||
return *this = DynamicTypeBuilder(other);
|
||||
}
|
||||
|
||||
|
||||
DynamicTypeBuilder &
|
||||
DynamicTypeBuilder::operator=(DynamicTypeBuilder && other) noexcept
|
||||
{
|
||||
std::swap(serialization_support_, other.serialization_support_);
|
||||
std::swap(rosidl_dynamic_type_builder_, other.rosidl_dynamic_type_builder_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
DynamicTypeBuilder::~DynamicTypeBuilder() {}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::init_from_description(
|
||||
const rosidl_runtime_c__type_description__TypeDescription * description,
|
||||
DynamicSerializationSupport::SharedPtr serialization_support)
|
||||
{
|
||||
if (!description) {
|
||||
throw std::runtime_error("description cannot be nullptr!");
|
||||
}
|
||||
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 = nullptr;
|
||||
rosidl_dynamic_type_builder =
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_init_from_description(
|
||||
serialization_support_->get_rosidl_serialization_support(), description);
|
||||
if (!rosidl_dynamic_type_builder) {
|
||||
throw std::runtime_error("could not create new dynamic type builder object");
|
||||
}
|
||||
|
||||
rosidl_dynamic_type_builder_.reset(
|
||||
rosidl_dynamic_type_builder,
|
||||
// Custom deleter
|
||||
[](rosidl_dynamic_typesupport_dynamic_type_builder_t * rosidl_dynamic_type_builder)->void {
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_fini(rosidl_dynamic_type_builder);
|
||||
free(rosidl_dynamic_type_builder);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::init_from_serialization_support_(
|
||||
DynamicSerializationSupport::SharedPtr serialization_support,
|
||||
const std::string & name)
|
||||
{
|
||||
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 = nullptr;
|
||||
rosidl_dynamic_type_builder = rosidl_dynamic_typesupport_dynamic_type_builder_init(
|
||||
serialization_support->get_rosidl_serialization_support(), name.c_str(), name.size());
|
||||
|
||||
if (!rosidl_dynamic_type_builder) {
|
||||
throw std::runtime_error("could not create new dynamic type builder object");
|
||||
}
|
||||
|
||||
rosidl_dynamic_type_builder_.reset(
|
||||
rosidl_dynamic_type_builder,
|
||||
// Custom deleter
|
||||
[](rosidl_dynamic_typesupport_dynamic_type_builder_t * rosidl_dynamic_type_builder)->void {
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_fini(rosidl_dynamic_type_builder);
|
||||
free(rosidl_dynamic_type_builder);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DynamicTypeBuilder::match_serialization_support_(
|
||||
const DynamicSerializationSupport & serialization_support,
|
||||
const rosidl_dynamic_typesupport_dynamic_type_builder_t & rosidl_dynamic_type_builder)
|
||||
{
|
||||
bool out = true;
|
||||
|
||||
if (serialization_support.get_library_identifier() != std::string(
|
||||
rosidl_dynamic_type_builder.serialization_support->library_identifier))
|
||||
{
|
||||
RCUTILS_LOG_ERROR(
|
||||
"serialization support library identifier does not match dynamic type builder's");
|
||||
out = false;
|
||||
}
|
||||
|
||||
// TODO(methylDragon): Can I do this?? Is it portable?
|
||||
if (serialization_support.get_rosidl_serialization_support() !=
|
||||
rosidl_dynamic_type_builder.serialization_support)
|
||||
{
|
||||
RCUTILS_LOG_ERROR(
|
||||
"serialization support pointer does not match dynamic type builder's");
|
||||
out = false;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// GETTERS =======================================================================================
|
||||
const std::string
|
||||
DynamicTypeBuilder::get_library_identifier() const
|
||||
{
|
||||
return std::string(rosidl_dynamic_type_builder_->serialization_support->library_identifier);
|
||||
}
|
||||
|
||||
|
||||
const std::string
|
||||
DynamicTypeBuilder::get_name() const
|
||||
{
|
||||
size_t buf_length;
|
||||
const char * buf = rosidl_dynamic_typesupport_dynamic_type_builder_get_name(
|
||||
get_rosidl_dynamic_type_builder(), &buf_length);
|
||||
return std::string(buf, buf_length);
|
||||
}
|
||||
|
||||
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_t *
|
||||
DynamicTypeBuilder::get_rosidl_dynamic_type_builder()
|
||||
{
|
||||
return rosidl_dynamic_type_builder_.get();
|
||||
}
|
||||
|
||||
|
||||
const rosidl_dynamic_typesupport_dynamic_type_builder_t *
|
||||
DynamicTypeBuilder::get_rosidl_dynamic_type_builder() const
|
||||
{
|
||||
return rosidl_dynamic_type_builder_.get();
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<rosidl_dynamic_typesupport_dynamic_type_builder_t>
|
||||
DynamicTypeBuilder::get_shared_rosidl_dynamic_type_builder()
|
||||
{
|
||||
return std::shared_ptr<rosidl_dynamic_typesupport_dynamic_type_builder_t>(
|
||||
shared_from_this(), rosidl_dynamic_type_builder_.get());
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<const rosidl_dynamic_typesupport_dynamic_type_builder_t>
|
||||
DynamicTypeBuilder::get_shared_rosidl_dynamic_type_builder() const
|
||||
{
|
||||
return std::shared_ptr<rosidl_dynamic_typesupport_dynamic_type_builder_t>(
|
||||
shared_from_this(), rosidl_dynamic_type_builder_.get());
|
||||
}
|
||||
|
||||
|
||||
DynamicSerializationSupport::SharedPtr
|
||||
DynamicTypeBuilder::get_shared_dynamic_serialization_support()
|
||||
{
|
||||
return serialization_support_;
|
||||
}
|
||||
|
||||
|
||||
DynamicSerializationSupport::ConstSharedPtr
|
||||
DynamicTypeBuilder::get_shared_dynamic_serialization_support() const
|
||||
{
|
||||
return serialization_support_;
|
||||
}
|
||||
|
||||
|
||||
// METHODS =======================================================================================
|
||||
void
|
||||
DynamicTypeBuilder::set_name(const std::string & name)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_set_name(
|
||||
get_rosidl_dynamic_type_builder(), name.c_str(), name.size());
|
||||
}
|
||||
|
||||
|
||||
DynamicTypeBuilder
|
||||
DynamicTypeBuilder::clone() const
|
||||
{
|
||||
return DynamicTypeBuilder(
|
||||
serialization_support_,
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_clone(get_rosidl_dynamic_type_builder()));
|
||||
}
|
||||
|
||||
|
||||
DynamicTypeBuilder::SharedPtr
|
||||
DynamicTypeBuilder::clone_shared() const
|
||||
{
|
||||
return DynamicTypeBuilder::make_shared(
|
||||
serialization_support_,
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_clone(get_rosidl_dynamic_type_builder()));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::clear()
|
||||
{
|
||||
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);
|
||||
if (!rosidl_dynamic_type_builder_) {
|
||||
throw std::runtime_error("could not create new dynamic type builder object");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DynamicData
|
||||
DynamicTypeBuilder::build_data()
|
||||
{
|
||||
return DynamicData(shared_from_this());
|
||||
}
|
||||
|
||||
|
||||
DynamicData::SharedPtr
|
||||
DynamicTypeBuilder::build_data_shared()
|
||||
{
|
||||
return DynamicData::make_shared(shared_from_this());
|
||||
}
|
||||
|
||||
|
||||
DynamicType
|
||||
DynamicTypeBuilder::build_type()
|
||||
{
|
||||
return DynamicType(shared_from_this());
|
||||
}
|
||||
|
||||
|
||||
DynamicType::SharedPtr
|
||||
DynamicTypeBuilder::build_type_shared()
|
||||
{
|
||||
return DynamicType::make_shared(shared_from_this());
|
||||
}
|
||||
|
||||
|
||||
// ADD MEMBERS =====================================================================================
|
||||
// Defined in "detail/dynamic_type_builder_impl.hpp"
|
||||
|
||||
|
||||
// ADD BOUNDED STRING MEMBERS ======================================================================
|
||||
void
|
||||
DynamicTypeBuilder::add_bounded_string_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_bound)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_member(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(), string_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::add_bounded_wstring_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_member(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(), wstring_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::add_bounded_string_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t string_bound, size_t array_length)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_array_member(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(), string_bound, array_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::add_bounded_wstring_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
size_t wstring_bound, size_t array_length)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_array_member(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(), wstring_bound, array_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::add_bounded_string_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_bound)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_unbounded_sequence_member(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(), string_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::add_bounded_wstring_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_unbounded_sequence_member(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(), wstring_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::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)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_bounded_sequence_member(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(),
|
||||
string_bound, sequence_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::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)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_bounded_sequence_member(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(),
|
||||
wstring_bound, sequence_bound);
|
||||
}
|
||||
|
||||
|
||||
// ADD NESTED MEMBERS ==============================================================================
|
||||
void
|
||||
DynamicTypeBuilder::add_complex_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicType & nested_type)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_member(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(),
|
||||
nested_type.get_rosidl_dynamic_type());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::add_complex_array_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicType & nested_type, size_t array_length)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_array_member(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(),
|
||||
nested_type.get_rosidl_dynamic_type(), array_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::add_complex_unbounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicType & nested_type)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_unbounded_sequence_member(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(),
|
||||
nested_type.get_rosidl_dynamic_type());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::add_complex_bounded_sequence_member(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicType & nested_type, size_t sequence_bound)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_bounded_sequence_member(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(),
|
||||
nested_type.get_rosidl_dynamic_type(), sequence_bound);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::add_complex_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicTypeBuilder & nested_type_builder)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_member_builder(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(),
|
||||
nested_type_builder.get_rosidl_dynamic_type_builder());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::add_complex_array_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicTypeBuilder & nested_type_builder, size_t array_length)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_array_member_builder(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(),
|
||||
nested_type_builder.get_rosidl_dynamic_type_builder(), array_length);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::add_complex_unbounded_sequence_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicTypeBuilder & nested_type_builder)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_unbounded_sequence_member_builder(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(),
|
||||
nested_type_builder.get_rosidl_dynamic_type_builder());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DynamicTypeBuilder::add_complex_bounded_sequence_member_builder(
|
||||
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
|
||||
DynamicTypeBuilder & nested_type_builder, size_t sequence_bound)
|
||||
{
|
||||
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_bounded_sequence_member_builder(
|
||||
get_rosidl_dynamic_type_builder(), id, name.c_str(), name.size(),
|
||||
nested_type_builder.get_rosidl_dynamic_type_builder(), sequence_bound);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
{
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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