Compare commits

...

33 Commits

Author SHA1 Message Date
Guillaume Papin
d00a441195 move user-defined literals in their own namespace (#284)
* isolate chrono literals in literals.hpp

* rclcpp.hpp: remove 'using namespace rclcpp::literals'

The examples have been migrated to the new namespace.

* literals: constexpr, make return types consistent with function body

_ms returned nanoseconds instead of milliseconds.

A few return types where using integral return type
for floating point literals.

* remove literals in favor of std::chrono_literals
2016-12-17 02:16:43 -08:00
Karsten Knese
2c6d95946e add rclcpp lifecycle
* initial state machine implementation

(fix) correctly initialize default state machine

uncrustify

(dev) first high level api interface

src/default_state_machine.c

(fix) correctly initialize arrays in statemachine

(dev) deactivate/activate publisher demo

(dev) initial state machine implementation in rcl

* (dev) demo application for a managed lifecycle node

* add visibility control

* correct install of c-library

* fix compilation on windows

* refactoring of external/internal api

* (dev) generate static functions for c-callback

* (fix) correct typo

* (dev) cleanup for c-statemachine

(dev) cleanup for c-statemachine

* (dev) cpp callback map

* (dev) mv source file into project folders

* (dev) more helper functions for valid transition

* (dev) pimpl implementation for cpp lifecyclemanager

* (dev) register non-default callback functions

* (dev) cleanup lifecycle node to serve as base class

* (dev) new my_node child of lifecyclenode for demo purpose

update

stuff

(cleanup) remove unused comments

(fix) correct dllexport in windows

(fix) correctly install libraries

(fix) uncrustify

(dev) composition over inheritance

(dev) publish notification in state_machine transition

 (dev) lifecycle talker + listener demo for notification

(dev) custom transition message generation

(dev) publish transition message on state change

(dev) correctly malloc/free c data structures

(fix) use single thread executor

(dev) use services for get state

(fix) set freed pointer to NULL

(dev) add change state service

(dev) introduce services: get_state and change_state in LM

(dev) asynchronous caller script for service client

(fix) correct dllexport for pimpl

(dev) correct constness

(dev) concatenate function for topic

(fix) uncrustify

prepare new service api

(tmp) refactor stash

(fix) correctly concatenate topics

(fix) correctly initialize Service wo/ copy

(dev) call both service types

extract demo files

(fix) remove debug prints

(dev) change to lifecycle_msgs

(refactor) extract rcl_lifecycle package

(refactor) extract lifecycle demos

(fix) address review comments

(fix) address review comments

(fix) pass shared_ptr by value

(fix) make find_package(rmw) required

(fix) return to shared node handle pointer

(refactor) attach sm to lifecycle node and disable lc_manager

(dev) construct service from existing rcl_service_t

(refactor) extract method for adding a service to a node

(fix) stop mock msgs from being installed

service takes rcl_node_t*

correct typo

add_service has to be public

uncrustify

initial state machine implementation

(fix) correctly initialize default state machine

uncrustify

(dev) first high level api interface

src/default_state_machine.c

(fix) correctly initialize arrays in statemachine

(dev) deactivate/activate publisher demo

(dev) initial state machine implementation in rcl

(dev) demo application for a managed lifecycle node

add visibility control

correct install of c-library

fix compilation on windows

refactoring of external/internal api

(dev) generate static functions for c-callback

(fix) correct typo

(dev) cleanup for c-statemachine

(dev) cleanup for c-statemachine

(dev) cpp callback map

(dev) mv source file into project folders

(dev) more helper functions for valid transition

(dev) pimpl implementation for cpp lifecyclemanager

(dev) register non-default callback functions

(dev) cleanup lifecycle node to serve as base class

(dev) new my_node child of lifecyclenode for demo purpose

update

stuff

(cleanup) remove unused comments

(fix) correct dllexport in windows

(fix) correctly install libraries

(fix) uncrustify

(dev) composition over inheritance

(dev) publish notification in state_machine transition

 (dev) lifecycle talker + listener demo for notification

(dev) custom transition message generation

(dev) publish transition message on state change

(dev) correctly malloc/free c data structures

(fix) use single thread executor

(dev) use services for get state

(fix) set freed pointer to NULL

(dev) add change state service

(dev) introduce services: get_state and change_state in LM

(dev) asynchronous caller script for service client

(fix) correct dllexport for pimpl

(dev) correct constness

(dev) concatenate function for topic

(fix) uncrustify

prepare new service api

(tmp) refactor stash

* (dev) construct service from existing rcl_service_t

* service takes rcl_node_t*

* correct typo

* add_service has to be public

* uncrustify

* (fix) correctly concatenate topics

* (fix) correctly initialize Service wo/ copy

* (dev) call both service types

* extract demo files

* (fix) remove debug prints

* (dev) change to lifecycle_msgs

* (refactor) extract rcl_lifecycle package

* (refactor) extract lifecycle demos

* (fix) address review comments

(fix) address review comments

* (fix) make find_package(rmw) required

* (refactor) attach sm to lifecycle node and disable lc_manager

* (fix) adjust code to rcl_test refactor

* (dev) remove unused deps

* (rebase) merge commit

* (bugfix) correct rcl_ret_t error handling

* (fix) depedencies

* (refactor) change to lifecycle_msgs

* (fix) correct find_rcl

* (refactor) comply for new state machine

* visibility control and test api

* (rebase) change to new typesupport

* uncrustify'

* fix visibility control

* (fix) correct whitespace

* (fix) unused variable

* comparison signed and unsigned

* get_state returns complete state

* get_available_states service

* new service msgs

* get available states and transitions api

* (broken) state after rebase, does not compile demos

* fix the way lifecycle node impl is included

* fixed rebase compilation errors

* remove copy&paste comment

* remove empty line

* (test) register custom callbacks

* (dev) return codes

* style

* test for exception handling

* refacotr new state machine

* c++14

* change exception message for windows ci bug

change exception message for windows ci bug
2016-12-14 09:29:27 -08:00
Morgan Quigley
d241a730fe c++14 (#287) 2016-12-13 14:41:22 -08:00
William Woodall
cc98d00add remove unused include (#291) 2016-12-12 11:56:59 -08:00
William Woodall
e2f53b09b4 fix the return type of create_subscription (#290) 2016-12-09 22:25:09 -08:00
William Woodall
734ac278db break Node into several separate interfaces (#277)
* add the NodeBaseInterface and impl NodeBase

* refactor rclcpp to use NodeBaseInterface

* triggering a guard condition is not const

* remove unnecessary pure virtual destructor

* remove unused private member, style

* create NodeTopics interface, refactor pub/sub

* add convenience functions to fix API breaks

* fix compilation errors from NodeTopics refactor

* move "Event" based exceptions to exceptions.hpp

* add the NodeGraphInterface and related API's

* update node and graph_listener to use NodeGraph API

* initialize node_topics_ and node_graph_ in Node

* remove methods from Node and reorganize the order

the removed methods are really low level and still
available via their respective Node*Interface class

* add the NodeServices API and implementation

* add the NodeParameters API and refactor Node

* mixups

* fixup NodeParameters constructor

* added NodeTimers API and refactor Node

* make new create_publisher and create_subscription free template functions

* fixup

* fixup

* fixup

* fixup share pointer to node in any_executable

* free env value before throwing on Windows

* uncrustify and cpplint

* address constness issues

* do not store the topic name as a std::string in subscription

* fixes to support const char * topic name

* fix incomplete type specification, which fails on Windows

* refactor after rebase from type support changes

* fixup Windows build

* fix template issues on Windows

* uncrustify

* remove the unnecessary callback group argument from the add_publisher func

* remove unnecessary using = directive

* do not store node name in C++

* fix client and service creation in Node constructor

* fix include orders
2016-12-09 17:09:29 -08:00
Dirk Thomas
2309e5e250 Merge pull request #285 from ros2/typesupport_reloaded
use rosidl_typesupport_cpp
2016-12-08 14:00:42 -08:00
Dirk Thomas
3fe1924c63 pass custom LIBRARY_NAME 2016-12-06 14:54:31 -08:00
Dirk Thomas
3405f489d3 use rosidl_typesupport_cpp 2016-12-06 11:07:11 -08:00
Karsten Knese
1b5168195b construct service from existing rcl_service_t (#279)
* (dev) construct service from existing rcl_service_t

* (refactor) extract method for adding a service to a node

* (fix) stop mock msgs from being installed

* service takes rcl_node_t*

* correct typo

* add_service has to be public

* uncrustify

* correctly initialize service_handle

* (fix) address review comments

* (fix) pass shared pointer by value

* (fix) return to shared node handle pointer

* (fix) make find_package(rmw) required

* style

* (revert) leave c++11 flags within CXX flags

* (fix) unused variable warning

* (fix) remove unnecessary if in cmake
2016-12-02 01:05:59 -08:00
geoffviola
a987f8d015 removed extra semi-colon (#253)
* removed extra semi-colon

* added -Wpedantic flag for GCC

* added same warnings for clang

* simplified CMake command
2016-11-28 14:11:22 -08:00
Geoffrey Biggs
aa2d0a3954 Fix has_invalid_weak_nodes (#266)
* Fix #264

* Corrected test comment
2016-11-17 09:59:50 -08:00
Dirk Thomas
5894a9cd4e Merge pull request #270 from ros2/composition
fix allocator type
2016-11-10 16:55:02 -08:00
Dirk Thomas
80fc2a59cd Merge pull request #271 from ros2/fix_error_reporting
fix error reporting for services
2016-11-10 13:25:29 -08:00
Dirk Thomas
d12154b1f9 fix error reporting for services 2016-11-10 11:13:19 -08:00
Dirk Thomas
a06a397cc6 fix allocator type 2016-11-10 10:09:24 -08:00
Dirk Thomas
4c876d5966 fix wrong variable name in docblock 2016-11-08 15:21:48 -08:00
Mikael Arguedas
da14d88cd6 line length (#269) 2016-11-07 18:47:05 -08:00
Dirk Thomas
01317def07 Merge pull request #268 from ros2/composition
add rclcpp_register_node_plugins macro
2016-11-07 15:41:14 -08:00
Dirk Thomas
74505f25fa add rclcpp_register_node_plugins macro 2016-11-07 15:21:53 -08:00
Dirk Thomas
ec71e6562e move CMake function into separate file 2016-11-07 10:55:17 -08:00
Karsten Knese
7f714a8601 open Node/Publisher API for allowing inheritance (#258)
* (dev) template create_publisher with publisher type

* (dev) template publisher type for dynamic publisher type instantiation

* (dev) make Publisher::publish function virtual

* (fix) uncrustify

* different indentation of long template declaration
2016-11-01 15:07:58 -07:00
William Woodall
7494350ad2 always check if the service is available, even if the graph event wasn't triggered (#262)
* always check if the service is available, even if the graph event wasn't triggered

* more descriptive comment

* Even more descriptive in case the link ever breaks
2016-10-28 18:31:15 -07:00
Rafał Kozik
d158dd46db CallbackGroup keeps now WeakPtrs to Services. (#261)
It was not possible to shutdown a service when there
was a shared pointer preventing destructor from being called.
2016-10-28 15:59:46 -07:00
Mikael Arguedas
a71ce25a5a Zero init topic names and types (#260)
* use zero init topic_names_and_type struct

* use the right arguments

* line length
2016-10-27 19:02:35 -07:00
Dirk Thomas
f28bb11078 Merge pull request #255 from ros2/fix_cpplint
comply with stricter cpplint rules
2016-10-03 16:17:03 -07:00
Dirk Thomas
29a1bd44dc comply with stricter cpplint rules 2016-10-01 11:23:53 -07:00
William Woodall
4e74edf8d4 fixup comment 2016-09-15 15:41:48 -07:00
William Woodall
6ea435f743 Issue 251 wjwwood (#252)
* removed warning in windows

* removed warning in windows

* fixup
2016-09-06 16:35:10 -07:00
Rohan Agrawal
902d558e64 Get single parameter (#245)
* added function to get parameter by exact name

* added ros1 style get_parameter for parameter variant

* added ros1 style get_param for non-variant types

* Make the get_parameter functions call a private base function

* Parameter Variant requires name in constructor

* Cleaned up to no longer need private function

* Made exception message more clear
2016-08-01 19:49:21 -07:00
Rohan Agrawal
1402715d76 Added basic hook for parameter changes (#244)
* Added basic hook for parameter changes

* Rename hook and add docblock
2016-07-28 18:01:24 -07:00
Dirk Thomas
8c5f6e4e06 Merge pull request #242 from SantiagoMunoz/setparameters
Added missing variable initialization in Node constructor
2016-07-20 08:17:16 -07:00
Santiago Munoz
2401c0f197 Added missing variable initialization in Node constructor 2016-07-20 16:56:33 +02:00
103 changed files with 6416 additions and 1140 deletions

View File

@@ -9,9 +9,11 @@ find_package(rmw REQUIRED)
find_package(rmw_implementation REQUIRED)
find_package(rmw_implementation_cmake REQUIRED)
find_package(rosidl_generator_cpp REQUIRED)
find_package(rosidl_typesupport_cpp REQUIRED)
if(NOT WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wextra")
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-std=c++14")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
include_directories(include)
@@ -33,11 +35,17 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/intra_process_manager_impl.cpp
src/rclcpp/memory_strategies.cpp
src/rclcpp/memory_strategy.cpp
src/rclcpp/node.cpp
src/rclcpp/node_interfaces/node_base.cpp
src/rclcpp/node_interfaces/node_graph.cpp
src/rclcpp/node_interfaces/node_parameters.cpp
src/rclcpp/node_interfaces/node_services.cpp
src/rclcpp/node_interfaces/node_timers.cpp
src/rclcpp/node_interfaces/node_topics.cpp
src/rclcpp/parameter.cpp
src/rclcpp/parameter_client.cpp
src/rclcpp/parameter_service.cpp
src/rclcpp/publisher.cpp
src/rclcpp/node.cpp
src/rclcpp/service.cpp
src/rclcpp/subscription.cpp
src/rclcpp/timer.cpp
@@ -53,7 +61,8 @@ macro(target)
${${PROJECT_NAME}_SRCS})
ament_target_dependencies(${PROJECT_NAME}${target_suffix}
"rcl${target_suffix}"
"rosidl_generator_cpp")
"rosidl_generator_cpp"
"rosidl_typesupport_cpp")
# Causes the visibility macros to use dllexport rather than dllimport,
# which is appropriate when building the dll but not consuming it.
@@ -73,6 +82,7 @@ call_for_each_rmw_implementation(target GENERATE_DEFAULT)
ament_export_dependencies(ament_cmake)
ament_export_dependencies(rcl)
ament_export_dependencies(rosidl_generator_cpp)
ament_export_dependencies(rosidl_typesupport_cpp)
ament_export_include_directories(include)
@@ -88,6 +98,7 @@ if(BUILD_TESTING)
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
endif()
ament_add_gtest(test_mapped_ring_buffer test/test_mapped_ring_buffer.cpp)
@@ -97,6 +108,7 @@ if(BUILD_TESTING)
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
endif()
ament_add_gtest(test_intra_process_manager test/test_intra_process_manager.cpp)
@@ -106,6 +118,7 @@ if(BUILD_TESTING)
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
endif()
ament_add_gtest(test_rate test/test_rate.cpp
@@ -115,11 +128,52 @@ if(BUILD_TESTING)
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_rate
${PROJECT_NAME}
)
endif()
ament_add_gtest(test_find_weak_nodes test/test_find_weak_nodes.cpp)
if(TARGET test_find_weak_nodes)
target_include_directories(test_find_weak_nodes PUBLIC
${rcl_INCLUDE_DIRS}
)
target_link_libraries(test_find_weak_nodes ${PROJECT_NAME})
endif()
get_default_rmw_implementation(default_rmw)
find_package(${default_rmw} REQUIRED)
get_rmw_typesupport(typesupport_impls_cpp "${default_rmw}" LANGUAGE "cpp")
get_rmw_typesupport(typesupport_impls_c "${default_rmw}" LANGUAGE "c")
set(mock_msg_files
"test/mock_msgs/srv/Mock.srv")
rosidl_generate_interfaces(mock_msgs
${mock_msg_files}
LIBRARY_NAME "rclcpp"
SKIP_INSTALL)
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
if(WIN32)
set(append_library_dirs "${append_library_dirs}/$<CONFIGURATION>")
endif()
ament_add_gtest(test_externally_defined_services test/test_externally_defined_services.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_externally_defined_services)
target_include_directories(test_externally_defined_services PUBLIC
${rcl_INCLUDE_DIRS}
)
target_link_libraries(test_externally_defined_services ${PROJECT_NAME})
foreach(typesupport_impl_cpp ${typesupport_impls_cpp})
rosidl_target_interfaces(test_externally_defined_services
mock_msgs ${typesupport_impl_cpp})
endforeach()
foreach(typesupport_impl_c ${typesupport_impls_c})
rosidl_target_interfaces(test_externally_defined_services
mock_msgs ${typesupport_impl_c})
endforeach()
endif()
endif()
ament_package(

View File

@@ -19,7 +19,7 @@
# with the given prefix.
#
# :param rmw_implementation: the RMW implementation name
# :type target: string
# :type rmw_implementation: string
# :param var_prefix: the prefix of all output variable names
# :type var_prefix: string
#
@@ -69,7 +69,8 @@ macro(get_rclcpp_information rmw_implementation var_prefix)
set(_exported_dependencies
"rcl_interfaces"
"rcl${target_suffix}"
"rosidl_generator_cpp")
"rosidl_generator_cpp"
"rosidl_typesupport_cpp")
set(${var_prefix}_DEFINITIONS)
foreach(_dep ${_exported_dependencies})
if(NOT ${_dep}_FOUND)

View File

@@ -0,0 +1,26 @@
# Copyright 2015 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set(rclcpp_node_main_SRC "${rclcpp_DIR}/../../../src/rclcpp/node_main.cpp")
function(rclcpp_create_node_main node_library_target)
if(NOT TARGET ${node_library_target})
message(FATAL_ERROR "rclcpp_create_node_main() the first argument must be a valid target name")
endif()
set(executable_name_ ${node_library_target}_node)
add_executable(${executable_name_} ${rclcpp_node_main_SRC})
target_link_libraries(${executable_name_} ${node_library_target})
install(TARGETS ${executable_name_} DESTINATION bin)
endfunction()

View File

@@ -0,0 +1,17 @@
# Copyright 2016 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.
# register node plugins
ament_index_register_resource(
"node_plugin" CONTENT "${_RCLCPP__NODE_PLUGINS}")

View File

@@ -0,0 +1,61 @@
# Copyright 2016 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.
#
# Register a node plugin with the ament resource index.
#
# The passed library can contain multiple plugins extending the node interface.
#
# :param target: the shared library target
# :type target: string
# :param ARGN: the unique plugin names being exported using class_loader
# :type ARGN: list of strings
#
macro(rclcpp_register_node_plugins target)
if(NOT TARGET ${target})
message(
FATAL_ERROR
"rclcpp_register_node_plugins() first argument "
"'${target}' is not a target")
endif()
get_target_property(_target_type ${target} TYPE)
if(NOT _target_type STREQUAL "SHARED_LIBRARY")
message(
FATAL_ERROR
"rclcpp_register_node_plugins() first argument "
"'${target}' is not a shared library target")
endif()
if(${ARGC} GREATER 0)
_rclcpp_register_package_hook()
set(_unique_names)
foreach(_arg ${ARGN})
if(_arg IN_LIST _unique_names)
message(
FATAL_ERROR
"rclcpp_register_node_plugins() the plugin names "
"must be unique (multiple '${_arg}')")
endif()
list(APPEND _unique_names "${_arg}")
if(WIN32)
set(_path "bin")
else()
set(_path "lib")
endif()
set(_RCLCPP__NODE_PLUGINS
"${_RCLCPP__NODE_PLUGINS}${_arg};${_path}/$<TARGET_FILE_NAME:${target}>\n")
endforeach()
endif()
endmacro()

View File

@@ -74,6 +74,8 @@ rcl_allocator_t get_rcl_allocator(Alloc & allocator)
rcl_allocator.deallocate = &retyped_deallocate<T, Alloc>;
rcl_allocator.reallocate = &retyped_reallocate<T, Alloc>;
rcl_allocator.state = &allocator;
#else
(void)allocator; // Remove warning
#endif
return rcl_allocator;
}

View File

@@ -17,8 +17,13 @@
#include <memory>
#include "rclcpp/callback_group.hpp"
#include "rclcpp/client.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/timer.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
@@ -28,7 +33,7 @@ namespace executor
struct AnyExecutable
{
RCLCPP_SMART_PTR_DEFINITIONS(AnyExecutable);
RCLCPP_SMART_PTR_DEFINITIONS(AnyExecutable)
RCLCPP_PUBLIC
AnyExecutable();
@@ -44,7 +49,7 @@ struct AnyExecutable
rclcpp::client::ClientBase::SharedPtr client;
// These are used to keep the scope on the containing items
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group;
rclcpp::node::Node::SharedPtr node;
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base;
};
} // namespace executor

View File

@@ -21,6 +21,7 @@
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/function_traits.hpp"

View File

@@ -30,10 +30,12 @@ namespace rclcpp
{
// Forward declarations for friend statement in class CallbackGroup
namespace node
namespace node_interfaces
{
class Node;
} // namespace node
class NodeServices;
class NodeTimers;
class NodeTopics;
} // namespace node_interfaces
namespace callback_group
{
@@ -46,10 +48,12 @@ enum class CallbackGroupType
class CallbackGroup
{
friend class rclcpp::node::Node;
friend class rclcpp::node_interfaces::NodeServices;
friend class rclcpp::node_interfaces::NodeTimers;
friend class rclcpp::node_interfaces::NodeTopics;
public:
RCLCPP_SMART_PTR_DEFINITIONS(CallbackGroup);
RCLCPP_SMART_PTR_DEFINITIONS(CallbackGroup)
RCLCPP_PUBLIC
explicit CallbackGroup(CallbackGroupType group_type);
@@ -63,7 +67,7 @@ public:
get_timer_ptrs() const;
RCLCPP_PUBLIC
const std::vector<rclcpp::service::ServiceBase::SharedPtr> &
const std::vector<rclcpp::service::ServiceBase::WeakPtr> &
get_service_ptrs() const;
RCLCPP_PUBLIC
@@ -78,8 +82,8 @@ public:
const CallbackGroupType &
type() const;
private:
RCLCPP_DISABLE_COPY(CallbackGroup);
protected:
RCLCPP_DISABLE_COPY(CallbackGroup)
RCLCPP_PUBLIC
void
@@ -102,7 +106,7 @@ private:
mutable std::mutex mutex_;
std::vector<rclcpp::subscription::SubscriptionBase::WeakPtr> subscription_ptrs_;
std::vector<rclcpp::timer::TimerBase::WeakPtr> timer_ptrs_;
std::vector<rclcpp::service::ServiceBase::SharedPtr> service_ptrs_;
std::vector<rclcpp::service::ServiceBase::WeakPtr> service_ptrs_;
std::vector<rclcpp::client::ClientBase::WeakPtr> client_ptrs_;
std::atomic_bool can_be_taken_from_;
};

View File

@@ -29,6 +29,7 @@
#include "rclcpp/function_traits.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -39,10 +40,10 @@
namespace rclcpp
{
namespace node
namespace node_interfaces
{
class Node;
} // namespace node
class NodeBaseInterface;
} // namespace node_interfaces
namespace client
{
@@ -50,11 +51,12 @@ namespace client
class ClientBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ClientBase);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ClientBase)
RCLCPP_PUBLIC
ClientBase(
std::shared_ptr<rclcpp::node::Node> parent_node,
rclcpp::node_interfaces::NodeBaseInterface * node_base,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
const std::string & service_name);
RCLCPP_PUBLIC
@@ -88,7 +90,7 @@ public:
std::shared_ptr<rmw_request_id_t> request_header, std::shared_ptr<void> response) = 0;
protected:
RCLCPP_DISABLE_COPY(ClientBase);
RCLCPP_DISABLE_COPY(ClientBase)
RCLCPP_PUBLIC
bool
@@ -98,7 +100,7 @@ protected:
rcl_node_t *
get_rcl_node_handle() const;
std::weak_ptr<rclcpp::node::Node> node_;
rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_;
std::shared_ptr<rcl_node_t> node_handle_;
rcl_client_t client_handle_ = rcl_get_zero_initialized_client();
@@ -124,15 +126,16 @@ public:
using CallbackType = std::function<void(SharedFuture)>;
using CallbackWithRequestType = std::function<void(SharedFutureWithRequest)>;
RCLCPP_SMART_PTR_DEFINITIONS(Client);
RCLCPP_SMART_PTR_DEFINITIONS(Client)
Client(
std::shared_ptr<rclcpp::node::Node> parent_node,
rclcpp::node_interfaces::NodeBaseInterface * node_base,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
const std::string & service_name,
rcl_client_options_t & client_options)
: ClientBase(parent_node, service_name)
: ClientBase(node_base, node_graph, service_name)
{
using rosidl_generator_cpp::get_service_type_support_handle;
using rosidl_typesupport_cpp::get_service_type_support_handle;
auto service_type_support_handle =
get_service_type_support_handle<ServiceT>();
if (rcl_client_init(&client_handle_, this->get_rcl_node_handle(),
@@ -250,7 +253,7 @@ public:
}
private:
RCLCPP_DISABLE_COPY(Client);
RCLCPP_DISABLE_COPY(Client)
std::map<int64_t, std::tuple<SharedPromise, CallbackType, SharedFuture>> pending_requests_;
std::mutex pending_requests_mutex_;

View File

@@ -21,6 +21,7 @@
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
#include <utility>
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -35,7 +36,7 @@ namespace context
class Context
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(Context);
RCLCPP_SMART_PTR_DEFINITIONS(Context)
RCLCPP_PUBLIC
Context();
@@ -67,7 +68,7 @@ public:
}
private:
RCLCPP_DISABLE_COPY(Context);
RCLCPP_DISABLE_COPY(Context)
std::unordered_map<std::type_index, std::shared_ptr<void>> sub_contexts_;
std::mutex mutex_;

View File

@@ -28,7 +28,7 @@ namespace default_context
class DefaultContext : public rclcpp::context::Context
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(DefaultContext);
RCLCPP_SMART_PTR_DEFINITIONS(DefaultContext)
RCLCPP_PUBLIC
DefaultContext();

View File

@@ -0,0 +1,51 @@
// Copyright 2016 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__CREATE_PUBLISHER_HPP_
#define RCLCPP__CREATE_PUBLISHER_HPP_
#include <memory>
#include <string>
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/publisher_factory.hpp"
#include "rmw/qos_profiles.h"
namespace rclcpp
{
template<typename MessageT, typename AllocatorT, typename PublisherT>
std::shared_ptr<PublisherT>
create_publisher(
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
const std::string & topic_name,
const rmw_qos_profile_t & qos_profile,
bool use_intra_process_comms,
std::shared_ptr<AllocatorT> allocator)
{
auto publisher_options = rcl_publisher_get_default_options();
publisher_options.qos = qos_profile;
auto pub = node_topics->create_publisher(
topic_name,
rclcpp::create_publisher_factory<MessageT, AllocatorT, PublisherT>(allocator),
publisher_options,
use_intra_process_comms);
node_topics->add_publisher(pub);
return std::dynamic_pointer_cast<PublisherT>(pub);
}
} // namespace rclcpp
#endif // RCLCPP__CREATE_PUBLISHER_HPP_

View File

@@ -0,0 +1,62 @@
// Copyright 2016 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__CREATE_SUBSCRIPTION_HPP_
#define RCLCPP__CREATE_SUBSCRIPTION_HPP_
#include <memory>
#include <string>
#include <utility>
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/subscription_factory.hpp"
#include "rmw/qos_profiles.h"
namespace rclcpp
{
template<typename MessageT, typename CallbackT, typename AllocatorT, typename SubscriptionT>
typename rclcpp::subscription::Subscription<MessageT, AllocatorT>::SharedPtr
create_subscription(
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
const std::string & topic_name,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group,
bool ignore_local_publications,
bool use_intra_process_comms,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, AllocatorT>::SharedPtr
msg_mem_strat,
typename std::shared_ptr<AllocatorT> allocator)
{
auto subscription_options = rcl_subscription_get_default_options();
subscription_options.qos = qos_profile;
subscription_options.ignore_local_publications = ignore_local_publications;
auto factory =
rclcpp::create_subscription_factory<MessageT, CallbackT, AllocatorT, SubscriptionT>(
std::forward<CallbackT>(callback), msg_mem_strat, allocator);
auto sub = node_topics->create_subscription(
topic_name,
factory,
subscription_options,
use_intra_process_comms);
node_topics->add_subscription(sub, group);
return std::dynamic_pointer_cast<SubscriptionT>(sub);
}
} // namespace rclcpp
#endif // RCLCPP__CREATE_SUBSCRIPTION_HPP_

View File

@@ -29,7 +29,7 @@ namespace event
class Event
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Event);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Event)
RCLCPP_PUBLIC
Event();
@@ -47,7 +47,7 @@ public:
check_and_clear();
private:
RCLCPP_DISABLE_COPY(Event);
RCLCPP_DISABLE_COPY(Event)
std::atomic_bool state_;
};

View File

@@ -95,6 +95,22 @@ public:
RCLInvalidArgument(const RCLErrorBase & base_exc, const std::string & prefix);
};
/// Thrown when an invalid rclcpp::Event object or SharedPtr is encountered.
class InvalidEventError : public std::runtime_error
{
public:
InvalidEventError()
: std::runtime_error("event is invalid") {}
};
/// Thrown when an unregistered rclcpp::Event is encountered where a registered one was expected.
class EventNotRegisteredError : public std::runtime_error
{
public:
EventNotRegisteredError()
: std::runtime_error("event already registered") {}
};
} // namespace exceptions
} // namespace rclcpp

View File

@@ -28,16 +28,21 @@
#include "rcl/guard_condition.h"
#include "rcl/wait.h"
#include "rclcpp/any_executable.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/memory_strategies.hpp"
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
// Forward declaration is used in convenience method signature.
namespace node
{
class Node;
} // namespace node
namespace executor
{
@@ -88,7 +93,7 @@ static inline ExecutorArgs create_default_executor_arguments()
class Executor
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Executor);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Executor)
/// Default constructor.
// \param[in] ms The memory strategy to be used with this executor.
@@ -114,7 +119,12 @@ public:
*/
RCLCPP_PUBLIC
virtual void
add_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify = true);
add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify = true);
/// Convenience function which takes Node and forwards NodeBaseInterface.
RCLCPP_PUBLIC
virtual void
add_node(std::shared_ptr<rclcpp::node::Node> node_ptr, bool notify = true);
/// Remove a node from the executor.
/**
@@ -125,7 +135,12 @@ public:
*/
RCLCPP_PUBLIC
virtual void
remove_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify = true);
remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify = true);
/// Convenience function which takes Node and forwards NodeBaseInterface.
RCLCPP_PUBLIC
virtual void
remove_node(std::shared_ptr<rclcpp::node::Node> node_ptr, bool notify = true);
/// Add a node to executor, execute the next available unit of work, and remove the node.
/**
@@ -136,7 +151,7 @@ public:
*/
template<typename T = std::milli>
void
spin_node_once(rclcpp::node::Node::SharedPtr node,
spin_node_once(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
std::chrono::duration<int64_t, T> timeout = std::chrono::duration<int64_t, T>(-1))
{
return spin_node_once_nanoseconds(
@@ -145,13 +160,30 @@ public:
);
}
/// Convenience function which takes Node and forwards NodeBaseInterface.
template<typename NodeT = rclcpp::node::Node, typename T = std::milli>
void
spin_node_once(std::shared_ptr<NodeT> node,
std::chrono::duration<int64_t, T> timeout = std::chrono::duration<int64_t, T>(-1))
{
return spin_node_once_nanoseconds(
node->get_node_base_interface(),
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
);
}
/// Add a node, complete all immediately available work, and remove the node.
/**
* \param[in] node Shared pointer to the node to add.
*/
RCLCPP_PUBLIC
void
spin_node_some(rclcpp::node::Node::SharedPtr node);
spin_node_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node);
/// Convenience function which takes Node and forwards NodeBaseInterface.
RCLCPP_PUBLIC
void
spin_node_some(std::shared_ptr<rclcpp::node::Node> node);
/// Complete all available queued work without blocking.
/**
@@ -247,7 +279,9 @@ public:
protected:
RCLCPP_PUBLIC
void
spin_node_once_nanoseconds(rclcpp::node::Node::SharedPtr node, std::chrono::nanoseconds timeout);
spin_node_once_nanoseconds(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
std::chrono::nanoseconds timeout);
/// Find the next available executable and do the work associated with it.
/** \param[in] any_exec Union structure that can hold any executable type (timer, subscription,
@@ -284,7 +318,7 @@ protected:
wait_for_work(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
RCLCPP_PUBLIC
rclcpp::node::Node::SharedPtr
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group);
RCLCPP_PUBLIC
@@ -316,9 +350,9 @@ protected:
memory_strategy::MemoryStrategy::SharedPtr memory_strategy_;
private:
RCLCPP_DISABLE_COPY(Executor);
RCLCPP_DISABLE_COPY(Executor)
std::vector<std::weak_ptr<rclcpp::node::Node>> weak_nodes_;
std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> weak_nodes_;
};
} // namespace executor

View File

@@ -16,6 +16,7 @@
#define RCLCPP__EXECUTORS_HPP_
#include <future>
#include <memory>
#include "rclcpp/executors/multi_threaded_executor.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
@@ -30,13 +31,21 @@ namespace rclcpp
// \param[in] node_ptr Shared pointer to the node to spin.
RCLCPP_PUBLIC
void
spin_some(node::Node::SharedPtr node_ptr);
spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
RCLCPP_PUBLIC
void
spin_some(rclcpp::node::Node::SharedPtr node_ptr);
/// Create a default single-threaded executor and spin the specified node.
// \param[in] node_ptr Shared pointer to the node to spin.
RCLCPP_PUBLIC
void
spin(node::Node::SharedPtr node_ptr);
spin(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
RCLCPP_PUBLIC
void
spin(rclcpp::node::Node::SharedPtr node_ptr);
namespace executors
{
@@ -57,7 +66,8 @@ using rclcpp::executors::single_threaded_executor::SingleThreadedExecutor;
template<typename ResponseT, typename TimeT = std::milli>
rclcpp::executor::FutureReturnCode
spin_node_until_future_complete(
rclcpp::executor::Executor & executor, rclcpp::node::Node::SharedPtr node_ptr,
rclcpp::executor::Executor & executor,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
std::shared_future<ResponseT> & future,
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
{
@@ -69,18 +79,44 @@ spin_node_until_future_complete(
return retcode;
}
template<typename NodeT = rclcpp::node::Node, typename ResponseT, typename TimeT = std::milli>
rclcpp::executor::FutureReturnCode
spin_node_until_future_complete(
rclcpp::executor::Executor & executor,
std::shared_ptr<NodeT> node_ptr,
std::shared_future<ResponseT> & future,
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
{
return rclcpp::executors::spin_node_until_future_complete(
executor,
node_ptr->get_node_base_interface(),
future,
timeout);
}
} // namespace executors
template<typename FutureT, typename TimeT = std::milli>
rclcpp::executor::FutureReturnCode
spin_until_future_complete(
node::Node::SharedPtr node_ptr, std::shared_future<FutureT> & future,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
std::shared_future<FutureT> & future,
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
{
rclcpp::executors::SingleThreadedExecutor executor;
return executors::spin_node_until_future_complete<FutureT>(executor, node_ptr, future, timeout);
}
template<typename NodeT = rclcpp::node::Node, typename FutureT, typename TimeT = std::milli>
rclcpp::executor::FutureReturnCode
spin_until_future_complete(
std::shared_ptr<NodeT> node_ptr,
std::shared_future<FutureT> & future,
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
{
return rclcpp::spin_until_future_complete(node_ptr->get_node_base_interface(), future, timeout);
}
} // namespace rclcpp
#endif // RCLCPP__EXECUTORS_HPP_

View File

@@ -34,7 +34,7 @@ namespace multi_threaded_executor
class MultiThreadedExecutor : public executor::Executor
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(MultiThreadedExecutor);
RCLCPP_SMART_PTR_DEFINITIONS(MultiThreadedExecutor)
RCLCPP_PUBLIC
MultiThreadedExecutor(
@@ -57,7 +57,7 @@ protected:
run(size_t this_thread_number);
private:
RCLCPP_DISABLE_COPY(MultiThreadedExecutor);
RCLCPP_DISABLE_COPY(MultiThreadedExecutor)
std::mutex wait_mutex_;
size_t number_of_threads_;

View File

@@ -42,7 +42,7 @@ namespace single_threaded_executor
class SingleThreadedExecutor : public executor::Executor
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(SingleThreadedExecutor);
RCLCPP_SMART_PTR_DEFINITIONS(SingleThreadedExecutor)
/// Default constructor. See the default constructor for Executor.
RCLCPP_PUBLIC
@@ -61,7 +61,7 @@ public:
spin();
private:
RCLCPP_DISABLE_COPY(SingleThreadedExecutor);
RCLCPP_DISABLE_COPY(SingleThreadedExecutor)
};
} // namespace single_threaded_executor

View File

@@ -24,16 +24,12 @@
#include "rcl/guard_condition.h"
#include "rcl/wait.h"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node
{
class Node;
} // namespace node
namespace graph_listener
{
@@ -91,7 +87,7 @@ public:
RCLCPP_PUBLIC
virtual
void
add_node(rclcpp::node::Node * node);
add_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph);
/// Return true if the given node is in the graph listener's list of nodes.
/* Also return false if given nullptr.
@@ -101,7 +97,7 @@ public:
RCLCPP_PUBLIC
virtual
bool
has_node(rclcpp::node::Node * node);
has_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph);
/// Remove a node from the graph listener's list of nodes.
/*
@@ -112,7 +108,7 @@ public:
RCLCPP_PUBLIC
virtual
void
remove_node(rclcpp::node::Node * node);
remove_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph);
/// Stop the listening thread.
/* The thread cannot be restarted, and the class is defunct after calling.
@@ -152,16 +148,16 @@ protected:
run_loop();
private:
RCLCPP_DISABLE_COPY(GraphListener);
RCLCPP_DISABLE_COPY(GraphListener)
std::thread listener_thread_;
bool is_started_;
std::atomic_bool is_shutdown_;
mutable std::mutex shutdown_mutex_;
mutable std::mutex nodes_barrier_mutex_;
mutable std::mutex nodes_mutex_;
std::vector<rclcpp::node::Node *> nodes_;
mutable std::mutex node_graph_interfaces_barrier_mutex_;
mutable std::mutex node_graph_interfaces_mutex_;
std::vector<rclcpp::node_interfaces::NodeGraphInterface *> node_graph_interfaces_;
rcl_guard_condition_t interrupt_guard_condition_ = rcl_get_zero_initialized_guard_condition();
rcl_guard_condition_t * shutdown_guard_condition_;

View File

@@ -120,10 +120,10 @@ namespace intra_process_manager
class IntraProcessManager
{
private:
RCLCPP_DISABLE_COPY(IntraProcessManager);
RCLCPP_DISABLE_COPY(IntraProcessManager)
public:
RCLCPP_SMART_PTR_DEFINITIONS(IntraProcessManager);
RCLCPP_SMART_PTR_DEFINITIONS(IntraProcessManager)
RCLCPP_PUBLIC
explicit IntraProcessManager(

View File

@@ -17,6 +17,7 @@
#include <algorithm>
#include <atomic>
#include <cstring>
#include <functional>
#include <limits>
#include <map>
@@ -41,7 +42,7 @@ namespace intra_process_manager
class IntraProcessManagerImplBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(IntraProcessManagerImplBase);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(IntraProcessManagerImplBase)
IntraProcessManagerImplBase() = default;
~IntraProcessManagerImplBase() = default;
@@ -78,7 +79,7 @@ public:
matches_any_publishers(const rmw_gid_t * id) const = 0;
private:
RCLCPP_DISABLE_COPY(IntraProcessManagerImplBase);
RCLCPP_DISABLE_COPY(IntraProcessManagerImplBase)
};
template<typename Allocator = std::allocator<void>>
@@ -92,6 +93,8 @@ public:
add_subscription(uint64_t id, subscription::SubscriptionBase::SharedPtr subscription)
{
subscriptions_[id] = subscription;
// subscription->get_topic_name() -> const char * can be used as the key,
// since subscriptions_ shares the ownership of subscription
subscription_ids_by_topic_[subscription->get_topic_name()].insert(id);
}
@@ -242,7 +245,7 @@ public:
}
private:
RCLCPP_DISABLE_COPY(IntraProcessManagerImpl);
RCLCPP_DISABLE_COPY(IntraProcessManagerImpl)
template<typename T>
using RebindAlloc = typename std::allocator_traits<Allocator>::template rebind_alloc<T>;
@@ -253,8 +256,20 @@ private:
using SubscriptionMap = std::unordered_map<uint64_t, subscription::SubscriptionBase::WeakPtr,
std::hash<uint64_t>, std::equal_to<uint64_t>,
RebindAlloc<std::pair<const uint64_t, subscription::SubscriptionBase::WeakPtr>>>;
using IDTopicMap = std::map<std::string, AllocSet,
std::less<std::string>, RebindAlloc<std::pair<std::string, AllocSet>>>;
struct strcmp_wrapper : public std::binary_function<const char *, const char *, bool>
{
bool
operator()(const char * lhs, const char * rhs) const
{
return std::strcmp(lhs, rhs) < 0;
}
};
using IDTopicMap = std::map<
const char *,
AllocSet,
strcmp_wrapper,
RebindAlloc<std::pair<const std::string, AllocSet>>>;
SubscriptionMap subscriptions_;
@@ -262,7 +277,7 @@ private:
struct PublisherInfo
{
RCLCPP_DISABLE_COPY(PublisherInfo);
RCLCPP_DISABLE_COPY(PublisherInfo)
PublisherInfo() = default;

View File

@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <utility>
#ifndef RCLCPP__MACROS_HPP_
#define RCLCPP__MACROS_HPP_
@@ -47,7 +50,23 @@
RCLCPP_WEAK_PTR_DEFINITIONS(__VA_ARGS__) \
__RCLCPP_UNIQUE_PTR_ALIAS(__VA_ARGS__)
#define __RCLCPP_SHARED_PTR_ALIAS(...) using SharedPtr = std::shared_ptr<__VA_ARGS__>;
/* Defines aliases only for using the Class with smart pointers.
*
* Same as RCLCPP_SMART_PTR_DEFINITIONS expect it excludes the static
* method definitions which do not work on pure virtual classes and classes
* which are not CopyConstructable.
*
* Use in the public section of the class.
* Make sure to include <memory> in the header when using this.
*/
#define RCLCPP_SMART_PTR_ALIASES_ONLY(...) \
__RCLCPP_SHARED_PTR_ALIAS(__VA_ARGS__) \
__RCLCPP_WEAK_PTR_ALIAS(__VA_ARGS__) \
__RCLCPP_MAKE_SHARED_DEFINITION(__VA_ARGS__)
#define __RCLCPP_SHARED_PTR_ALIAS(...) \
using SharedPtr = std::shared_ptr<__VA_ARGS__>; \
using ConstSharedPtr = std::shared_ptr<const __VA_ARGS__>;
#define __RCLCPP_MAKE_SHARED_DEFINITION(...) \
template<typename ... Args> \
@@ -62,7 +81,9 @@
__RCLCPP_SHARED_PTR_ALIAS(__VA_ARGS__) \
__RCLCPP_MAKE_SHARED_DEFINITION(__VA_ARGS__)
#define __RCLCPP_WEAK_PTR_ALIAS(...) using WeakPtr = std::weak_ptr<__VA_ARGS__>;
#define __RCLCPP_WEAK_PTR_ALIAS(...) \
using WeakPtr = std::weak_ptr<__VA_ARGS__>; \
using ConstWeakPtr = std::weak_ptr<const __VA_ARGS__>;
/// Defines aliases and static functions for using the Class with weak_ptrs.
#define RCLCPP_WEAK_PTR_DEFINITIONS(...) __RCLCPP_WEAK_PTR_ALIAS(__VA_ARGS__)

View File

@@ -20,6 +20,7 @@
#include <cstdint>
#include <memory>
#include <mutex>
#include <utility>
#include <vector>
#include "rclcpp/allocator/allocator_common.hpp"
@@ -34,7 +35,7 @@ namespace mapped_ring_buffer
class RCLCPP_PUBLIC MappedRingBufferBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBufferBase);
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBufferBase)
};
/// Ring buffer container of unique_ptr's of T, which can be accessed by a key.
@@ -57,7 +58,7 @@ template<typename T, typename Alloc = std::allocator<void>>
class MappedRingBuffer : public MappedRingBufferBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBuffer<T, Alloc>);
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBuffer<T, Alloc>)
using ElemAllocTraits = allocator::AllocRebind<T, Alloc>;
using ElemAlloc = typename ElemAllocTraits::allocator_type;
using ElemDeleter = allocator::Deleter<ElemAlloc, T>;
@@ -206,7 +207,7 @@ public:
}
private:
RCLCPP_DISABLE_COPY(MappedRingBuffer<T, Alloc>);
RCLCPP_DISABLE_COPY(MappedRingBuffer<T, Alloc>)
struct element
{

View File

@@ -23,7 +23,7 @@
#include "rclcpp/any_executable.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
@@ -40,8 +40,8 @@ namespace memory_strategy
class RCLCPP_PUBLIC MemoryStrategy
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(MemoryStrategy);
using WeakNodeVector = std::vector<std::weak_ptr<rclcpp::node::Node>>;
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(MemoryStrategy)
using WeakNodeVector = std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>;
virtual bool collect_entities(const WeakNodeVector & weak_nodes) = 0;
@@ -88,7 +88,7 @@ public:
static rclcpp::client::ClientBase::SharedPtr
get_client_by_handle(const rcl_client_t * client_handle, const WeakNodeVector & weak_nodes);
static rclcpp::node::Node::SharedPtr
static rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group,
const WeakNodeVector & weak_nodes);

View File

@@ -33,7 +33,7 @@ template<typename MessageT, typename Alloc = std::allocator<void>>
class MessageMemoryStrategy
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(MessageMemoryStrategy);
RCLCPP_SMART_PTR_DEFINITIONS(MessageMemoryStrategy)
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;

View File

@@ -39,6 +39,12 @@
#include "rclcpp/event.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/message_memory_strategy.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/service.hpp"
@@ -46,41 +52,17 @@
#include "rclcpp/timer.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rcl
{
struct rcl_node_t;
} // namespace rcl
namespace rclcpp
{
namespace graph_listener
{
class GraphListener;
} // namespace graph_listener
namespace node
{
class InvalidEventError : public std::runtime_error
{
public:
InvalidEventError()
: std::runtime_error("event is invalid") {}
};
class EventNotRegisteredError : public std::runtime_error
{
public:
EventNotRegisteredError()
: std::runtime_error("event already registered") {}
};
/// Node is the single point of entry for creating publishers and subscribers.
class Node : public std::enable_shared_from_this<Node>
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(Node);
RCLCPP_SMART_PTR_DEFINITIONS(Node)
/// Create a new node with the specified name.
/**
@@ -109,7 +91,7 @@ public:
/// Get the name of the node.
// \return The name of the node.
RCLCPP_PUBLIC
const std::string &
const char *
get_name() const;
/// Create and return a callback group.
@@ -117,14 +99,21 @@ public:
rclcpp::callback_group::CallbackGroup::SharedPtr
create_callback_group(rclcpp::callback_group::CallbackGroupType group_type);
/// Return the list of callback groups in the node.
RCLCPP_PUBLIC
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
get_callback_groups() const;
/// Create and return a Publisher.
/**
* \param[in] topic_name The topic for this publisher to publish on.
* \param[in] qos_history_depth The depth of the publisher message queue.
* \return Shared pointer to the created publisher.
*/
template<typename MessageT, typename Alloc = std::allocator<void>>
typename rclcpp::publisher::Publisher<MessageT, Alloc>::SharedPtr
template<
typename MessageT, typename Alloc = std::allocator<void>,
typename PublisherT = ::rclcpp::publisher::Publisher<MessageT, Alloc>>
std::shared_ptr<PublisherT>
create_publisher(
const std::string & topic_name, size_t qos_history_depth,
std::shared_ptr<Alloc> allocator = nullptr);
@@ -135,8 +124,10 @@ public:
* \param[in] qos_profile The quality of service profile to pass on to the rmw implementation.
* \return Shared pointer to the created publisher.
*/
template<typename MessageT, typename Alloc = std::allocator<void>>
typename rclcpp::publisher::Publisher<MessageT, Alloc>::SharedPtr
template<
typename MessageT, typename Alloc = std::allocator<void>,
typename PublisherT = ::rclcpp::publisher::Publisher<MessageT, Alloc>>
std::shared_ptr<PublisherT>
create_publisher(
const std::string & topic_name,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_default,
@@ -156,8 +147,12 @@ public:
Windows build breaks when static member function passed as default
argument to msg_mem_strat, nullptr is a workaround.
*/
template<typename MessageT, typename CallbackT, typename Alloc = std::allocator<void>>
typename rclcpp::subscription::Subscription<MessageT, Alloc>::SharedPtr
template<
typename MessageT,
typename CallbackT,
typename Alloc = std::allocator<void>,
typename SubscriptionT = rclcpp::subscription::Subscription<MessageT, Alloc>>
std::shared_ptr<SubscriptionT>
create_subscription(
const std::string & topic_name,
CallbackT && callback,
@@ -182,8 +177,12 @@ public:
Windows build breaks when static member function passed as default
argument to msg_mem_strat, nullptr is a workaround.
*/
template<typename MessageT, typename CallbackT, typename Alloc = std::allocator<void>>
typename rclcpp::subscription::Subscription<MessageT, Alloc>::SharedPtr
template<
typename MessageT,
typename CallbackT,
typename Alloc = std::allocator<void>,
typename SubscriptionT = rclcpp::subscription::Subscription<MessageT, Alloc>>
std::shared_ptr<SubscriptionT>
create_subscription(
const std::string & topic_name,
size_t qos_history_depth,
@@ -200,30 +199,13 @@ public:
* \param[in] callback User-defined callback function.
* \param[in] group Callback group to execute this timer's callback in.
*/
template<typename CallbackType>
typename rclcpp::timer::WallTimer<CallbackType>::SharedPtr
template<typename DurationT = std::milli, typename CallbackT>
typename rclcpp::timer::WallTimer<CallbackT>::SharedPtr
create_wall_timer(
std::chrono::nanoseconds period,
CallbackType callback,
std::chrono::duration<int64_t, DurationT> period,
CallbackT callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
/// Create a timer.
/**
* \param[in] period Time interval between triggers of the callback.
* \param[in] callback User-defined callback function.
* \param[in] group Callback group to execute this timer's callback in.
*/
// TODO(wjwwood): reenable this once I figure out why the demo doesn't build with it.
// rclcpp::timer::WallTimer::SharedPtr
// create_wall_timer(
// std::chrono::duration<long double, std::nano> period,
// rclcpp::timer::CallbackType callback,
// rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
using CallbackGroup = rclcpp::callback_group::CallbackGroup;
using CallbackGroupWeakPtr = std::weak_ptr<CallbackGroup>;
using CallbackGroupWeakPtrList = std::list<CallbackGroupWeakPtr>;
/* Create and return a Client. */
template<typename ServiceT>
typename rclcpp::client::Client<ServiceT>::SharedPtr
@@ -253,6 +235,20 @@ public:
std::vector<rclcpp::parameter::ParameterVariant>
get_parameters(const std::vector<std::string> & names) const;
RCLCPP_PUBLIC
rclcpp::parameter::ParameterVariant
get_parameter(const std::string & name) const;
RCLCPP_PUBLIC
bool
get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const;
template<typename ParameterT>
bool
get_parameter(const std::string & name, ParameterT & parameter) const;
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::ParameterDescriptor>
describe_parameters(const std::vector<std::string> & names) const;
@@ -265,6 +261,15 @@ public:
rcl_interfaces::msg::ListParametersResult
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const;
/// Register the callback for parameter changes
/**
* \param[in] User defined callback function, It is expected to atomically set parameters.
* \note Repeated invocations of this function will overwrite previous callbacks
*/
template<typename CallbackT>
void
register_param_change_callback(CallbackT && callback);
RCLCPP_PUBLIC
std::map<std::string, std::string>
get_topic_names_and_types() const;
@@ -277,53 +282,6 @@ public:
size_t
count_subscribers(const std::string & topic_name) const;
RCLCPP_PUBLIC
const CallbackGroupWeakPtrList &
get_callback_groups() const;
RCLCPP_PUBLIC
const rcl_guard_condition_t *
get_notify_guard_condition() const;
RCLCPP_PUBLIC
const rcl_guard_condition_t *
get_graph_guard_condition() const;
RCLCPP_PUBLIC
const rcl_node_t *
get_rcl_node_handle() const;
/// Return the rcl_node_t node handle (non-const version).
RCLCPP_PUBLIC
rcl_node_t *
get_rcl_node_handle();
/// Return the rcl_node_t node handle in a std::shared_ptr.
/* This handle remains valid after the Node is destroyed.
* The actual rcl node is not finalized until it is out of scope everywhere.
*/
RCLCPP_PUBLIC
std::shared_ptr<rcl_node_t>
get_shared_node_handle();
/// Notify threads waiting on graph changes.
/* Affects threads waiting on the notify guard condition, see:
* get_notify_guard_condition(), as well as the threads waiting on graph
* changes using a graph Event, see: wait_for_graph_change().
*
* This is typically only used by the rclcpp::graph_listener::GraphListener.
*
* \throws RCLBaseError (a child of that exception) when an rcl error occurs
*/
RCLCPP_PUBLIC
void
notify_graph_change();
/// Notify any and all blocking node actions that shutdown has occurred.
RCLCPP_PUBLIC
void
notify_shutdown();
/// Return a graph event, which will be set anytime a graph change occurs.
/* The graph Event object is a loan which must be returned.
* The Event object is scoped and therefore to return the load just let it go
@@ -346,63 +304,51 @@ public:
rclcpp::event::Event::SharedPtr event,
std::chrono::nanoseconds timeout);
/// Return the number of on loan graph events, see get_graph_event().
/* This is typically only used by the rclcpp::graph_listener::GraphListener.
*/
/// Return the Node's internal NodeBaseInterface implementation.
RCLCPP_PUBLIC
size_t
count_graph_users();
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_base_interface();
std::atomic_bool has_executor;
/// Return the Node's internal NodeGraphInterface implementation.
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr
get_node_graph_interface();
/// Return the Node's internal NodeTimersInterface implementation.
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr
get_node_timers_interface();
/// Return the Node's internal NodeTopicsInterface implementation.
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr
get_node_topics_interface();
/// Return the Node's internal NodeServicesInterface implementation.
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr
get_node_services_interface();
/// Return the Node's internal NodeParametersInterface implementation.
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr
get_node_parameters_interface();
private:
RCLCPP_DISABLE_COPY(Node);
RCLCPP_DISABLE_COPY(Node)
RCLCPP_PUBLIC
bool
group_in_node(callback_group::CallbackGroup::SharedPtr group);
std::string name_;
std::shared_ptr<rcl_node_t> node_handle_;
rclcpp::context::Context::SharedPtr context_;
CallbackGroup::SharedPtr default_callback_group_;
CallbackGroupWeakPtrList callback_groups_;
size_t number_of_subscriptions_;
size_t number_of_timers_;
size_t number_of_services_;
size_t number_of_clients_;
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_;
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_;
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr node_timers_;
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_;
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
bool use_intra_process_comms_;
mutable std::mutex mutex_;
/// Guard condition for notifying the Executor of changes to this node.
mutable std::mutex notify_guard_condition_mutex_;
rcl_guard_condition_t notify_guard_condition_ = rcl_get_zero_initialized_guard_condition();
bool notify_guard_condition_is_valid_;
/// Graph Listener which waits on graph changes for the node and is shared across nodes.
std::shared_ptr<rclcpp::graph_listener::GraphListener> graph_listener_;
/// Whether or not this node needs to be added to the graph listener.
std::atomic_bool should_add_to_graph_listener_;
/// Mutex to guard the graph event related data structures.
std::mutex graph_mutex_;
/// For notifying waiting threads (wait_for_graph_change()) on changes (notify_graph_change()).
std::condition_variable graph_cv_;
/// Weak references to graph events out on loan.
std::vector<rclcpp::event::Event::WeakPtr> graph_events_;
/// Number of graph events out on loan, used to determine if the graph should be monitored.
/* graph_users_count_ is atomic so that it can be accessed without acquiring the graph_mutex_ */
std::atomic_size_t graph_users_count_;
std::map<std::string, rclcpp::parameter::ParameterVariant> parameters_;
publisher::Publisher<rcl_interfaces::msg::ParameterEvent>::SharedPtr events_publisher_;
};
} // namespace node

View File

@@ -38,6 +38,8 @@
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/create_publisher.hpp"
#include "rclcpp/create_subscription.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -50,8 +52,8 @@ namespace rclcpp
namespace node
{
template<typename MessageT, typename Alloc>
typename rclcpp::publisher::Publisher<MessageT, Alloc>::SharedPtr
template<typename MessageT, typename Alloc, typename PublisherT>
std::shared_ptr<PublisherT>
Node::create_publisher(
const std::string & topic_name, size_t qos_history_depth,
std::shared_ptr<Alloc> allocator)
@@ -61,11 +63,11 @@ Node::create_publisher(
}
rmw_qos_profile_t qos = rmw_qos_profile_default;
qos.depth = qos_history_depth;
return this->create_publisher<MessageT, Alloc>(topic_name, qos, allocator);
return this->create_publisher<MessageT, Alloc, PublisherT>(topic_name, qos, allocator);
}
template<typename MessageT, typename Alloc>
typename publisher::Publisher<MessageT, Alloc>::SharedPtr
template<typename MessageT, typename Alloc, typename PublisherT>
std::shared_ptr<PublisherT>
Node::create_publisher(
const std::string & topic_name, const rmw_qos_profile_t & qos_profile,
std::shared_ptr<Alloc> allocator)
@@ -73,67 +75,16 @@ Node::create_publisher(
if (!allocator) {
allocator = std::make_shared<Alloc>();
}
auto publisher_options = rcl_publisher_get_default_options();
publisher_options.qos = qos_profile;
auto message_alloc =
std::make_shared<typename publisher::Publisher<MessageT, Alloc>::MessageAlloc>(
*allocator.get());
publisher_options.allocator = allocator::get_rcl_allocator<MessageT>(
*message_alloc.get());
auto publisher = publisher::Publisher<MessageT, Alloc>::make_shared(
node_handle_, topic_name, publisher_options, message_alloc);
if (use_intra_process_comms_) {
auto intra_process_manager =
context_->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
uint64_t intra_process_publisher_id =
intra_process_manager->add_publisher<MessageT, Alloc>(publisher);
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = intra_process_manager;
// *INDENT-OFF*
auto shared_publish_callback =
[weak_ipm](uint64_t publisher_id, void * msg, const std::type_info & type_info) -> uint64_t
{
auto ipm = weak_ipm.lock();
if (!ipm) {
// TODO(wjwwood): should this just return silently? Or maybe return with a logged warning?
throw std::runtime_error(
"intra process publish called after destruction of intra process manager");
}
if (!msg) {
throw std::runtime_error("cannot publisher msg which is a null pointer");
}
auto & message_type_info = typeid(MessageT);
if (message_type_info != type_info) {
throw std::runtime_error(
std::string("published type '") + type_info.name() +
"' is incompatible from the publisher type '" + message_type_info.name() + "'");
}
MessageT * typed_message_ptr = static_cast<MessageT *>(msg);
using MessageDeleter = typename publisher::Publisher<MessageT, Alloc>::MessageDeleter;
std::unique_ptr<MessageT, MessageDeleter> unique_msg(typed_message_ptr);
uint64_t message_seq =
ipm->store_intra_process_message<MessageT, Alloc>(publisher_id, unique_msg);
return message_seq;
};
// *INDENT-ON*
publisher->setup_intra_process(
intra_process_publisher_id,
shared_publish_callback,
publisher_options);
}
if (rcl_trigger_guard_condition(&notify_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on publisher creation: ") + rmw_get_error_string());
}
return publisher;
return rclcpp::create_publisher<MessageT, Alloc, PublisherT>(
this->node_topics_.get(),
topic_name,
qos_profile,
use_intra_process_comms_,
allocator);
}
template<typename MessageT, typename CallbackT, typename Alloc>
typename rclcpp::subscription::Subscription<MessageT, Alloc>::SharedPtr
template<typename MessageT, typename CallbackT, typename Alloc, typename SubscriptionT>
std::shared_ptr<SubscriptionT>
Node::create_subscription(
const std::string & topic_name,
CallbackT && callback,
@@ -142,103 +93,31 @@ Node::create_subscription(
bool ignore_local_publications,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
msg_mem_strat,
typename std::shared_ptr<Alloc> allocator)
std::shared_ptr<Alloc> allocator)
{
if (!allocator) {
allocator = std::make_shared<Alloc>();
}
rclcpp::subscription::AnySubscriptionCallback<MessageT,
Alloc> any_subscription_callback(allocator);
any_subscription_callback.set(std::forward<CallbackT>(callback));
if (!msg_mem_strat) {
msg_mem_strat =
rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::create_default();
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
msg_mem_strat = MessageMemoryStrategy<MessageT, Alloc>::create_default();
}
auto message_alloc =
std::make_shared<typename subscription::Subscription<MessageT, Alloc>::MessageAlloc>();
auto subscription_options = rcl_subscription_get_default_options();
subscription_options.qos = qos_profile;
subscription_options.allocator = rclcpp::allocator::get_rcl_allocator<MessageT>(
*message_alloc.get());
subscription_options.ignore_local_publications = ignore_local_publications;
using rclcpp::subscription::Subscription;
using rclcpp::subscription::SubscriptionBase;
auto sub = Subscription<MessageT, Alloc>::make_shared(
node_handle_,
return rclcpp::create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
this->node_topics_.get(),
topic_name,
subscription_options,
any_subscription_callback,
msg_mem_strat);
auto sub_base_ptr = std::dynamic_pointer_cast<SubscriptionBase>(sub);
// Setup intra process.
if (use_intra_process_comms_) {
auto intra_process_options = rcl_subscription_get_default_options();
intra_process_options.allocator = rclcpp::allocator::get_rcl_allocator<MessageT>(
*message_alloc.get());
intra_process_options.qos = qos_profile;
intra_process_options.ignore_local_publications = false;
auto intra_process_manager =
context_->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = intra_process_manager;
uint64_t intra_process_subscription_id =
intra_process_manager->add_subscription(sub_base_ptr);
// *INDENT-OFF*
sub->setup_intra_process(
intra_process_subscription_id,
[weak_ipm](
uint64_t publisher_id,
uint64_t message_sequence,
uint64_t subscription_id,
typename Subscription<MessageT, Alloc>::MessageUniquePtr & message)
{
auto ipm = weak_ipm.lock();
if (!ipm) {
// TODO(wjwwood): should this just return silently? Or maybe return with a logged warning?
throw std::runtime_error(
"intra process take called after destruction of intra process manager");
}
ipm->take_intra_process_message<MessageT, Alloc>(
publisher_id, message_sequence, subscription_id, message);
},
[weak_ipm](const rmw_gid_t * sender_gid) -> bool {
auto ipm = weak_ipm.lock();
if (!ipm) {
throw std::runtime_error(
"intra process publisher check called after destruction of intra process manager");
}
return ipm->matches_any_publishers(sender_gid);
},
intra_process_options
);
// *INDENT-ON*
}
// Assign to a group.
if (group) {
if (!group_in_node(group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create subscription, group not in node.");
}
group->add_subscription(sub_base_ptr);
} else {
default_callback_group_->add_subscription(sub_base_ptr);
}
number_of_subscriptions_++;
if (rcl_trigger_guard_condition(&notify_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on subscription creation: ") + rmw_get_error_string());
}
return sub;
std::forward<CallbackT>(callback),
qos_profile,
group,
ignore_local_publications,
use_intra_process_comms_,
msg_mem_strat,
allocator);
}
template<typename MessageT, typename CallbackT, typename Alloc>
typename rclcpp::subscription::Subscription<MessageT, Alloc>::SharedPtr
template<typename MessageT, typename CallbackT, typename Alloc, typename SubscriptionT>
std::shared_ptr<SubscriptionT>
Node::create_subscription(
const std::string & topic_name,
size_t qos_history_depth,
@@ -251,7 +130,7 @@ Node::create_subscription(
{
rmw_qos_profile_t qos = rmw_qos_profile_default;
qos.depth = qos_history_depth;
return this->create_subscription<MessageT, CallbackT, Alloc>(
return this->create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
topic_name,
std::forward<CallbackT>(callback),
qos,
@@ -261,30 +140,17 @@ Node::create_subscription(
allocator);
}
template<typename CallbackType>
typename rclcpp::timer::WallTimer<CallbackType>::SharedPtr
template<typename DurationT, typename CallbackT>
typename rclcpp::timer::WallTimer<CallbackT>::SharedPtr
Node::create_wall_timer(
std::chrono::nanoseconds period,
CallbackType callback,
std::chrono::duration<int64_t, DurationT> period,
CallbackT callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
auto timer = rclcpp::timer::WallTimer<CallbackType>::make_shared(
period, std::move(callback));
if (group) {
if (!group_in_node(group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create timer, group not in node.");
}
group->add_timer(timer);
} else {
default_callback_group_->add_timer(timer);
}
number_of_timers_++;
if (rcl_trigger_guard_condition(&notify_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on timer creation: ") + rmw_get_error_string());
}
auto timer = rclcpp::timer::WallTimer<CallbackT>::make_shared(
std::chrono::duration_cast<std::chrono::nanoseconds>(period),
std::move(callback));
node_timers_->add_timer(timer, group);
return timer;
}
@@ -302,27 +168,13 @@ Node::create_client(
using rclcpp::client::ClientBase;
auto cli = Client<ServiceT>::make_shared(
shared_from_this(),
node_base_.get(),
node_graph_,
service_name,
options);
auto cli_base_ptr = std::dynamic_pointer_cast<ClientBase>(cli);
if (group) {
if (!group_in_node(group)) {
// TODO(esteve): use custom exception
throw std::runtime_error("Cannot create client, group not in node.");
}
group->add_client(cli_base_ptr);
} else {
default_callback_group_->add_client(cli_base_ptr);
}
number_of_clients_++;
if (rcl_trigger_guard_condition(&notify_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on client creation: ") + rmw_get_error_string());
}
node_services_->add_client(cli_base_ptr, group);
return cli;
}
@@ -341,27 +193,31 @@ Node::create_service(
service_options.qos = qos_profile;
auto serv = service::Service<ServiceT>::make_shared(
node_handle_,
node_base_->get_shared_rcl_node_handle(),
service_name, any_service_callback, service_options);
auto serv_base_ptr = std::dynamic_pointer_cast<service::ServiceBase>(serv);
if (group) {
if (!group_in_node(group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create service, group not in node.");
}
group->add_service(serv_base_ptr);
} else {
default_callback_group_->add_service(serv_base_ptr);
}
number_of_services_++;
if (rcl_trigger_guard_condition(&notify_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on service creation: ") + rmw_get_error_string());
}
node_services_->add_service(serv_base_ptr, group);
return serv;
}
template<typename CallbackT>
void
Node::register_param_change_callback(CallbackT && callback)
{
this->node_parameters_->register_param_change_callback(std::forward<CallbackT>(callback));
}
template<typename ParameterT>
bool
Node::get_parameter(const std::string & name, ParameterT & parameter) const
{
rclcpp::parameter::ParameterVariant parameter_variant(name, parameter);
bool result = get_parameter(name, parameter_variant);
parameter = parameter_variant.get_value<ParameterT>();
return result;
}
} // namespace node
} // namespace rclcpp

View File

@@ -0,0 +1,131 @@
// Copyright 2016 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__NODE_INTERFACES__NODE_BASE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_BASE_HPP_
#include <memory>
#include <string>
#include <vector>
#include "rclcpp/context.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Implementation of the NodeBase part of the Node API.
class NodeBase : public NodeBaseInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBaseInterface)
RCLCPP_PUBLIC
NodeBase(const std::string & node_name, rclcpp::context::Context::SharedPtr context);
RCLCPP_PUBLIC
virtual
~NodeBase();
RCLCPP_PUBLIC
virtual
const char *
get_name() const;
RCLCPP_PUBLIC
virtual
rclcpp::context::Context::SharedPtr
get_context();
RCLCPP_PUBLIC
virtual
rcl_node_t *
get_rcl_node_handle();
RCLCPP_PUBLIC
virtual
const rcl_node_t *
get_rcl_node_handle() const;
RCLCPP_PUBLIC
virtual
std::shared_ptr<rcl_node_t>
get_shared_rcl_node_handle();
RCLCPP_PUBLIC
virtual
std::shared_ptr<const rcl_node_t>
get_shared_rcl_node_handle() const;
RCLCPP_PUBLIC
virtual
rclcpp::callback_group::CallbackGroup::SharedPtr
create_callback_group(rclcpp::callback_group::CallbackGroupType group_type);
RCLCPP_PUBLIC
virtual
rclcpp::callback_group::CallbackGroup::SharedPtr
get_default_callback_group();
RCLCPP_PUBLIC
virtual
bool
callback_group_in_node(rclcpp::callback_group::CallbackGroup::SharedPtr group);
RCLCPP_PUBLIC
virtual
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
get_callback_groups() const;
RCLCPP_PUBLIC
virtual
std::atomic_bool &
get_associated_with_executor_atomic();
RCLCPP_PUBLIC
virtual
rcl_guard_condition_t *
get_notify_guard_condition();
RCLCPP_PUBLIC
virtual
std::unique_lock<std::recursive_mutex>
acquire_notify_guard_condition_lock() const;
private:
RCLCPP_DISABLE_COPY(NodeBase)
rclcpp::context::Context::SharedPtr context_;
std::shared_ptr<rcl_node_t> node_handle_;
rclcpp::callback_group::CallbackGroup::SharedPtr default_callback_group_;
std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> callback_groups_;
std::atomic_bool associated_with_executor_;
/// Guard condition for notifying the Executor of changes to this node.
mutable std::recursive_mutex notify_guard_condition_mutex_;
rcl_guard_condition_t notify_guard_condition_ = rcl_get_zero_initialized_guard_condition();
bool notify_guard_condition_is_valid_;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_BASE_HPP_

View File

@@ -0,0 +1,136 @@
// Copyright 2016 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__NODE_INTERFACES__NODE_BASE_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_BASE_INTERFACE_HPP_
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include "rcl/node.h"
#include "rclcpp/callback_group.hpp"
#include "rclcpp/context.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Pure virtual interface class for the NodeBase part of the Node API.
class NodeBaseInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBaseInterface)
/// Return the name of the node.
// \return The name of the node.
RCLCPP_PUBLIC
virtual
const char *
get_name() const = 0;
/// Return the context of the node.
// \return SharedPtr to the node's context.
RCLCPP_PUBLIC
virtual
rclcpp::context::Context::SharedPtr
get_context() = 0;
/// Return the rcl_node_t node handle (non-const version).
RCLCPP_PUBLIC
virtual
rcl_node_t *
get_rcl_node_handle() = 0;
/// Return the rcl_node_t node handle (const version).
RCLCPP_PUBLIC
virtual
const rcl_node_t *
get_rcl_node_handle() const = 0;
/// Return the rcl_node_t node handle in a std::shared_ptr.
/* This handle remains valid after the Node is destroyed.
* The actual rcl node is not finalized until it is out of scope everywhere.
*/
RCLCPP_PUBLIC
virtual
std::shared_ptr<rcl_node_t>
get_shared_rcl_node_handle() = 0;
/// Return the rcl_node_t node handle in a std::shared_ptr.
/* This handle remains valid after the Node is destroyed.
* The actual rcl node is not finalized until it is out of scope everywhere.
*/
RCLCPP_PUBLIC
virtual
std::shared_ptr<const rcl_node_t>
get_shared_rcl_node_handle() const = 0;
/// Create and return a callback group.
RCLCPP_PUBLIC
virtual
rclcpp::callback_group::CallbackGroup::SharedPtr
create_callback_group(rclcpp::callback_group::CallbackGroupType group_type) = 0;
/// Return the default callback group.
RCLCPP_PUBLIC
virtual
rclcpp::callback_group::CallbackGroup::SharedPtr
get_default_callback_group() = 0;
/// Return true if the given callback group is associated with this node.
RCLCPP_PUBLIC
virtual
bool
callback_group_in_node(rclcpp::callback_group::CallbackGroup::SharedPtr group) = 0;
/// Return list of callback groups associated with this node.
RCLCPP_PUBLIC
virtual
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
get_callback_groups() const = 0;
/// Return the atomic bool which is used to ensure only one executor is used.
RCLCPP_PUBLIC
virtual
std::atomic_bool &
get_associated_with_executor_atomic() = 0;
/// Return guard condition that should be notified when the internal node state changes.
/* For example, this should be notified when a publisher is added or removed.
*
* \return the rcl_guard_condition_t if it is valid, else nullptr
*/
RCLCPP_PUBLIC
virtual
rcl_guard_condition_t *
get_notify_guard_condition() = 0;
/// Acquire and return a scoped lock that protects the notify guard condition.
/* This should be used when triggering the notify guard condition. */
RCLCPP_PUBLIC
virtual
std::unique_lock<std::recursive_mutex>
acquire_notify_guard_condition_lock() const = 0;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_BASE_INTERFACE_HPP_

View File

@@ -0,0 +1,129 @@
// Copyright 2016 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__NODE_INTERFACES__NODE_GRAPH_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_GRAPH_HPP_
#include <chrono>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include "rcl/guard_condition.h"
#include "rclcpp/event.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace graph_listener
{
class GraphListener;
} // namespace graph_listener
namespace node_interfaces
{
/// Implementation the NodeGraph part of the Node API.
class NodeGraph : public NodeGraphInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeGraph)
RCLCPP_PUBLIC
explicit NodeGraph(rclcpp::node_interfaces::NodeBaseInterface * node_base);
RCLCPP_PUBLIC
virtual
~NodeGraph();
RCLCPP_PUBLIC
virtual
std::map<std::string, std::string>
get_topic_names_and_types() const;
RCLCPP_PUBLIC
virtual
size_t
count_publishers(const std::string & topic_name) const;
RCLCPP_PUBLIC
virtual
size_t
count_subscribers(const std::string & topic_name) const;
RCLCPP_PUBLIC
virtual
const rcl_guard_condition_t *
get_graph_guard_condition() const;
RCLCPP_PUBLIC
virtual
void
notify_graph_change();
RCLCPP_PUBLIC
virtual
void
notify_shutdown();
RCLCPP_PUBLIC
virtual
rclcpp::event::Event::SharedPtr
get_graph_event();
RCLCPP_PUBLIC
virtual
void
wait_for_graph_change(
rclcpp::event::Event::SharedPtr event,
std::chrono::nanoseconds timeout);
RCLCPP_PUBLIC
virtual
size_t
count_graph_users();
private:
RCLCPP_DISABLE_COPY(NodeGraph)
/// Handle to the NodeBaseInterface given in the constructor.
rclcpp::node_interfaces::NodeBaseInterface * node_base_;
/// Graph Listener which waits on graph changes for the node and is shared across nodes.
std::shared_ptr<rclcpp::graph_listener::GraphListener> graph_listener_;
/// Whether or not this node needs to be added to the graph listener.
std::atomic_bool should_add_to_graph_listener_;
/// Mutex to guard the graph event related data structures.
mutable std::mutex graph_mutex_;
/// For notifying waiting threads (wait_for_graph_change()) on changes (notify_graph_change()).
std::condition_variable graph_cv_;
/// Weak references to graph events out on loan.
std::vector<rclcpp::event::Event::WeakPtr> graph_events_;
/// Number of graph events out on loan, used to determine if the graph should be monitored.
/* graph_users_count_ is atomic so that it can be accessed without acquiring the graph_mutex_ */
std::atomic_size_t graph_users_count_;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_GRAPH_HPP_

View File

@@ -0,0 +1,122 @@
// Copyright 2016 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__NODE_INTERFACES__NODE_GRAPH_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_GRAPH_INTERFACE_HPP_
#include <chrono>
#include <map>
#include <string>
#include "rcl/guard_condition.h"
#include "rclcpp/event.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Pure virtual interface class for the NodeGraph part of the Node API.
class NodeGraphInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeGraphInterface)
/// Return a map of existing topic names (string) to topic types (string).
/* A topic is considered to exist when at least one publisher or subscriber
* exists for it, whether they be local or remote to this process.
*/
RCLCPP_PUBLIC
virtual
std::map<std::string, std::string>
get_topic_names_and_types() const = 0;
/// Return the number of publishers that are advertised on a given topic.
RCLCPP_PUBLIC
virtual
size_t
count_publishers(const std::string & topic_name) const = 0;
/// Return the number of subscribers who have created a subscription for a given topic.
RCLCPP_PUBLIC
virtual
size_t
count_subscribers(const std::string & topic_name) const = 0;
/// Return the rcl guard condition which is triggered when the ROS graph changes.
RCLCPP_PUBLIC
virtual
const rcl_guard_condition_t *
get_graph_guard_condition() const = 0;
/// Notify threads waiting on graph changes.
/* Affects threads waiting on the notify guard condition, see:
* get_notify_guard_condition(), as well as the threads waiting on graph
* changes using a graph Event, see: wait_for_graph_change().
*
* This is typically only used by the rclcpp::graph_listener::GraphListener.
*
* \throws RCLBaseError (a child of that exception) when an rcl error occurs
*/
RCLCPP_PUBLIC
virtual
void
notify_graph_change() = 0;
/// Notify any and all blocking node actions that shutdown has occurred.
RCLCPP_PUBLIC
virtual
void
notify_shutdown() = 0;
/// Return a graph event, which will be set anytime a graph change occurs.
/* The graph Event object is a loan which must be returned.
* The Event object is scoped and therefore to return the load just let it go
* out of scope.
*/
RCLCPP_PUBLIC
virtual
rclcpp::event::Event::SharedPtr
get_graph_event() = 0;
/// Wait for a graph event to occur by waiting on an Event to become set.
/* The given Event must be acquire through the get_graph_event() method.
*
* \throws InvalidEventError if the given event is nullptr
* \throws EventNotRegisteredError if the given event was not acquired with
* get_graph_event().
*/
RCLCPP_PUBLIC
virtual
void
wait_for_graph_change(
rclcpp::event::Event::SharedPtr event,
std::chrono::nanoseconds timeout) = 0;
/// Return the number of on loan graph events, see get_graph_event().
/* This is typically only used by the rclcpp::graph_listener::GraphListener.
*/
RCLCPP_PUBLIC
virtual
size_t
count_graph_users() = 0;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_GRAPH_INTERFACE_HPP_

View File

@@ -0,0 +1,122 @@
// Copyright 2016 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__NODE_INTERFACES__NODE_PARAMETERS_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_HPP_
#include <map>
#include <string>
#include <vector>
#include "rcl_interfaces/msg/list_parameters_result.hpp"
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
#include "rcl_interfaces/msg/parameter_event.hpp"
#include "rcl_interfaces/msg/set_parameters_result.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Implementation of the NodeParameters part of the Node API.
class NodeParameters : public NodeParametersInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeParameters)
RCLCPP_PUBLIC
NodeParameters(
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
bool use_intra_process);
RCLCPP_PUBLIC
virtual
~NodeParameters();
RCLCPP_PUBLIC
virtual
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
RCLCPP_PUBLIC
virtual
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
RCLCPP_PUBLIC
virtual
std::vector<rclcpp::parameter::ParameterVariant>
get_parameters(const std::vector<std::string> & names) const;
RCLCPP_PUBLIC
virtual
rclcpp::parameter::ParameterVariant
get_parameter(const std::string & name) const;
RCLCPP_PUBLIC
virtual
bool
get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const;
RCLCPP_PUBLIC
virtual
std::vector<rcl_interfaces::msg::ParameterDescriptor>
describe_parameters(const std::vector<std::string> & names) const;
RCLCPP_PUBLIC
virtual
std::vector<uint8_t>
get_parameter_types(const std::vector<std::string> & names) const;
RCLCPP_PUBLIC
virtual
rcl_interfaces::msg::ListParametersResult
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const;
RCLCPP_PUBLIC
virtual
void
register_param_change_callback(ParametersCallbackFunction callback);
private:
RCLCPP_DISABLE_COPY(NodeParameters)
rclcpp::node_interfaces::NodeTopicsInterface * node_topics_;
mutable std::mutex mutex_;
ParametersCallbackFunction parameters_callback_ = nullptr;
std::map<std::string, rclcpp::parameter::ParameterVariant> parameters_;
publisher::Publisher<rcl_interfaces::msg::ParameterEvent>::SharedPtr events_publisher_;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_HPP_

View File

@@ -0,0 +1,97 @@
// Copyright 2016 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__NODE_INTERFACES__NODE_PARAMETERS_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_INTERFACE_HPP_
#include <string>
#include <vector>
#include "rcl_interfaces/msg/list_parameters_result.hpp"
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
#include "rcl_interfaces/msg/set_parameters_result.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Pure virtual interface class for the NodeParameters part of the Node API.
class NodeParametersInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeParametersInterface)
RCLCPP_PUBLIC
virtual
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters) = 0;
RCLCPP_PUBLIC
virtual
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters) = 0;
RCLCPP_PUBLIC
virtual
std::vector<rclcpp::parameter::ParameterVariant>
get_parameters(const std::vector<std::string> & names) const = 0;
RCLCPP_PUBLIC
virtual
rclcpp::parameter::ParameterVariant
get_parameter(const std::string & name) const = 0;
RCLCPP_PUBLIC
virtual
bool
get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const = 0;
RCLCPP_PUBLIC
virtual
std::vector<rcl_interfaces::msg::ParameterDescriptor>
describe_parameters(const std::vector<std::string> & names) const = 0;
RCLCPP_PUBLIC
virtual
std::vector<uint8_t>
get_parameter_types(const std::vector<std::string> & names) const = 0;
RCLCPP_PUBLIC
virtual
rcl_interfaces::msg::ListParametersResult
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const = 0;
using ParametersCallbackFunction =
std::function<rcl_interfaces::msg::SetParametersResult(
const std::vector<rclcpp::parameter::ParameterVariant> &)>;
RCLCPP_PUBLIC
virtual
void
register_param_change_callback(ParametersCallbackFunction callback) = 0;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_INTERFACE_HPP_

View File

@@ -0,0 +1,67 @@
// Copyright 2016 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__NODE_INTERFACES__NODE_SERVICES_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_SERVICES_HPP_
#include "rclcpp/callback_group.hpp"
#include "rclcpp/client.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Implementation of the NodeServices part of the Node API.
class NodeServices : public NodeServicesInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeServices)
RCLCPP_PUBLIC
explicit NodeServices(rclcpp::node_interfaces::NodeBaseInterface * node_base);
RCLCPP_PUBLIC
virtual
~NodeServices();
RCLCPP_PUBLIC
virtual
void
add_client(
rclcpp::client::ClientBase::SharedPtr client_base_ptr,
rclcpp::callback_group::CallbackGroup::SharedPtr group);
RCLCPP_PUBLIC
virtual
void
add_service(
rclcpp::service::ServiceBase::SharedPtr service_base_ptr,
rclcpp::callback_group::CallbackGroup::SharedPtr group);
private:
RCLCPP_DISABLE_COPY(NodeServices)
rclcpp::node_interfaces::NodeBaseInterface * node_base_;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_SERVICES_HPP_

View File

@@ -0,0 +1,53 @@
// Copyright 2016 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__NODE_INTERFACES__NODE_SERVICES_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_SERVICES_INTERFACE_HPP_
#include "rclcpp/callback_group.hpp"
#include "rclcpp/client.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Pure virtual interface class for the NodeServices part of the Node API.
class NodeServicesInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeServicesInterface)
RCLCPP_PUBLIC
virtual
void
add_client(
rclcpp::client::ClientBase::SharedPtr client_base_ptr,
rclcpp::callback_group::CallbackGroup::SharedPtr group) = 0;
RCLCPP_PUBLIC
virtual
void
add_service(
rclcpp::service::ServiceBase::SharedPtr service_base_ptr,
rclcpp::callback_group::CallbackGroup::SharedPtr group) = 0;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_SERVICES_INTERFACE_HPP_

View File

@@ -0,0 +1,60 @@
// Copyright 2016 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__NODE_INTERFACES__NODE_TIMERS_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_TIMERS_HPP_
#include "rclcpp/callback_group.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
#include "rclcpp/timer.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Implementation of the NodeTimers part of the Node API.
class NodeTimers : public NodeTimersInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTimers)
RCLCPP_PUBLIC
explicit NodeTimers(rclcpp::node_interfaces::NodeBaseInterface * node_base);
RCLCPP_PUBLIC
virtual
~NodeTimers();
/// Add a timer to the node.
RCLCPP_PUBLIC
virtual
void
add_timer(
rclcpp::timer::TimerBase::SharedPtr timer,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group);
private:
RCLCPP_DISABLE_COPY(NodeTimers)
rclcpp::node_interfaces::NodeBaseInterface * node_base_;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_TIMERS_HPP_

View File

@@ -0,0 +1,46 @@
// Copyright 2016 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__NODE_INTERFACES__NODE_TIMERS_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_TIMERS_INTERFACE_HPP_
#include "rclcpp/callback_group.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/timer.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Pure virtual interface class for the NodeTimers part of the Node API.
class NodeTimersInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTimersInterface)
/// Add a timer to the node.
RCLCPP_PUBLIC
virtual
void
add_timer(
rclcpp::timer::TimerBase::SharedPtr timer,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) = 0;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_TIMERS_INTERFACE_HPP_

View File

@@ -0,0 +1,88 @@
// Copyright 2016 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__NODE_INTERFACES__NODE_TOPICS_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_TOPICS_HPP_
#include <string>
#include "rcl/publisher.h"
#include "rcl/subscription.h"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/publisher_factory.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Implementation of the NodeTopics part of the Node API.
class NodeTopics : public NodeTopicsInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTopicsInterface)
RCLCPP_PUBLIC
explicit NodeTopics(rclcpp::node_interfaces::NodeBaseInterface * node_base);
RCLCPP_PUBLIC
virtual
~NodeTopics();
RCLCPP_PUBLIC
virtual
rclcpp::publisher::PublisherBase::SharedPtr
create_publisher(
const std::string & topic_name,
const rclcpp::PublisherFactory & publisher_factory,
rcl_publisher_options_t & publisher_options,
bool use_intra_process);
RCLCPP_PUBLIC
virtual
void
add_publisher(
rclcpp::publisher::PublisherBase::SharedPtr publisher);
RCLCPP_PUBLIC
virtual
rclcpp::subscription::SubscriptionBase::SharedPtr
create_subscription(
const std::string & topic_name,
const rclcpp::SubscriptionFactory & subscription_factory,
rcl_subscription_options_t & subscription_options,
bool use_intra_process);
RCLCPP_PUBLIC
virtual
void
add_subscription(
rclcpp::subscription::SubscriptionBase::SharedPtr subscription,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group);
private:
RCLCPP_DISABLE_COPY(NodeTopics)
NodeBaseInterface * node_base_;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_TOPICS_HPP_

View File

@@ -0,0 +1,78 @@
// Copyright 2016 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__NODE_INTERFACES__NODE_TOPICS_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_TOPICS_INTERFACE_HPP_
#include <functional>
#include <memory>
#include <string>
#include "rcl/publisher.h"
#include "rcl/subscription.h"
#include "rclcpp/macros.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/publisher_factory.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/subscription_factory.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Pure virtual interface class for the NodeTopics part of the Node API.
class NodeTopicsInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTopicsInterface)
RCLCPP_PUBLIC
virtual
rclcpp::publisher::PublisherBase::SharedPtr
create_publisher(
const std::string & topic_name,
const rclcpp::PublisherFactory & publisher_factory,
rcl_publisher_options_t & publisher_options,
bool use_intra_process) = 0;
RCLCPP_PUBLIC
virtual
void
add_publisher(
rclcpp::publisher::PublisherBase::SharedPtr publisher) = 0;
RCLCPP_PUBLIC
virtual
rclcpp::subscription::SubscriptionBase::SharedPtr
create_subscription(
const std::string & topic_name,
const rclcpp::SubscriptionFactory & subscription_factory,
rcl_subscription_options_t & subscription_options,
bool use_intra_process) = 0;
RCLCPP_PUBLIC
virtual
void
add_subscription(
rclcpp::subscription::SubscriptionBase::SharedPtr subscription,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) = 0;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_TOPICS_INTERFACE_HPP_

View File

@@ -16,6 +16,7 @@
#define RCLCPP__PARAMETER_CLIENT_HPP_
#include <string>
#include <utility>
#include <vector>
#include "rcl_interfaces/msg/parameter.hpp"
@@ -43,7 +44,7 @@ namespace parameter_client
class AsyncParametersClient
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(AsyncParametersClient);
RCLCPP_SMART_PTR_DEFINITIONS(AsyncParametersClient)
RCLCPP_PUBLIC
AsyncParametersClient(
@@ -117,7 +118,7 @@ private:
class SyncParametersClient
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(SyncParametersClient);
RCLCPP_SMART_PTR_DEFINITIONS(SyncParametersClient)
RCLCPP_PUBLIC
explicit SyncParametersClient(

View File

@@ -38,7 +38,7 @@ namespace parameter_service
class ParameterService
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(ParameterService);
RCLCPP_SMART_PTR_DEFINITIONS(ParameterService)
RCLCPP_PUBLIC
explicit ParameterService(

View File

@@ -28,43 +28,48 @@
#include "rcl/publisher.h"
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rmw/impl/cpp/demangle.hpp"
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
// Forward declaration for friend statement
namespace node
// Forward declaration is used for friend statement.
namespace node_interfaces
{
class Node;
} // namespace node
class NodeTopicsInterface;
}
namespace publisher
{
class PublisherBase
{
friend rclcpp::node_interfaces::NodeTopicsInterface;
public:
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase);
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
/// Default constructor.
/**
* Typically, a publisher is not created through this method, but instead is created through a
* call to `Node::create_publisher`.
* \param[in] node_handle The corresponding rcl representation of the owner node.
* \param[in] node_base A pointer to the NodeBaseInterface for the parent node.
* \param[in] topic The topic that this publisher publishes on.
* \param[in] queue_size The maximum number of unpublished messages to queue.
* \param[in] type_support The type support structure for the type to be published.
* \param[in] publisher_options QoS settings for this publisher.
*/
RCLCPP_PUBLIC
PublisherBase(
std::shared_ptr<rcl_node_t> node_handle,
std::string topic,
size_t queue_size);
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rosidl_message_type_support_t & type_support,
const rcl_publisher_options_t & publisher_options);
RCLCPP_PUBLIC
virtual ~PublisherBase();
@@ -72,7 +77,7 @@ public:
/// Get the topic that this publisher publishes on.
// \return The topic name.
RCLCPP_PUBLIC
const std::string &
const char *
get_topic_name() const;
/// Get the queue size for this publisher.
@@ -113,9 +118,9 @@ public:
bool
operator==(const rmw_gid_t * gid) const;
typedef std::function<uint64_t(uint64_t, void *, const std::type_info &)> StoreMessageCallbackT;
using StoreMessageCallbackT = std::function<uint64_t(uint64_t, void *, const std::type_info &)>;
protected:
/// Implementation utility function used to setup intra process publishing after creation.
RCLCPP_PUBLIC
void
setup_intra_process(
@@ -123,14 +128,11 @@ protected:
StoreMessageCallbackT callback,
const rcl_publisher_options_t & intra_process_options);
std::shared_ptr<rcl_node_t> node_handle_;
protected:
std::shared_ptr<rcl_node_t> rcl_node_handle_;
rcl_publisher_t publisher_handle_ = rcl_get_zero_initialized_publisher();
rcl_publisher_t intra_process_publisher_handle_ = rcl_get_zero_initialized_publisher();
rcl_allocator_t rcl_allocator_ = rcl_get_default_allocator();
std::string topic_;
size_t queue_size_;
uint64_t intra_process_publisher_id_;
StoreMessageCallbackT store_intra_process_message_;
@@ -143,74 +145,38 @@ protected:
template<typename MessageT, typename Alloc = std::allocator<void>>
class Publisher : public PublisherBase
{
friend rclcpp::node::Node;
public:
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<MessageT, Alloc>);
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<MessageT, Alloc>)
Publisher(
std::shared_ptr<rcl_node_t> node_handle,
std::string topic,
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rcl_publisher_options_t & publisher_options,
std::shared_ptr<MessageAlloc> allocator)
: PublisherBase(node_handle, topic, publisher_options.qos.depth), message_allocator_(allocator)
const std::shared_ptr<MessageAlloc> & allocator)
: PublisherBase(
node_base,
topic,
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
publisher_options),
message_allocator_(allocator)
{
using rosidl_generator_cpp::get_message_type_support_handle;
allocator::set_allocator_for_deleter(&message_deleter_, message_allocator_.get());
rcl_allocator_ = publisher_options.allocator;
auto type_support_handle = get_message_type_support_handle<MessageT>();
if (rcl_publisher_init(
&publisher_handle_, node_handle_.get(), type_support_handle,
topic.c_str(), &publisher_options) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("could not create publisher: ") +
rcl_get_error_string_safe());
}
// Life time of this object is tied to the publisher handle.
rmw_publisher_t * publisher_rmw_handle = rcl_publisher_get_rmw_handle(&publisher_handle_);
if (!publisher_rmw_handle) {
throw std::runtime_error(
std::string("failed to get rmw handle: ") + rcl_get_error_string_safe());
}
if (rmw_get_gid_for_publisher(publisher_rmw_handle, &rmw_gid_) != RMW_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("failed to get publisher gid: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
}
virtual ~Publisher()
{
if (rcl_publisher_fini(&intra_process_publisher_handle_, node_handle_.get()) != RCL_RET_OK) {
fprintf(
stderr,
"Error in destruction of intra process rcl publisher handle: %s\n",
rcl_get_error_string_safe());
}
if (rcl_publisher_fini(&publisher_handle_, node_handle_.get()) != RCL_RET_OK) {
fprintf(
stderr,
"Error in destruction of rcl publisher handle: %s\n",
rcl_get_error_string_safe());
}
}
{}
/// Send a message to the topic for this publisher.
/**
* This function is templated on the input message type, MessageT.
* \param[in] msg A shared pointer to the message to send.
*/
void
virtual void
publish(std::unique_ptr<MessageT, MessageDeleter> & msg)
{
this->do_inter_process_publish(msg.get());
@@ -242,7 +208,7 @@ public:
}
}
void
virtual void
publish(const std::shared_ptr<MessageT> & msg)
{
// Avoid allocating when not using intra process.
@@ -261,7 +227,7 @@ public:
return this->publish(unique_msg);
}
void
virtual void
publish(std::shared_ptr<const MessageT> msg)
{
// Avoid allocating when not using intra process.
@@ -280,7 +246,7 @@ public:
return this->publish(unique_msg);
}
void
virtual void
publish(const MessageT & msg)
{
// Avoid allocating when not using intra process.
@@ -319,6 +285,7 @@ protected:
};
} // namespace publisher
} // namespace rclcpp
#endif // RCLCPP__PUBLISHER_HPP_

View File

@@ -0,0 +1,147 @@
// Copyright 2016 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__PUBLISHER_FACTORY_HPP_
#define RCLCPP__PUBLISHER_FACTORY_HPP_
#include <functional>
#include <memory>
#include <string>
#include "rcl/publisher.h"
#include "rosidl_typesupport_cpp/message_type_support.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// Factory with functions used to create a MessageT specific PublisherT.
/* This factory class is used to encapsulate the template generated functions
* which are used during the creation of a Message type specific publisher
* within a non-templated class.
*
* It is created using the create_publisher_factory function, which is usually
* called from a templated "create_publisher" method on the Node class, and
* is passed to the non-templated "create_publisher" method on the NodeTopics
* class where it is used to create and setup the Publisher.
*/
struct PublisherFactory
{
// Creates a PublisherT<MessageT, ...> publisher object and returns it as a PublisherBase.
using PublisherFactoryFunction = std::function<
rclcpp::publisher::PublisherBase::SharedPtr(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_publisher_options_t & publisher_options)>;
PublisherFactoryFunction create_typed_publisher;
// Adds the PublisherBase to the intraprocess manager with the correctly
// templated call to IntraProcessManager::store_intra_process_message.
using AddPublisherToIntraProcessManagerFunction = std::function<
uint64_t(
rclcpp::intra_process_manager::IntraProcessManager * ipm,
rclcpp::publisher::PublisherBase::SharedPtr publisher)>;
AddPublisherToIntraProcessManagerFunction add_publisher_to_intra_process_manager;
// Creates the callback function which is called on each
// PublisherT::publish() and which handles the intra process transmission of
// the message being published.
using SharedPublishCallbackFactoryFunction = std::function<
rclcpp::publisher::PublisherBase::StoreMessageCallbackT(
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm)>;
SharedPublishCallbackFactoryFunction create_shared_publish_callback;
};
/// Return a PublisherFactory with functions setup for creating a PublisherT<MessageT, Alloc>.
template<typename MessageT, typename Alloc, typename PublisherT>
PublisherFactory
create_publisher_factory(std::shared_ptr<Alloc> allocator)
{
PublisherFactory factory;
// factory function that creates a MessageT specific PublisherT
factory.create_typed_publisher =
[allocator](
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_publisher_options_t & publisher_options) -> std::shared_ptr<PublisherT>
{
auto message_alloc = std::make_shared<typename PublisherT::MessageAlloc>(*allocator.get());
publisher_options.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc.get());
return std::make_shared<PublisherT>(node_base, topic_name, publisher_options, message_alloc);
};
// function to add a publisher to the intra process manager
factory.add_publisher_to_intra_process_manager =
[](
rclcpp::intra_process_manager::IntraProcessManager * ipm,
rclcpp::publisher::PublisherBase::SharedPtr publisher) -> uint64_t
{
return ipm->add_publisher<MessageT, Alloc>(std::dynamic_pointer_cast<PublisherT>(publisher));
};
// function to create a shared publish callback std::function
using StoreMessageCallbackT = rclcpp::publisher::PublisherBase::StoreMessageCallbackT;
factory.create_shared_publish_callback =
[](rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm) -> StoreMessageCallbackT
{
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = ipm;
// this function is called on each call to publish() and handles storing
// of the published message in the intra process manager
auto shared_publish_callback =
[weak_ipm](uint64_t publisher_id, void * msg, const std::type_info & type_info) -> uint64_t
{
auto ipm = weak_ipm.lock();
if (!ipm) {
// TODO(wjwwood): should this just return silently? Or maybe return with a warning?
throw std::runtime_error(
"intra process publish called after destruction of intra process manager");
}
if (!msg) {
throw std::runtime_error("cannot publisher msg which is a null pointer");
}
auto & message_type_info = typeid(MessageT);
if (message_type_info != type_info) {
throw std::runtime_error(
std::string("published type '") + type_info.name() +
"' is incompatible from the publisher type '" + message_type_info.name() + "'");
}
MessageT * typed_message_ptr = static_cast<MessageT *>(msg);
using MessageDeleter = typename publisher::Publisher<MessageT, Alloc>::MessageDeleter;
std::unique_ptr<MessageT, MessageDeleter> unique_msg(typed_message_ptr);
uint64_t message_seq =
ipm->store_intra_process_message<MessageT, Alloc>(publisher_id, unique_msg);
return message_seq;
};
return shared_publish_callback;
};
// return the factory now that it is populated
return factory;
}
} // namespace rclcpp
#endif // RCLCPP__PUBLISHER_FACTORY_HPP_

View File

@@ -31,7 +31,7 @@ namespace rate
class RateBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(RateBase);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(RateBase)
virtual bool sleep() = 0;
virtual bool is_steady() const = 0;
@@ -46,7 +46,7 @@ template<class Clock = std::chrono::high_resolution_clock>
class GenericRate : public RateBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(GenericRate);
RCLCPP_SMART_PTR_DEFINITIONS(GenericRate)
explicit GenericRate(double rate)
: GenericRate<Clock>(
@@ -106,7 +106,7 @@ public:
}
private:
RCLCPP_DISABLE_COPY(GenericRate);
RCLCPP_DISABLE_COPY(GenericRate)
std::chrono::nanoseconds period_;
using ClockDurationNano = std::chrono::duration<typename Clock::rep, std::nano>;

View File

@@ -27,39 +27,6 @@
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
// NOLINTNEXTLINE(runtime/int)
inline const std::chrono::seconds operator"" _s(unsigned long long s)
{
return std::chrono::seconds(s);
}
inline const std::chrono::nanoseconds operator"" _s(long double s)
{
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::duration<long double>(s));
}
// NOLINTNEXTLINE(runtime/int)
inline const std::chrono::nanoseconds operator"" _ms(unsigned long long ms)
{
return std::chrono::milliseconds(ms);
}
inline const std::chrono::nanoseconds operator"" _ms(long double ms)
{
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::duration<long double, std::milli>(ms));
}
// NOLINTNEXTLINE(runtime/int)
inline const std::chrono::nanoseconds operator"" _ns(unsigned long long ns)
{
return std::chrono::nanoseconds(ns);
}
inline const std::chrono::nanoseconds operator"" _ns(long double ns)
{
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::duration<long double, std::nano>(ns));
}
namespace rclcpp
{

View File

@@ -39,12 +39,16 @@ namespace service
class ServiceBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ServiceBase);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ServiceBase)
RCLCPP_PUBLIC
ServiceBase(
std::shared_ptr<rcl_node_t> node_handle,
const std::string service_name);
const std::string & service_name);
RCLCPP_PUBLIC
explicit ServiceBase(
std::shared_ptr<rcl_node_t> node_handle);
RCLCPP_PUBLIC
virtual ~ServiceBase();
@@ -64,12 +68,13 @@ public:
std::shared_ptr<void> request) = 0;
protected:
RCLCPP_DISABLE_COPY(ServiceBase);
RCLCPP_DISABLE_COPY(ServiceBase)
std::shared_ptr<rcl_node_t> node_handle_;
rcl_service_t service_handle_ = rcl_get_zero_initialized_service();
rcl_service_t * service_handle_ = nullptr;
std::string service_name_;
bool owns_rcl_handle_ = true;
};
using any_service_callback::AnyServiceCallback;
@@ -88,7 +93,7 @@ public:
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<typename ServiceT::Request>,
std::shared_ptr<typename ServiceT::Response>)>;
RCLCPP_SMART_PTR_DEFINITIONS(Service);
RCLCPP_SMART_PTR_DEFINITIONS(Service)
Service(
std::shared_ptr<rcl_node_t> node_handle,
@@ -97,28 +102,56 @@ public:
rcl_service_options_t & service_options)
: ServiceBase(node_handle, service_name), any_callback_(any_callback)
{
using rosidl_generator_cpp::get_service_type_support_handle;
using rosidl_typesupport_cpp::get_service_type_support_handle;
auto service_type_support_handle = get_service_type_support_handle<ServiceT>();
// rcl does the static memory allocation here
service_handle_ = new rcl_service_t;
*service_handle_ = rcl_get_zero_initialized_service();
if (rcl_service_init(
&service_handle_, node_handle.get(), service_type_support_handle, service_name.c_str(),
service_handle_, node_handle.get(), service_type_support_handle, service_name.c_str(),
&service_options) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("could not create service: ") +
throw std::runtime_error(std::string("could not create service: ") +
rcl_get_error_string_safe());
}
}
Service(
std::shared_ptr<rcl_node_t> node_handle,
rcl_service_t * service_handle,
AnyServiceCallback<ServiceT> any_callback)
: ServiceBase(node_handle),
any_callback_(any_callback)
{
// check if service handle was initialized
// TODO(karsten1987): Take this verification
// directly in rcl_*_t
// see: https://github.com/ros2/rcl/issues/81
if (!service_handle->impl) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("rcl_service_t in constructor argument must be initialized beforehand."));
// *INDENT-ON*
}
service_handle_ = service_handle;
service_name_ = std::string(rcl_service_get_service_name(service_handle));
owns_rcl_handle_ = false;
}
Service() = delete;
virtual ~Service()
{
if (rcl_service_fini(&service_handle_, node_handle_.get()) != RCL_RET_OK) {
std::stringstream ss;
ss << "Error in destruction of rcl service_handle_ handle: " <<
rcl_get_error_string_safe() << '\n';
(std::cerr << ss.str()).flush();
// check if you have ownership of the handle
if (owns_rcl_handle_) {
if (rcl_service_fini(service_handle_, node_handle_.get()) != RCL_RET_OK) {
std::stringstream ss;
ss << "Error in destruction of rcl service_handle_ handle: " <<
rcl_get_error_string_safe() << '\n';
(std::cerr << ss.str()).flush();
}
}
}
@@ -158,7 +191,7 @@ public:
}
private:
RCLCPP_DISABLE_COPY(Service);
RCLCPP_DISABLE_COPY(Service)
AnyServiceCallback<ServiceT> any_callback_;
};

View File

@@ -44,7 +44,7 @@ template<typename Alloc = std::allocator<void>>
class AllocatorMemoryStrategy : public memory_strategy::MemoryStrategy
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(AllocatorMemoryStrategy<Alloc>);
RCLCPP_SMART_PTR_DEFINITIONS(AllocatorMemoryStrategy<Alloc>)
using ExecAllocTraits = allocator::AllocRebind<executor::AnyExecutable, Alloc>;
using ExecAlloc = typename ExecAllocTraits::allocator_type;
@@ -52,8 +52,6 @@ public:
using VoidAllocTraits = typename allocator::AllocRebind<void *, Alloc>;
using VoidAlloc = typename VoidAllocTraits::allocator_type;
using WeakNodeVector = std::vector<std::weak_ptr<node::Node>>;
explicit AllocatorMemoryStrategy(std::shared_ptr<Alloc> allocator)
{
executable_allocator_ = std::make_shared<ExecAlloc>(*allocator.get());
@@ -144,7 +142,7 @@ public:
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
has_invalid_weak_nodes = false;
has_invalid_weak_nodes = true;
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
@@ -162,7 +160,8 @@ public:
}
}
}
for (auto & service : group->get_service_ptrs()) {
for (auto & weak_service : group->get_service_ptrs()) {
auto service = weak_service.lock();
if (service) {
service_handles_.push_back(service->get_service_handle());
}
@@ -265,7 +264,7 @@ public:
any_exec->subscription = subscription;
}
any_exec->callback_group = group;
any_exec->node = get_node_by_group(group, weak_nodes);
any_exec->node_base = get_node_by_group(group, weak_nodes);
subscription_handles_.erase(it);
return;
}
@@ -299,7 +298,7 @@ public:
// Otherwise it is safe to set and return the any_exec
any_exec->service = service;
any_exec->callback_group = group;
any_exec->node = get_node_by_group(group, weak_nodes);
any_exec->node_base = get_node_by_group(group, weak_nodes);
service_handles_.erase(it);
return;
}
@@ -332,7 +331,7 @@ public:
// Otherwise it is safe to set and return the any_exec
any_exec->client = client;
any_exec->callback_group = group;
any_exec->node = get_node_by_group(group, weak_nodes);
any_exec->node_base = get_node_by_group(group, weak_nodes);
client_handles_.erase(it);
return;
}

View File

@@ -15,6 +15,8 @@
#ifndef RCLCPP__STRATEGIES__MESSAGE_POOL_MEMORY_STRATEGY_HPP_
#define RCLCPP__STRATEGIES__MESSAGE_POOL_MEMORY_STRATEGY_HPP_
#include <memory>
#include "rclcpp/macros.hpp"
#include "rclcpp/message_memory_strategy.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -44,7 +46,7 @@ class MessagePoolMemoryStrategy
: public message_memory_strategy::MessageMemoryStrategy<MessageT>
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(MessagePoolMemoryStrategy);
RCLCPP_SMART_PTR_DEFINITIONS(MessagePoolMemoryStrategy)
/// Default constructor
MessagePoolMemoryStrategy()

View File

@@ -38,10 +38,10 @@
namespace rclcpp
{
namespace node
namespace node_interfaces
{
class Node;
} // namespace node
class NodeTopicsInterface;
} // namespace node_interfaces
namespace subscription
{
@@ -51,19 +51,21 @@ namespace subscription
class SubscriptionBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase)
/// Default constructor.
/**
* \param[in] node_handle The rcl representation of the node that owns this subscription.
* \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
* \param[in] topic_name Name of the topic to subscribe to.
* \param[in] ignore_local_publications True to ignore local publications (unused).
* \param[in] subscription_options options for the subscription.
*/
RCLCPP_PUBLIC
SubscriptionBase(
std::shared_ptr<rcl_node_t> node_handle,
const rosidl_message_type_support_t & type_support_handle,
const std::string & topic_name,
bool ignore_local_publications);
const rcl_subscription_options_t & subscription_options);
/// Default destructor.
RCLCPP_PUBLIC
@@ -71,7 +73,7 @@ public:
/// Get the topic that this subscription is subscribed on.
RCLCPP_PUBLIC
const std::string &
const char *
get_topic_name() const;
RCLCPP_PUBLIC
@@ -109,9 +111,7 @@ protected:
std::shared_ptr<rcl_node_t> node_handle_;
private:
RCLCPP_DISABLE_COPY(SubscriptionBase);
std::string topic_name_;
bool ignore_local_publications_;
RCLCPP_DISABLE_COPY(SubscriptionBase)
};
using any_subscription_callback::AnySubscriptionCallback;
@@ -120,7 +120,7 @@ using any_subscription_callback::AnySubscriptionCallback;
template<typename MessageT, typename Alloc = std::allocator<void>>
class Subscription : public SubscriptionBase
{
friend class rclcpp::node::Node;
friend class rclcpp::node_interfaces::NodeTopicsInterface;
public:
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
@@ -128,7 +128,7 @@ public:
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
RCLCPP_SMART_PTR_DEFINITIONS(Subscription);
RCLCPP_SMART_PTR_DEFINITIONS(Subscription)
/// Default constructor.
/**
@@ -136,8 +136,8 @@ public:
* should be instantiated through Node::create_subscription.
* \param[in] node_handle rcl representation of the node that owns this subscription.
* \param[in] topic_name Name of the topic to subscribe to.
* \param[in] ignore_local_publications True to ignore local publications (unused).
* \param[in] callback User-defined callback to call when a message is received.
* \param[in] subscription_options options for the subscription.
* \param[in] callback User defined callback to call when a message is received.
* \param[in] memory_strategy The memory strategy to be used for managing message memory.
*/
Subscription(
@@ -149,23 +149,15 @@ public:
memory_strategy = message_memory_strategy::MessageMemoryStrategy<MessageT,
Alloc>::create_default())
: SubscriptionBase(
node_handle, topic_name, subscription_options.ignore_local_publications),
node_handle,
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
topic_name,
subscription_options),
any_callback_(callback),
message_memory_strategy_(memory_strategy),
get_intra_process_message_callback_(nullptr),
matches_any_intra_process_publishers_(nullptr)
{
using rosidl_generator_cpp::get_message_type_support_handle;
auto type_support_handle = get_message_type_support_handle<MessageT>();
if (rcl_subscription_init(
&subscription_handle_, node_handle_.get(), type_support_handle, topic_name.c_str(),
&subscription_options) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("could not create subscription: ") + rcl_get_error_string_safe());
}
}
{}
/// Support dynamically setting the message memory strategy.
/**
@@ -232,13 +224,11 @@ public:
any_callback_.dispatch_intra_process(msg, message_info);
}
private:
typedef
std::function<
void (uint64_t, uint64_t, uint64_t, MessageUniquePtr &)
> GetMessageCallbackType;
typedef std::function<bool (const rmw_gid_t *)> MatchesAnyPublishersCallbackType;
using GetMessageCallbackType =
std::function<void(uint64_t, uint64_t, uint64_t, MessageUniquePtr &)>;
using MatchesAnyPublishersCallbackType = std::function<bool(const rmw_gid_t *)>;
/// Implemenation detail.
void setup_intra_process(
uint64_t intra_process_subscription_id,
GetMessageCallbackType get_message_callback,
@@ -248,7 +238,7 @@ private:
if (rcl_subscription_init(
&intra_process_subscription_handle_, node_handle_.get(),
rclcpp::type_support::get_intra_process_message_msg_type_support(),
(get_topic_name() + "__intra").c_str(),
(std::string(get_topic_name()) + "__intra").c_str(),
&intra_process_options) != RCL_RET_OK)
{
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
@@ -262,6 +252,7 @@ private:
matches_any_intra_process_publishers_ = matches_any_publisher_callback;
}
/// Implemenation detail.
const rcl_subscription_t *
get_intra_process_subscription_handle() const
{
@@ -271,7 +262,8 @@ private:
return &intra_process_subscription_handle_;
}
RCLCPP_DISABLE_COPY(Subscription);
private:
RCLCPP_DISABLE_COPY(Subscription)
AnySubscriptionCallback<MessageT, Alloc> any_callback_;
typename message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr

View File

@@ -0,0 +1,170 @@
// Copyright 2016 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__SUBSCRIPTION_FACTORY_HPP_
#define RCLCPP__SUBSCRIPTION_FACTORY_HPP_
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include "rcl/subscription.h"
#include "rosidl_typesupport_cpp/message_type_support.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// Factory with functions used to create a Subscription<MessageT>.
/* This factory class is used to encapsulate the template generated functions
* which are used during the creation of a Message type specific subscription
* within a non-templated class.
*
* It is created using the create_subscription_factory function, which is
* usually called from a templated "create_subscription" method of the Node
* class, and is passed to the non-templated "create_subscription" method of
* the NodeTopics class where it is used to create and setup the Subscription.
*/
struct SubscriptionFactory
{
// Creates a Subscription<MessageT> object and returns it as a SubscriptionBase.
using SubscriptionFactoryFunction = std::function<
rclcpp::subscription::SubscriptionBase::SharedPtr(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_subscription_options_t & subscription_options)>;
SubscriptionFactoryFunction create_typed_subscription;
// Function that takes a MessageT from the intra process manager
using SetupIntraProcessFunction = std::function<void(
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm,
rclcpp::subscription::SubscriptionBase::SharedPtr subscription,
const rcl_subscription_options_t & subscription_options)>;
SetupIntraProcessFunction setup_intra_process;
};
/// Return a SubscriptionFactory with functions for creating a SubscriptionT<MessageT, Alloc>.
template<typename MessageT, typename CallbackT, typename Alloc, typename SubscriptionT>
SubscriptionFactory
create_subscription_factory(
CallbackT && callback,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
msg_mem_strat,
std::shared_ptr<Alloc> allocator)
{
SubscriptionFactory factory;
using rclcpp::subscription::AnySubscriptionCallback;
AnySubscriptionCallback<MessageT, Alloc> any_subscription_callback(allocator);
any_subscription_callback.set(std::forward<CallbackT>(callback));
auto message_alloc =
std::make_shared<typename subscription::Subscription<MessageT, Alloc>::MessageAlloc>();
// factory function that creates a MessageT specific SubscriptionT
factory.create_typed_subscription =
[allocator, msg_mem_strat, any_subscription_callback, message_alloc](
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_subscription_options_t & subscription_options
) -> rclcpp::subscription::SubscriptionBase::SharedPtr
{
subscription_options.allocator =
rclcpp::allocator::get_rcl_allocator<MessageT>(*message_alloc.get());
using rclcpp::subscription::Subscription;
using rclcpp::subscription::SubscriptionBase;
auto sub = Subscription<MessageT, Alloc>::make_shared(
node_base->get_shared_rcl_node_handle(),
topic_name,
subscription_options,
any_subscription_callback,
msg_mem_strat);
auto sub_base_ptr = std::dynamic_pointer_cast<SubscriptionBase>(sub);
return sub_base_ptr;
};
// function that will setup intra process communications for the subscription
factory.setup_intra_process =
[message_alloc](
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm,
rclcpp::subscription::SubscriptionBase::SharedPtr subscription,
const rcl_subscription_options_t & subscription_options)
{
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = ipm;
uint64_t intra_process_subscription_id = ipm->add_subscription(subscription);
auto intra_process_options = rcl_subscription_get_default_options();
intra_process_options.allocator = rclcpp::allocator::get_rcl_allocator<MessageT>(
*message_alloc.get());
intra_process_options.qos = subscription_options.qos;
intra_process_options.ignore_local_publications = false;
// function that will be called to take a MessageT from the intra process manager
auto take_intra_process_message_func =
[weak_ipm](
uint64_t publisher_id,
uint64_t message_sequence,
uint64_t subscription_id,
typename rclcpp::subscription::Subscription<MessageT, Alloc>::MessageUniquePtr & message)
{
auto ipm = weak_ipm.lock();
if (!ipm) {
// TODO(wjwwood): should this just return silently? Or return with a logged warning?
throw std::runtime_error(
"intra process take called after destruction of intra process manager");
}
ipm->take_intra_process_message<MessageT, Alloc>(
publisher_id, message_sequence, subscription_id, message);
};
// function that is called to see if the publisher id matches any local publishers
auto matches_any_publisher_func =
[weak_ipm](const rmw_gid_t * sender_gid) -> bool
{
auto ipm = weak_ipm.lock();
if (!ipm) {
throw std::runtime_error(
"intra process publisher check called "
"after destruction of intra process manager");
}
return ipm->matches_any_publishers(sender_gid);
};
auto typed_sub_ptr = std::dynamic_pointer_cast<SubscriptionT>(subscription);
typed_sub_ptr->setup_intra_process(
intra_process_subscription_id,
take_intra_process_message_func,
matches_any_publisher_func,
intra_process_options
);
};
// end definition of factory function to setup intra process
// return the factory now that it is populated
return factory;
}
} // namespace rclcpp
#endif // RCLCPP__SUBSCRIPTION_FACTORY_HPP_

View File

@@ -21,6 +21,7 @@
#include <sstream>
#include <thread>
#include <type_traits>
#include <utility>
#include "rclcpp/function_traits.hpp"
#include "rclcpp/macros.hpp"
@@ -42,7 +43,7 @@ namespace timer
class TimerBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(TimerBase);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(TimerBase)
RCLCPP_PUBLIC
explicit TimerBase(std::chrono::nanoseconds period);
@@ -102,7 +103,7 @@ template<
class GenericTimer : public TimerBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(GenericTimer);
RCLCPP_SMART_PTR_DEFINITIONS(GenericTimer)
/// Default constructor.
/**
@@ -169,7 +170,7 @@ public:
}
protected:
RCLCPP_DISABLE_COPY(GenericTimer);
RCLCPP_DISABLE_COPY(GenericTimer)
FunctorT callback_;
};

View File

@@ -18,8 +18,8 @@
#include "rosidl_generator_cpp/message_type_support_decl.hpp"
#include "rosidl_generator_cpp/service_type_support_decl.hpp"
#include "rosidl_generator_cpp/message_type_support.hpp"
#include "rosidl_generator_cpp/service_type_support.hpp"
#include "rosidl_typesupport_cpp/message_type_support.hpp"
#include "rosidl_typesupport_cpp/service_type_support.hpp"
#include "rclcpp/visibility_control.hpp"

View File

@@ -22,8 +22,6 @@
#ifndef RCLCPP__VISIBILITY_CONTROL_HPP_
#define RCLCPP__VISIBILITY_CONTROL_HPP_
#include "rmw/rmw.h"
// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
// https://gcc.gnu.org/wiki/Visibility

View File

@@ -14,8 +14,10 @@
<build_depend>rcl_interfaces</build_depend>
<build_depend>rmw_implementation_cmake</build_depend>
<build_depend>rosidl_generator_cpp</build_depend>
<build_depend>rosidl_typesupport_cpp</build_depend>
<build_export_depend>rcl_interfaces</build_export_depend>
<build_export_depend>rosidl_generator_cpp</build_export_depend>
<build_export_depend>rosidl_typesupport_cpp</build_export_depend>
<depend>rcl</depend>
<depend>rmw_implementation</depend>

View File

@@ -14,16 +14,17 @@
# copied from rclcpp/rclcpp-extras.cmake
include("${rclcpp_DIR}/get_rclcpp_information.cmake")
# register ament_package() hook for node plugins once
macro(_rclcpp_register_package_hook)
if(NOT DEFINED _RCLCPP_PACKAGE_HOOK_REGISTERED)
set(_RCLCPP_PACKAGE_HOOK_REGISTERED TRUE)
set(rclcpp_node_main_SRC "${rclcpp_DIR}/../../../src/rclcpp/node_main.cpp")
function(rclcpp_create_node_main node_library_target)
if(NOT TARGET ${node_library_target})
message(FATAL_ERROR "rclcpp_create_node_main() the first argument must be a valid target name")
find_package(ament_cmake_core QUIET REQUIRED)
ament_register_extension("ament_package" "rclcpp"
"rclcpp_package_hook.cmake")
endif()
set(executable_name_ ${node_library_target}_node)
add_executable(${executable_name_} ${rclcpp_node_main_SRC})
target_link_libraries(${executable_name_} ${node_library_target})
install(TARGETS ${executable_name_} DESTINATION bin)
endfunction()
endmacro()
include("${rclcpp_DIR}/get_rclcpp_information.cmake")
include("${rclcpp_DIR}/rclcpp_create_node_main.cmake")
include("${rclcpp_DIR}/rclcpp_register_node_plugins.cmake")

View File

@@ -23,7 +23,7 @@ AnyExecutable::AnyExecutable()
service(nullptr),
client(nullptr),
callback_group(nullptr),
node(nullptr)
node_base(nullptr)
{}
AnyExecutable::~AnyExecutable()

View File

@@ -37,7 +37,7 @@ CallbackGroup::get_timer_ptrs() const
return timer_ptrs_;
}
const std::vector<rclcpp::service::ServiceBase::SharedPtr> &
const std::vector<rclcpp::service::ServiceBase::WeakPtr> &
CallbackGroup::get_service_ptrs() const
{
std::lock_guard<std::mutex> lock(mutex_);

View File

@@ -16,13 +16,15 @@
#include <chrono>
#include <cstdio>
#include <memory>
#include <string>
#include "rcl/graph.h"
#include "rcl/node.h"
#include "rcl/wait.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/utilities.hpp"
using rclcpp::client::ClientBase;
@@ -30,9 +32,11 @@ using rclcpp::exceptions::InvalidNodeError;
using rclcpp::exceptions::throw_from_rcl_error;
ClientBase::ClientBase(
std::shared_ptr<rclcpp::node::Node> parent_node,
rclcpp::node_interfaces::NodeBaseInterface * node_base,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
const std::string & service_name)
: node_(parent_node), node_handle_(parent_node->get_shared_node_handle()),
: node_graph_(node_graph),
node_handle_(node_base->get_shared_rcl_node_handle()),
service_name_(service_name)
{}
@@ -75,12 +79,13 @@ ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
return false;
}
// make an event to reuse, rather than create a new one each time
auto node_ptr = node_.lock();
auto node_ptr = node_graph_.lock();
if (!node_ptr) {
throw InvalidNodeError();
}
auto event = node_ptr->get_graph_event();
// update the time even on the first loop to account for time in first server_is_read()
// update the time even on the first loop to account for time spent in the first call
// to this->server_is_ready()
std::chrono::nanoseconds time_to_wait = timeout - (std::chrono::steady_clock::now() - start);
if (timeout > std::chrono::nanoseconds(0) && time_to_wait < std::chrono::nanoseconds(0)) {
// Do not allow the time_to_wait to become negative when timeout was originally positive.
@@ -94,10 +99,13 @@ ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
return false;
}
node_ptr->wait_for_graph_change(event, time_to_wait);
if (event->check_and_clear()) {
if (this->service_is_ready()) {
return true;
}
event->check_and_clear(); // reset the event
// always check if the service is ready, even if the graph event wasn't triggered
// this is needed to avoid a race condition that is specific to the Connext RMW implementation
// (see https://github.com/ros2/rmw_connext/issues/201)
if (this->service_is_ready()) {
return true;
}
// server is not ready, loop if there is time left
time_to_wait = timeout - (std::chrono::steady_clock::now() - start);

View File

@@ -13,6 +13,7 @@
// limitations under the License.
#include <algorithm>
#include <memory>
#include <string>
#include <type_traits>
@@ -20,6 +21,7 @@
#include "rcl/error_handling.h"
#include "rclcpp/executor.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rclcpp/utilities.hpp"
@@ -85,10 +87,11 @@ Executor::~Executor()
}
void
Executor::add_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify)
Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
{
// If the node already has an executor
if (node_ptr->has_executor.exchange(true)) {
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.");
}
// Check to ensure node not already added
@@ -111,14 +114,20 @@ Executor::add_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify)
}
void
Executor::remove_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify)
Executor::add_node(std::shared_ptr<rclcpp::node::Node> node_ptr, bool notify)
{
this->add_node(node_ptr->get_node_base_interface(), notify);
}
void
Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
{
bool node_removed = false;
weak_nodes_.erase(
std::remove_if(
weak_nodes_.begin(), weak_nodes_.end(),
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
[&](std::weak_ptr<rclcpp::node::Node> & i)
[&](rclcpp::node_interfaces::NodeBaseInterface::WeakPtr & i)
{
bool matched = (i.lock() == node_ptr);
node_removed |= matched;
@@ -127,7 +136,8 @@ Executor::remove_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify)
// *INDENT-ON*
)
);
node_ptr->has_executor.store(false);
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
has_executor.store(false);
if (notify) {
// If the node was matched and removed, interrupt waiting
if (node_removed) {
@@ -139,9 +149,15 @@ Executor::remove_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify)
memory_strategy_->remove_guard_condition(node_ptr->get_notify_guard_condition());
}
void
Executor::remove_node(std::shared_ptr<rclcpp::node::Node> node_ptr, bool notify)
{
this->remove_node(node_ptr->get_node_base_interface(), notify);
}
void
Executor::spin_node_once_nanoseconds(
rclcpp::node::Node::SharedPtr node,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
std::chrono::nanoseconds timeout)
{
this->add_node(node, false);
@@ -151,13 +167,19 @@ Executor::spin_node_once_nanoseconds(
}
void
Executor::spin_node_some(rclcpp::node::Node::SharedPtr node)
Executor::spin_node_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node)
{
this->add_node(node, false);
spin_some();
this->remove_node(node, false);
}
void
Executor::spin_node_some(std::shared_ptr<rclcpp::node::Node> node)
{
this->spin_node_some(node->get_node_base_interface());
}
void
Executor::spin_some()
{
@@ -247,7 +269,7 @@ Executor::execute_subscription(
} else if (ret != RCL_RET_SUBSCRIPTION_TAKE_FAILED) {
fprintf(stderr,
"[rclcpp::error] take failed for subscription on topic '%s': %s\n",
subscription->get_topic_name().c_str(), rcl_get_error_string_safe());
subscription->get_topic_name(), rcl_get_error_string_safe());
}
subscription->return_message(message);
}
@@ -269,7 +291,7 @@ Executor::execute_intra_process_subscription(
} else if (status != RCL_RET_SUBSCRIPTION_TAKE_FAILED) {
fprintf(stderr,
"[rclcpp::error] take failed for intra process subscription on topic '%s': %s\n",
subscription->get_topic_name().c_str(), rcl_get_error_string_safe());
subscription->get_topic_name(), rcl_get_error_string_safe());
}
}
@@ -290,11 +312,9 @@ Executor::execute_service(
service->get_service_handle(),
request_header.get(),
request.get());
if (status != RCL_RET_SERVICE_TAKE_FAILED) {
if (status == RCL_RET_OK) {
service->handle_request(request_header, request);
}
} else {
if (status == RCL_RET_OK) {
service->handle_request(request_header, request);
} else if (status != RCL_RET_SERVICE_TAKE_FAILED) {
fprintf(stderr,
"[rclcpp::error] take request failed for server of service '%s': %s\n",
service->get_service_name().c_str(), rcl_get_error_string_safe());
@@ -311,11 +331,9 @@ Executor::execute_client(
client->get_client_handle(),
request_header.get(),
response.get());
if (status != RCL_RET_SERVICE_TAKE_FAILED) {
if (status == RCL_RET_OK) {
client->handle_response(request_header, response);
}
} else {
if (status == RCL_RET_OK) {
client->handle_response(request_header, response);
} else if (status != RCL_RET_SERVICE_TAKE_FAILED) {
fprintf(stderr,
"[rclcpp::error] take response failed for client of service '%s': %s\n",
client->get_service_name().c_str(), rcl_get_error_string_safe());
@@ -335,7 +353,7 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
remove_if(
weak_nodes_.begin(), weak_nodes_.end(),
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
[](std::weak_ptr<rclcpp::node::Node> i)
[](rclcpp::node_interfaces::NodeBaseInterface::WeakPtr i)
{
return i.expired();
}
@@ -415,11 +433,11 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
}
}
rclcpp::node::Node::SharedPtr
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
Executor::get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
if (!group) {
return rclcpp::node::Node::SharedPtr();
return nullptr;
}
for (auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
@@ -433,7 +451,7 @@ Executor::get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr gro
}
}
}
return rclcpp::node::Node::SharedPtr();
return nullptr;
}
rclcpp::callback_group::CallbackGroup::SharedPtr

View File

@@ -15,17 +15,29 @@
#include "rclcpp/executors.hpp"
void
rclcpp::spin_some(node::Node::SharedPtr node_ptr)
rclcpp::spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
{
rclcpp::executors::SingleThreadedExecutor exec;
exec.spin_node_some(node_ptr);
}
void
rclcpp::spin(node::Node::SharedPtr node_ptr)
rclcpp::spin_some(rclcpp::node::Node::SharedPtr node_ptr)
{
rclcpp::spin_some(node_ptr->get_node_base_interface());
}
void
rclcpp::spin(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
{
rclcpp::executors::SingleThreadedExecutor exec;
exec.add_node(node_ptr);
exec.spin();
exec.remove_node(node_ptr);
}
void
rclcpp::spin(rclcpp::node::Node::SharedPtr node_ptr)
{
rclcpp::spin(node_ptr->get_node_base_interface());
}

View File

@@ -16,6 +16,7 @@
#include <cstdio>
#include <exception>
#include <memory>
#include <string>
#include <vector>
@@ -114,17 +115,17 @@ GraphListener::run_loop()
rcl_ret_t ret;
{
// This "barrier" lock ensures that other functions can acquire the
// nodes_mutex_ after waking up rcl_wait.
std::lock_guard<std::mutex> nodes_barrier_lock(nodes_barrier_mutex_);
// node_graph_interfaces_mutex_ after waking up rcl_wait.
std::lock_guard<std::mutex> nodes_barrier_lock(node_graph_interfaces_barrier_mutex_);
// This is ownership is passed to nodes_lock in the next line.
nodes_mutex_.lock();
node_graph_interfaces_mutex_.lock();
}
// This lock is released when the loop continues or exits.
std::lock_guard<std::mutex> nodes_lock(nodes_mutex_, std::adopt_lock);
std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
// Resize the wait set if necessary.
if (wait_set_.size_of_guard_conditions < (nodes_.size() + 2)) {
ret = rcl_wait_set_resize_guard_conditions(&wait_set_, nodes_.size() + 2);
if (wait_set_.size_of_guard_conditions < (node_graph_interfaces_.size() + 2)) {
ret = rcl_wait_set_resize_guard_conditions(&wait_set_, node_graph_interfaces_.size() + 2);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to resize wait set");
}
@@ -145,13 +146,13 @@ GraphListener::run_loop()
throw_from_rcl_error(ret, "failed to add shutdown guard condition to wait set");
}
// Put graph guard conditions for each node into the wait set.
for (const auto node_ptr : nodes_) {
for (const auto node_ptr : node_graph_interfaces_) {
// Only wait on graph changes if some user of the node is watching.
if (node_ptr->count_graph_users() == 0) {
continue;
}
// Add the graph guard condition for the node to the wait set.
auto graph_gc = rcl_node_get_graph_guard_condition(node_ptr->get_rcl_node_handle());
auto graph_gc = node_ptr->get_graph_guard_condition();
if (!graph_gc) {
throw_from_rcl_error(RCL_RET_ERROR, "failed to get graph guard condition");
}
@@ -178,8 +179,8 @@ GraphListener::run_loop()
}
}
// Notify nodes who's guard conditions are set (triggered).
for (const auto node_ptr : nodes_) {
auto graph_gc = rcl_node_get_graph_guard_condition(node_ptr->get_rcl_node_handle());
for (const auto node_ptr : node_graph_interfaces_) {
auto graph_gc = node_ptr->get_graph_guard_condition();
if (!graph_gc) {
throw_from_rcl_error(RCL_RET_ERROR, "failed to get graph guard condition");
}
@@ -207,25 +208,27 @@ interrupt_(rcl_guard_condition_t * interrupt_guard_condition)
static void
acquire_nodes_lock_(
std::mutex * nodes_barrier_mutex,
std::mutex * nodes_mutex,
std::mutex * node_graph_interfaces_barrier_mutex,
std::mutex * node_graph_interfaces_mutex,
rcl_guard_condition_t * interrupt_guard_condition)
{
{
// Acquire this lock to prevent the run loop from re-locking the
// nodes_mutext_ after being woken up.
std::lock_guard<std::mutex> nodes_barrier_lock(*nodes_barrier_mutex);
std::lock_guard<std::mutex> nodes_barrier_lock(*node_graph_interfaces_barrier_mutex);
// Trigger the interrupt guard condition to wake up rcl_wait.
interrupt_(interrupt_guard_condition);
nodes_mutex->lock();
node_graph_interfaces_mutex->lock();
}
}
static bool
has_node_(std::vector<rclcpp::node::Node *> * nodes, rclcpp::node::Node * node)
has_node_(
std::vector<rclcpp::node_interfaces::NodeGraphInterface *> * node_graph_interfaces,
rclcpp::node_interfaces::NodeGraphInterface * node_graph)
{
for (const auto node_ptr : (*nodes)) {
if (node == node_ptr) {
for (const auto node_ptr : (*node_graph_interfaces)) {
if (node_graph == node_ptr) {
return true;
}
}
@@ -233,23 +236,26 @@ has_node_(std::vector<rclcpp::node::Node *> * nodes, rclcpp::node::Node * node)
}
bool
GraphListener::has_node(rclcpp::node::Node * node)
GraphListener::has_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph)
{
if (!node) {
if (!node_graph) {
return false;
}
// Acquire the nodes mutex using the barrier to prevent the run loop from
// re-locking the nodes mutex after being interrupted.
acquire_nodes_lock_(&nodes_barrier_mutex_, &nodes_mutex_, &interrupt_guard_condition_);
// Store the now acquired nodes_mutex_ in the scoped lock using adopt_lock.
std::lock_guard<std::mutex> nodes_lock(nodes_mutex_, std::adopt_lock);
return has_node_(&nodes_, node);
acquire_nodes_lock_(
&node_graph_interfaces_barrier_mutex_,
&node_graph_interfaces_mutex_,
&interrupt_guard_condition_);
// Store the now acquired node_graph_interfaces_mutex_ in the scoped lock using adopt_lock.
std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
return has_node_(&node_graph_interfaces_, node_graph);
}
void
GraphListener::add_node(rclcpp::node::Node * node)
GraphListener::add_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph)
{
if (!node) {
if (!node_graph) {
throw std::invalid_argument("node is nullptr");
}
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
@@ -259,25 +265,30 @@ GraphListener::add_node(rclcpp::node::Node * node)
// Acquire the nodes mutex using the barrier to prevent the run loop from
// re-locking the nodes mutex after being interrupted.
acquire_nodes_lock_(&nodes_barrier_mutex_, &nodes_mutex_, &interrupt_guard_condition_);
// Store the now acquired nodes_mutex_ in the scoped lock using adopt_lock.
std::lock_guard<std::mutex> nodes_lock(nodes_mutex_, std::adopt_lock);
if (has_node_(&nodes_, node)) {
acquire_nodes_lock_(
&node_graph_interfaces_barrier_mutex_,
&node_graph_interfaces_mutex_,
&interrupt_guard_condition_);
// Store the now acquired node_graph_interfaces_mutex_ in the scoped lock using adopt_lock.
std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
if (has_node_(&node_graph_interfaces_, node_graph)) {
throw NodeAlreadyAddedError();
}
nodes_.push_back(node);
node_graph_interfaces_.push_back(node_graph);
// The run loop has already been interrupted by acquire_nodes_lock_() and
// will evaluate the new node when nodes_lock releases the nodes_mutex_.
// will evaluate the new node when nodes_lock releases the node_graph_interfaces_mutex_.
}
static void
remove_node_(std::vector<rclcpp::node::Node *> * nodes, rclcpp::node::Node * node)
remove_node_(
std::vector<rclcpp::node_interfaces::NodeGraphInterface *> * node_graph_interfaces,
rclcpp::node_interfaces::NodeGraphInterface * node_graph)
{
// Remove the node if it is found.
for (auto it = nodes->begin(); it != nodes->end(); ++it) {
if (node == *it) {
for (auto it = node_graph_interfaces->begin(); it != node_graph_interfaces->end(); ++it) {
if (node_graph == *it) {
// Found the node, remove it.
nodes->erase(it);
node_graph_interfaces->erase(it);
// Now trigger the interrupt guard condition to make sure
return;
}
@@ -287,23 +298,26 @@ remove_node_(std::vector<rclcpp::node::Node *> * nodes, rclcpp::node::Node * nod
}
void
GraphListener::remove_node(rclcpp::node::Node * node)
GraphListener::remove_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph)
{
if (!node) {
if (!node_graph) {
throw std::invalid_argument("node is nullptr");
}
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
if (is_shutdown()) {
// If shutdown, then the run loop has been joined, so we can remove them directly.
return remove_node_(&nodes_, node);
return remove_node_(&node_graph_interfaces_, node_graph);
}
// Otherwise, first interrupt and lock against the run loop to safely remove the node.
// Acquire the nodes mutex using the barrier to prevent the run loop from
// re-locking the nodes mutex after being interrupted.
acquire_nodes_lock_(&nodes_barrier_mutex_, &nodes_mutex_, &interrupt_guard_condition_);
// Store the now acquired nodes_mutex_ in the scoped lock using adopt_lock.
std::lock_guard<std::mutex> nodes_lock(nodes_mutex_, std::adopt_lock);
remove_node_(&nodes_, node);
acquire_nodes_lock_(
&node_graph_interfaces_barrier_mutex_,
&node_graph_interfaces_mutex_,
&interrupt_guard_condition_);
// Store the now acquired node_graph_interfaces_mutex_ in the scoped lock using adopt_lock.
std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
remove_node_(&node_graph_interfaces_, node_graph);
}
void

View File

@@ -14,6 +14,8 @@
#include "rclcpp/memory_strategies.hpp"
#include <memory>
#include "rclcpp/strategies/allocator_memory_strategy.hpp"
using rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy;

View File

@@ -60,8 +60,9 @@ MemoryStrategy::get_service_by_handle(const rcl_service_t * service_handle,
if (!group) {
continue;
}
for (auto & service : group->get_service_ptrs()) {
if (service->get_service_handle() == service_handle) {
for (auto & weak_service : group->get_service_ptrs()) {
auto service = weak_service.lock();
if (service && service->get_service_handle() == service_handle) {
return service;
}
}
@@ -95,7 +96,7 @@ MemoryStrategy::get_client_by_handle(const rcl_client_t * client_handle,
return nullptr;
}
rclcpp::node::Node::SharedPtr
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
MemoryStrategy::get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group,
const WeakNodeVector & weak_nodes)
{
@@ -158,8 +159,9 @@ MemoryStrategy::get_group_by_service(
if (!group) {
continue;
}
for (auto & serv : group->get_service_ptrs()) {
if (serv == service) {
for (auto & weak_serv : group->get_service_ptrs()) {
auto serv = weak_serv.lock();
if (serv && serv == service) {
return group;
}
}

View File

@@ -15,14 +15,20 @@
#include <algorithm>
#include <limits>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "rcl_interfaces/srv/list_parameters.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/graph_listener.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/node_interfaces/node_base.hpp"
#include "rclcpp/node_interfaces/node_graph.hpp"
#include "rclcpp/node_interfaces/node_parameters.hpp"
#include "rclcpp/node_interfaces/node_services.hpp"
#include "rclcpp/node_interfaces/node_timers.hpp"
#include "rclcpp/node_interfaces/node_topics.hpp"
using rclcpp::node::Node;
using rclcpp::exceptions::throw_from_rcl_error;
@@ -38,443 +44,123 @@ Node::Node(
const std::string & node_name,
rclcpp::context::Context::SharedPtr context,
bool use_intra_process_comms)
: name_(node_name), context_(context),
number_of_subscriptions_(0), number_of_timers_(0), number_of_services_(0),
use_intra_process_comms_(use_intra_process_comms), notify_guard_condition_is_valid_(false),
graph_listener_(context->get_sub_context<rclcpp::graph_listener::GraphListener>()),
should_add_to_graph_listener_(true), graph_users_count_(0)
: node_base_(new rclcpp::node_interfaces::NodeBase(node_name, context)),
node_graph_(new rclcpp::node_interfaces::NodeGraph(node_base_.get())),
node_timers_(new rclcpp::node_interfaces::NodeTimers(node_base_.get())),
node_topics_(new rclcpp::node_interfaces::NodeTopics(node_base_.get())),
node_services_(new rclcpp::node_interfaces::NodeServices(node_base_.get())),
node_parameters_(new rclcpp::node_interfaces::NodeParameters(
node_topics_.get(),
use_intra_process_comms
)),
use_intra_process_comms_(use_intra_process_comms)
{
rcl_guard_condition_options_t guard_condition_options = rcl_guard_condition_get_default_options();
rcl_ret_t ret = rcl_guard_condition_init(&notify_guard_condition_, guard_condition_options);
if (ret != RCL_RET_OK) {
throw_from_rcl_error(ret, "failed to create interrupt guard condition");
}
has_executor.store(false);
size_t domain_id = 0;
char * ros_domain_id = nullptr;
const char * env_var = "ROS_DOMAIN_ID";
#ifndef _WIN32
ros_domain_id = getenv(env_var);
#else
size_t ros_domain_id_size;
_dupenv_s(&ros_domain_id, &ros_domain_id_size, env_var);
#endif
if (ros_domain_id) {
uint32_t number = strtoul(ros_domain_id, NULL, 0);
if (number == (std::numeric_limits<uint32_t>::max)()) {
// Finalize the interrupt guard condition.
if (rcl_guard_condition_fini(&notify_guard_condition_) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy guard condition: %s\n", rcl_get_error_string_safe());
}
throw std::runtime_error("failed to interpret ROS_DOMAIN_ID as integral number");
}
domain_id = static_cast<size_t>(number);
#ifdef _WIN32
free(ros_domain_id);
#endif
}
rcl_node_t * rcl_node = new rcl_node_t(rcl_get_zero_initialized_node());
node_handle_.reset(rcl_node, [](rcl_node_t * node) {
if (rcl_node_fini(node) != RCL_RET_OK) {
fprintf(
stderr, "Error in destruction of rmw node handle: %s\n", rcl_get_error_string_safe());
}
delete node;
});
rcl_node_options_t options = rcl_node_get_default_options();
// TODO(jacquelinekay): Allocator options
options.domain_id = domain_id;
ret = rcl_node_init(node_handle_.get(), name_.c_str(), &options);
if (ret != RCL_RET_OK) {
// Finalize the interrupt guard condition.
if (rcl_guard_condition_fini(&notify_guard_condition_) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy guard condition: %s\n", rcl_get_error_string_safe());
}
throw_from_rcl_error(ret, "failed to initialize rcl node");
}
using rclcpp::callback_group::CallbackGroupType;
default_callback_group_ = create_callback_group(
CallbackGroupType::MutuallyExclusive);
events_publisher_ = create_publisher<rcl_interfaces::msg::ParameterEvent>(
"parameter_events", rmw_qos_profile_parameter_events);
notify_guard_condition_is_valid_ = true;
}
Node::~Node()
{
// Remove self from graph listener.
// Exchange with false to prevent others from trying to add this node to the
// graph listener after checking that it was not here.
if (!should_add_to_graph_listener_.exchange(false)) {
// If it was already false, then it needs to now be removed.
graph_listener_->remove_node(this);
}
// Finalize the interrupt guard condition after removing self from graph listener.
{
std::lock_guard<std::mutex> notify_guard_condition_lock(notify_guard_condition_mutex_);
notify_guard_condition_is_valid_ = false;
if (rcl_guard_condition_fini(&notify_guard_condition_) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy guard condition: %s\n", rcl_get_error_string_safe());
}
}
}
{}
const std::string &
const char *
Node::get_name() const
{
return name_;
return node_base_->get_name();
}
rclcpp::callback_group::CallbackGroup::SharedPtr
Node::create_callback_group(
rclcpp::callback_group::CallbackGroupType group_type)
{
using rclcpp::callback_group::CallbackGroup;
using rclcpp::callback_group::CallbackGroupType;
auto group = CallbackGroup::SharedPtr(new CallbackGroup(group_type));
callback_groups_.push_back(group);
return group;
return node_base_->create_callback_group(group_type);
}
bool
Node::group_in_node(rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
bool group_belongs_to_this_node = false;
for (auto & weak_group : this->callback_groups_) {
auto cur_group = weak_group.lock();
if (cur_group && (cur_group == group)) {
group_belongs_to_this_node = true;
}
}
return group_belongs_to_this_node;
return node_base_->callback_group_in_node(group);
}
// TODO(wjwwood): reenable this once I figure out why the demo doesn't build with it.
// rclcpp::timer::WallTimer::SharedPtr
// Node::create_wall_timer(
// std::chrono::duration<long double, std::nano> period,
// rclcpp::timer::CallbackType callback,
// rclcpp::callback_group::CallbackGroup::SharedPtr group)
// {
// return create_wall_timer(
// std::chrono::duration_cast<std::chrono::nanoseconds>(period),
// callback,
// group);
// }
std::vector<rcl_interfaces::msg::SetParametersResult>
Node::set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
std::vector<rcl_interfaces::msg::SetParametersResult> results;
for (auto p : parameters) {
auto result = set_parameters_atomically({{p}});
results.push_back(result);
}
return results;
return node_parameters_->set_parameters(parameters);
}
rcl_interfaces::msg::SetParametersResult
Node::set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
std::lock_guard<std::mutex> lock(mutex_);
std::map<std::string, rclcpp::parameter::ParameterVariant> tmp_map;
auto parameter_event = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
for (auto p : parameters) {
if (parameters_.find(p.get_name()) == parameters_.end()) {
if (p.get_type() != rclcpp::parameter::ParameterType::PARAMETER_NOT_SET) {
parameter_event->new_parameters.push_back(p.to_parameter());
}
} else if (p.get_type() != rclcpp::parameter::ParameterType::PARAMETER_NOT_SET) {
parameter_event->changed_parameters.push_back(p.to_parameter());
} else {
parameter_event->deleted_parameters.push_back(p.to_parameter());
}
tmp_map[p.get_name()] = p;
}
// std::map::insert will not overwrite elements, so we'll keep the new
// ones and add only those that already exist in the Node's internal map
tmp_map.insert(parameters_.begin(), parameters_.end());
std::swap(tmp_map, parameters_);
// TODO(jacquelinekay): handle parameter constraints
rcl_interfaces::msg::SetParametersResult result;
result.successful = true;
events_publisher_->publish(parameter_event);
return result;
return node_parameters_->set_parameters_atomically(parameters);
}
std::vector<rclcpp::parameter::ParameterVariant>
Node::get_parameters(
const std::vector<std::string> & names) const
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<rclcpp::parameter::ParameterVariant> results;
return node_parameters_->get_parameters(names);
}
for (auto & name : names) {
if (std::any_of(parameters_.cbegin(), parameters_.cend(),
[&name](const std::pair<std::string, rclcpp::parameter::ParameterVariant> & kv) {
return name == kv.first;
}))
{
results.push_back(parameters_.at(name));
}
}
return results;
rclcpp::parameter::ParameterVariant
Node::get_parameter(const std::string & name) const
{
return node_parameters_->get_parameter(name);
}
bool Node::get_parameter(const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const
{
return node_parameters_->get_parameter(name, parameter);
}
std::vector<rcl_interfaces::msg::ParameterDescriptor>
Node::describe_parameters(
const std::vector<std::string> & names) const
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<rcl_interfaces::msg::ParameterDescriptor> results;
for (auto & kv : parameters_) {
if (std::any_of(names.cbegin(), names.cend(), [&kv](const std::string & name) {
return name == kv.first;
}))
{
rcl_interfaces::msg::ParameterDescriptor parameter_descriptor;
parameter_descriptor.name = kv.first;
parameter_descriptor.type = kv.second.get_type();
results.push_back(parameter_descriptor);
}
}
return results;
return node_parameters_->describe_parameters(names);
}
std::vector<uint8_t>
Node::get_parameter_types(
const std::vector<std::string> & names) const
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<uint8_t> results;
for (auto & kv : parameters_) {
if (std::any_of(names.cbegin(), names.cend(), [&kv](const std::string & name) {
return name == kv.first;
}))
{
results.push_back(kv.second.get_type());
} else {
results.push_back(rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET);
}
}
return results;
return node_parameters_->get_parameter_types(names);
}
rcl_interfaces::msg::ListParametersResult
Node::list_parameters(
const std::vector<std::string> & prefixes, uint64_t depth) const
{
std::lock_guard<std::mutex> lock(mutex_);
rcl_interfaces::msg::ListParametersResult result;
// TODO(esteve): define parameter separator, use "." for now
for (auto & kv : parameters_) {
if (((prefixes.size() == 0) &&
((depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) ||
(static_cast<uint64_t>(std::count(kv.first.begin(), kv.first.end(), '.')) < depth))) ||
(std::any_of(prefixes.cbegin(), prefixes.cend(), [&kv, &depth](const std::string & prefix) {
if (kv.first == prefix) {
return true;
} else if (kv.first.find(prefix + ".") == 0) {
size_t length = prefix.length();
std::string substr = kv.first.substr(length);
// Cast as unsigned integer to avoid warning
return (depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) ||
(static_cast<uint64_t>(std::count(substr.begin(), substr.end(), '.')) < depth);
}
return false;
})))
{
result.names.push_back(kv.first);
size_t last_separator = kv.first.find_last_of('.');
if (std::string::npos != last_separator) {
std::string prefix = kv.first.substr(0, last_separator);
if (std::find(result.prefixes.cbegin(), result.prefixes.cend(), prefix) ==
result.prefixes.cend())
{
result.prefixes.push_back(prefix);
}
}
}
}
return result;
return node_parameters_->list_parameters(prefixes, depth);
}
std::map<std::string, std::string>
Node::get_topic_names_and_types() const
{
rmw_topic_names_and_types_t topic_names_and_types;
topic_names_and_types.topic_count = 0;
topic_names_and_types.topic_names = nullptr;
topic_names_and_types.type_names = nullptr;
auto ret = rmw_get_topic_names_and_types(rcl_node_get_rmw_handle(node_handle_.get()),
&topic_names_and_types);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not get topic names and types: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
std::map<std::string, std::string> topics;
for (size_t i = 0; i < topic_names_and_types.topic_count; ++i) {
topics[topic_names_and_types.topic_names[i]] = topic_names_and_types.type_names[i];
}
ret = rmw_destroy_topic_names_and_types(&topic_names_and_types);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not destroy topic names and types: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
return topics;
return node_graph_->get_topic_names_and_types();
}
size_t
Node::count_publishers(const std::string & topic_name) const
{
size_t count;
auto ret = rmw_count_publishers(rcl_node_get_rmw_handle(node_handle_.get()),
topic_name.c_str(), &count);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not count publishers: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
return count;
return node_graph_->count_publishers(topic_name);
}
size_t
Node::count_subscribers(const std::string & topic_name) const
{
size_t count;
auto ret = rmw_count_subscribers(rcl_node_get_rmw_handle(node_handle_.get()),
topic_name.c_str(), &count);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not count subscribers: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
return count;
return node_graph_->count_subscribers(topic_name);
}
const Node::CallbackGroupWeakPtrList &
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
Node::get_callback_groups() const
{
return callback_groups_;
}
const rcl_guard_condition_t *
Node::get_notify_guard_condition() const
{
std::lock_guard<std::mutex> notify_guard_condition_lock(notify_guard_condition_mutex_);
if (!notify_guard_condition_is_valid_) {
return nullptr;
}
return &notify_guard_condition_;
}
const rcl_guard_condition_t *
Node::get_graph_guard_condition() const
{
return rcl_node_get_graph_guard_condition(node_handle_.get());
}
const rcl_node_t *
Node::get_rcl_node_handle() const
{
return node_handle_.get();
}
rcl_node_t *
Node::get_rcl_node_handle()
{
return node_handle_.get();
}
std::shared_ptr<rcl_node_t>
Node::get_shared_node_handle()
{
return node_handle_;
}
void
Node::notify_graph_change()
{
{
std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
bool bad_ptr_encountered = false;
for (auto & event_wptr : graph_events_) {
auto event_ptr = event_wptr.lock();
if (event_ptr) {
event_ptr->set();
} else {
bad_ptr_encountered = true;
}
}
if (bad_ptr_encountered) {
// remove invalid pointers with the erase-remove idiom
graph_events_.erase(
std::remove_if(
graph_events_.begin(),
graph_events_.end(),
[](const rclcpp::event::Event::WeakPtr & wptr) {
return wptr.expired();
}),
graph_events_.end());
// update graph_users_count_
graph_users_count_.store(graph_events_.size());
}
}
graph_cv_.notify_all();
{
std::lock_guard<std::mutex> notify_guard_condition_lock(notify_guard_condition_mutex_);
rcl_ret_t ret = rcl_trigger_guard_condition(&notify_guard_condition_);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to trigger notify guard condition");
}
}
}
void
Node::notify_shutdown()
{
// notify here anything that will not be woken up by ctrl-c or rclcpp::shutdown().
graph_cv_.notify_all();
return node_base_->get_callback_groups();
}
rclcpp::event::Event::SharedPtr
Node::get_graph_event()
{
auto event = rclcpp::event::Event::make_shared();
std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
// on first call, add node to graph_listener_
if (should_add_to_graph_listener_.exchange(false)) {
graph_listener_->add_node(this);
graph_listener_->start_if_not_started();
}
graph_events_.push_back(event);
graph_users_count_++;
return event;
return node_graph_->get_graph_event();
}
void
@@ -482,30 +168,41 @@ Node::wait_for_graph_change(
rclcpp::event::Event::SharedPtr event,
std::chrono::nanoseconds timeout)
{
if (!event) {
throw InvalidEventError();
}
{
std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
bool event_in_graph_events = false;
for (const auto & event_wptr : graph_events_) {
if (event == event_wptr.lock()) {
event_in_graph_events = true;
break;
}
}
if (!event_in_graph_events) {
throw EventNotRegisteredError();
}
}
std::unique_lock<std::mutex> graph_lock(graph_mutex_);
graph_cv_.wait_for(graph_lock, timeout, [&event]() {
return event->check() || !rclcpp::utilities::ok();
});
node_graph_->wait_for_graph_change(event, timeout);
}
size_t
Node::count_graph_users()
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
Node::get_node_base_interface()
{
return graph_users_count_.load();
return node_base_;
}
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr
Node::get_node_graph_interface()
{
return node_graph_;
}
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr
Node::get_node_timers_interface()
{
return node_timers_;
}
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr
Node::get_node_topics_interface()
{
return node_topics_;
}
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr
Node::get_node_services_interface()
{
return node_services_;
}
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr
Node::get_node_parameters_interface()
{
return node_parameters_;
}

View File

@@ -0,0 +1,215 @@
// Copyright 2016 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 <string>
#include <limits>
#include <memory>
#include <vector>
#include "rclcpp/node_interfaces/node_base.hpp"
#include "rclcpp/exceptions.hpp"
using rclcpp::exceptions::throw_from_rcl_error;
using rclcpp::node_interfaces::NodeBase;
NodeBase::NodeBase(
const std::string & node_name,
rclcpp::context::Context::SharedPtr context)
: context_(context),
node_handle_(nullptr),
default_callback_group_(nullptr),
associated_with_executor_(false),
notify_guard_condition_is_valid_(false)
{
// Setup the guard condition that is notified when changes occur in the graph.
rcl_guard_condition_options_t guard_condition_options = rcl_guard_condition_get_default_options();
rcl_ret_t ret = rcl_guard_condition_init(&notify_guard_condition_, guard_condition_options);
if (ret != RCL_RET_OK) {
throw_from_rcl_error(ret, "failed to create interrupt guard condition");
}
// Setup a safe exit lamda to clean up the guard condition in case of an error here.
auto finalize_notify_guard_condition = [this]() {
// Finalize the interrupt guard condition.
if (rcl_guard_condition_fini(&notify_guard_condition_) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy guard condition: %s\n", rcl_get_error_string_safe());
}
};
// Determine the domain id based on the options and the ROS_DOMAIN_ID env variable.
size_t domain_id = 0;
char * ros_domain_id = nullptr;
const char * env_var = "ROS_DOMAIN_ID";
#ifndef _WIN32
ros_domain_id = getenv(env_var);
#else
size_t ros_domain_id_size;
_dupenv_s(&ros_domain_id, &ros_domain_id_size, env_var);
#endif
if (ros_domain_id) {
uint32_t number = strtoul(ros_domain_id, NULL, 0);
if (number == (std::numeric_limits<uint32_t>::max)()) {
// Finalize the interrupt guard condition.
finalize_notify_guard_condition();
#ifdef _WIN32
// free the ros_domain_id before throwing, if getenv was used on Windows
free(ros_domain_id);
#endif
throw std::runtime_error("failed to interpret ROS_DOMAIN_ID as integral number");
}
domain_id = static_cast<size_t>(number);
#ifdef _WIN32
free(ros_domain_id);
#endif
}
// Create the rcl node and store it in a shared_ptr with a custom destructor.
rcl_node_t * rcl_node = new rcl_node_t(rcl_get_zero_initialized_node());
rcl_node_options_t options = rcl_node_get_default_options();
// TODO(wjwwood): pass the Allocator to the options
options.domain_id = domain_id;
ret = rcl_node_init(rcl_node, node_name.c_str(), &options);
if (ret != RCL_RET_OK) {
// Finalize the interrupt guard condition.
finalize_notify_guard_condition();
throw_from_rcl_error(ret, "failed to initialize rcl node");
}
node_handle_.reset(rcl_node, [](rcl_node_t * node) -> void {
if (rcl_node_fini(node) != RCL_RET_OK) {
fprintf(
stderr, "Error in destruction of rcl node handle: %s\n", rcl_get_error_string_safe());
}
delete node;
});
// Create the default callback group.
using rclcpp::callback_group::CallbackGroupType;
default_callback_group_ = create_callback_group(CallbackGroupType::MutuallyExclusive);
// Indicate the notify_guard_condition is now valid.
notify_guard_condition_is_valid_ = true;
}
NodeBase::~NodeBase()
{
// Finalize the interrupt guard condition after removing self from graph listener.
{
std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
notify_guard_condition_is_valid_ = false;
if (rcl_guard_condition_fini(&notify_guard_condition_) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy guard condition: %s\n", rcl_get_error_string_safe());
}
}
}
const char *
NodeBase::get_name() const
{
return rcl_node_get_name(node_handle_.get());
}
rclcpp::context::Context::SharedPtr
NodeBase::get_context()
{
return context_;
}
rcl_node_t *
NodeBase::get_rcl_node_handle()
{
return node_handle_.get();
}
const rcl_node_t *
NodeBase::get_rcl_node_handle() const
{
return node_handle_.get();
}
std::shared_ptr<rcl_node_t>
NodeBase::get_shared_rcl_node_handle()
{
return node_handle_;
}
std::shared_ptr<const rcl_node_t>
NodeBase::get_shared_rcl_node_handle() const
{
return node_handle_;
}
rclcpp::callback_group::CallbackGroup::SharedPtr
NodeBase::create_callback_group(rclcpp::callback_group::CallbackGroupType group_type)
{
using rclcpp::callback_group::CallbackGroup;
using rclcpp::callback_group::CallbackGroupType;
auto group = CallbackGroup::SharedPtr(new CallbackGroup(group_type));
callback_groups_.push_back(group);
return group;
}
rclcpp::callback_group::CallbackGroup::SharedPtr
NodeBase::get_default_callback_group()
{
return default_callback_group_;
}
bool
NodeBase::callback_group_in_node(rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
bool group_belongs_to_this_node = false;
for (auto & weak_group : this->callback_groups_) {
auto cur_group = weak_group.lock();
if (cur_group && (cur_group == group)) {
group_belongs_to_this_node = true;
}
}
return group_belongs_to_this_node;
}
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
NodeBase::get_callback_groups() const
{
return callback_groups_;
}
std::atomic_bool &
NodeBase::get_associated_with_executor_atomic()
{
return associated_with_executor_;
}
rcl_guard_condition_t *
NodeBase::get_notify_guard_condition()
{
std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
if (!notify_guard_condition_is_valid_) {
return nullptr;
}
return &notify_guard_condition_;
}
std::unique_lock<std::recursive_mutex>
NodeBase::acquire_notify_guard_condition_lock() const
{
return std::unique_lock<std::recursive_mutex>(notify_guard_condition_mutex_);
}

View File

@@ -0,0 +1,209 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/node_interfaces/node_graph.hpp"
#include <map>
#include <string>
#include "rcl/graph.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/event.hpp"
#include "rclcpp/graph_listener.hpp"
using rclcpp::node_interfaces::NodeGraph;
using rclcpp::exceptions::throw_from_rcl_error;
using rclcpp::graph_listener::GraphListener;
NodeGraph::NodeGraph(rclcpp::node_interfaces::NodeBaseInterface * node_base)
: node_base_(node_base),
graph_listener_(node_base->get_context()->get_sub_context<GraphListener>()),
should_add_to_graph_listener_(true),
graph_users_count_(0)
{}
NodeGraph::~NodeGraph()
{
// Remove self from graph listener.
// Exchange with false to prevent others from trying to add this node to the
// graph listener after checking that it was not here.
if (!should_add_to_graph_listener_.exchange(false)) {
// If it was already false, then it needs to now be removed.
graph_listener_->remove_node(this);
}
}
std::map<std::string, std::string>
NodeGraph::get_topic_names_and_types() const
{
rcl_topic_names_and_types_t topic_names_and_types =
rcl_get_zero_initialized_topic_names_and_types();
auto ret = rcl_get_topic_names_and_types(node_base_->get_rcl_node_handle(),
&topic_names_and_types);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not get topic names and types: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
std::map<std::string, std::string> topics;
for (size_t i = 0; i < topic_names_and_types.topic_count; ++i) {
topics[topic_names_and_types.topic_names[i]] = topic_names_and_types.type_names[i];
}
ret = rmw_destroy_topic_names_and_types(&topic_names_and_types);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not destroy topic names and types: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
return topics;
}
size_t
NodeGraph::count_publishers(const std::string & topic_name) const
{
size_t count;
// TODO(wjwwood): use the rcl equivalent methods
auto ret = rmw_count_publishers(rcl_node_get_rmw_handle(node_base_->get_rcl_node_handle()),
topic_name.c_str(), &count);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not count publishers: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
return count;
}
size_t
NodeGraph::count_subscribers(const std::string & topic_name) const
{
size_t count;
// TODO(wjwwood): use the rcl equivalent methods
auto ret = rmw_count_subscribers(rcl_node_get_rmw_handle(node_base_->get_rcl_node_handle()),
topic_name.c_str(), &count);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not count subscribers: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
return count;
}
const rcl_guard_condition_t *
NodeGraph::get_graph_guard_condition() const
{
return rcl_node_get_graph_guard_condition(node_base_->get_rcl_node_handle());
}
void
NodeGraph::notify_graph_change()
{
{
std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
bool bad_ptr_encountered = false;
for (auto & event_wptr : graph_events_) {
auto event_ptr = event_wptr.lock();
if (event_ptr) {
event_ptr->set();
} else {
bad_ptr_encountered = true;
}
}
if (bad_ptr_encountered) {
// remove invalid pointers with the erase-remove idiom
graph_events_.erase(
std::remove_if(
graph_events_.begin(),
graph_events_.end(),
[](const rclcpp::event::Event::WeakPtr & wptr) {
return wptr.expired();
}),
graph_events_.end());
// update graph_users_count_
graph_users_count_.store(graph_events_.size());
}
}
graph_cv_.notify_all();
{
auto notify_condition_lock = node_base_->acquire_notify_guard_condition_lock();
rcl_ret_t ret = rcl_trigger_guard_condition(node_base_->get_notify_guard_condition());
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to trigger notify guard condition");
}
}
}
void
NodeGraph::notify_shutdown()
{
// notify here anything that will not be woken up by ctrl-c or rclcpp::shutdown().
graph_cv_.notify_all();
}
rclcpp::event::Event::SharedPtr
NodeGraph::get_graph_event()
{
auto event = rclcpp::event::Event::make_shared();
std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
// on first call, add node to graph_listener_
if (should_add_to_graph_listener_.exchange(false)) {
graph_listener_->add_node(this);
graph_listener_->start_if_not_started();
}
graph_events_.push_back(event);
graph_users_count_++;
return event;
}
void
NodeGraph::wait_for_graph_change(
rclcpp::event::Event::SharedPtr event,
std::chrono::nanoseconds timeout)
{
using rclcpp::exceptions::InvalidEventError;
using rclcpp::exceptions::EventNotRegisteredError;
if (!event) {
throw InvalidEventError();
}
{
std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
bool event_in_graph_events = false;
for (const auto & event_wptr : graph_events_) {
if (event == event_wptr.lock()) {
event_in_graph_events = true;
break;
}
}
if (!event_in_graph_events) {
throw EventNotRegisteredError();
}
}
std::unique_lock<std::mutex> graph_lock(graph_mutex_);
graph_cv_.wait_for(graph_lock, timeout, [&event]() {
return event->check() || !rclcpp::utilities::ok();
});
}
size_t
NodeGraph::count_graph_users()
{
return graph_users_count_.load();
}

View File

@@ -0,0 +1,230 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/node_interfaces/node_parameters.hpp"
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "rcl_interfaces/srv/list_parameters.hpp"
#include "rclcpp/create_publisher.hpp"
#include "rmw/qos_profiles.h"
using rclcpp::node_interfaces::NodeParameters;
NodeParameters::NodeParameters(
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
bool use_intra_process)
: node_topics_(node_topics)
{
using MessageT = rcl_interfaces::msg::ParameterEvent;
using PublisherT = rclcpp::publisher::Publisher<MessageT>;
using AllocatorT = std::allocator<void>;
// TODO(wjwwood): expose this allocator through the Parameter interface.
auto allocator = std::make_shared<AllocatorT>();
events_publisher_ = rclcpp::create_publisher<MessageT, AllocatorT, PublisherT>(
node_topics_,
"parameter_events",
rmw_qos_profile_parameter_events,
use_intra_process,
allocator);
}
NodeParameters::~NodeParameters()
{}
std::vector<rcl_interfaces::msg::SetParametersResult>
NodeParameters::set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
std::vector<rcl_interfaces::msg::SetParametersResult> results;
for (auto p : parameters) {
auto result = set_parameters_atomically({{p}});
results.push_back(result);
}
return results;
}
rcl_interfaces::msg::SetParametersResult
NodeParameters::set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
std::lock_guard<std::mutex> lock(mutex_);
std::map<std::string, rclcpp::parameter::ParameterVariant> tmp_map;
auto parameter_event = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
// TODO(jacquelinekay): handle parameter constraints
rcl_interfaces::msg::SetParametersResult result;
if (parameters_callback_) {
result = parameters_callback_(parameters);
} else {
result.successful = true;
}
if (!result.successful) {
return result;
}
for (auto p : parameters) {
if (parameters_.find(p.get_name()) == parameters_.end()) {
if (p.get_type() != rclcpp::parameter::ParameterType::PARAMETER_NOT_SET) {
parameter_event->new_parameters.push_back(p.to_parameter());
}
} else if (p.get_type() != rclcpp::parameter::ParameterType::PARAMETER_NOT_SET) {
parameter_event->changed_parameters.push_back(p.to_parameter());
} else {
parameter_event->deleted_parameters.push_back(p.to_parameter());
}
tmp_map[p.get_name()] = p;
}
// std::map::insert will not overwrite elements, so we'll keep the new
// ones and add only those that already exist in the Node's internal map
tmp_map.insert(parameters_.begin(), parameters_.end());
std::swap(tmp_map, parameters_);
events_publisher_->publish(parameter_event);
return result;
}
std::vector<rclcpp::parameter::ParameterVariant>
NodeParameters::get_parameters(const std::vector<std::string> & names) const
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<rclcpp::parameter::ParameterVariant> results;
for (auto & name : names) {
if (std::any_of(parameters_.cbegin(), parameters_.cend(),
[&name](const std::pair<std::string, rclcpp::parameter::ParameterVariant> & kv) {
return name == kv.first;
}))
{
results.push_back(parameters_.at(name));
}
}
return results;
}
rclcpp::parameter::ParameterVariant
NodeParameters::get_parameter(const std::string & name) const
{
rclcpp::parameter::ParameterVariant parameter;
if (get_parameter(name, parameter)) {
return parameter;
} else {
throw std::out_of_range("Parameter '" + name + "' not set");
}
}
bool
NodeParameters::get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const
{
std::lock_guard<std::mutex> lock(mutex_);
if (parameters_.count(name)) {
parameter = parameters_.at(name);
return true;
} else {
return false;
}
}
std::vector<rcl_interfaces::msg::ParameterDescriptor>
NodeParameters::describe_parameters(const std::vector<std::string> & names) const
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<rcl_interfaces::msg::ParameterDescriptor> results;
for (auto & kv : parameters_) {
if (std::any_of(names.cbegin(), names.cend(), [&kv](const std::string & name) {
return name == kv.first;
}))
{
rcl_interfaces::msg::ParameterDescriptor parameter_descriptor;
parameter_descriptor.name = kv.first;
parameter_descriptor.type = kv.second.get_type();
results.push_back(parameter_descriptor);
}
}
return results;
}
std::vector<uint8_t>
NodeParameters::get_parameter_types(const std::vector<std::string> & names) const
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<uint8_t> results;
for (auto & kv : parameters_) {
if (std::any_of(names.cbegin(), names.cend(), [&kv](const std::string & name) {
return name == kv.first;
}))
{
results.push_back(kv.second.get_type());
} else {
results.push_back(rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET);
}
}
return results;
}
rcl_interfaces::msg::ListParametersResult
NodeParameters::list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const
{
std::lock_guard<std::mutex> lock(mutex_);
rcl_interfaces::msg::ListParametersResult result;
// TODO(esteve): define parameter separator, use "." for now
for (auto & kv : parameters_) {
if (((prefixes.size() == 0) &&
((depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) ||
(static_cast<uint64_t>(std::count(kv.first.begin(), kv.first.end(), '.')) < depth))) ||
(std::any_of(prefixes.cbegin(), prefixes.cend(), [&kv, &depth](const std::string & prefix) {
if (kv.first == prefix) {
return true;
} else if (kv.first.find(prefix + ".") == 0) {
size_t length = prefix.length();
std::string substr = kv.first.substr(length);
// Cast as unsigned integer to avoid warning
return (depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) ||
(static_cast<uint64_t>(std::count(substr.begin(), substr.end(), '.')) < depth);
}
return false;
})))
{
result.names.push_back(kv.first);
size_t last_separator = kv.first.find_last_of('.');
if (std::string::npos != last_separator) {
std::string prefix = kv.first.substr(0, last_separator);
if (std::find(result.prefixes.cbegin(), result.prefixes.cend(), prefix) ==
result.prefixes.cend())
{
result.prefixes.push_back(prefix);
}
}
}
}
return result;
}
void
NodeParameters::register_param_change_callback(ParametersCallbackFunction callback)
{
parameters_callback_ = callback;
}

View File

@@ -0,0 +1,78 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/node_interfaces/node_services.hpp"
#include <string>
using rclcpp::node_interfaces::NodeServices;
NodeServices::NodeServices(rclcpp::node_interfaces::NodeBaseInterface * node_base)
: node_base_(node_base)
{}
NodeServices::~NodeServices()
{}
void
NodeServices::add_service(
rclcpp::service::ServiceBase::SharedPtr service_base_ptr,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
if (group) {
if (!node_base_->callback_group_in_node(group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create service, group not in node.");
}
group->add_service(service_base_ptr);
} else {
node_base_->get_default_callback_group()->add_service(service_base_ptr);
}
// Notify the executor that a new service was created using the parent Node.
{
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string("Failed to notify waitset on service creation: ") + rmw_get_error_string()
);
}
}
}
void
NodeServices::add_client(
rclcpp::client::ClientBase::SharedPtr client_base_ptr,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
if (group) {
if (!node_base_->callback_group_in_node(group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create client, group not in node.");
}
group->add_client(client_base_ptr);
} else {
node_base_->get_default_callback_group()->add_client(client_base_ptr);
}
// Notify the executor that a new client was created using the parent Node.
{
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string("Failed to notify waitset on client creation: ") + rmw_get_error_string()
);
}
}
}

View File

@@ -0,0 +1,47 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/node_interfaces/node_timers.hpp"
#include <string>
using rclcpp::node_interfaces::NodeTimers;
NodeTimers::NodeTimers(rclcpp::node_interfaces::NodeBaseInterface * node_base)
: node_base_(node_base)
{}
NodeTimers::~NodeTimers()
{}
void
NodeTimers::add_timer(
rclcpp::timer::TimerBase::SharedPtr timer,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group)
{
if (callback_group) {
if (!node_base_->callback_group_in_node(callback_group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create timer, group not in node.");
}
callback_group->add_timer(timer);
} else {
node_base_->get_default_callback_group()->add_timer(timer);
}
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on timer creation: ") + rmw_get_error_string());
}
}

View File

@@ -0,0 +1,131 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/node_interfaces/node_topics.hpp"
#include <string>
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/exceptions.hpp"
using rclcpp::exceptions::throw_from_rcl_error;
using rclcpp::node_interfaces::NodeTopics;
NodeTopics::NodeTopics(rclcpp::node_interfaces::NodeBaseInterface * node_base)
: node_base_(node_base)
{}
NodeTopics::~NodeTopics()
{}
rclcpp::publisher::PublisherBase::SharedPtr
NodeTopics::create_publisher(
const std::string & topic_name,
const rclcpp::PublisherFactory & publisher_factory,
rcl_publisher_options_t & publisher_options,
bool use_intra_process)
{
// Create the MessageT specific Publisher using the factory, but store it as PublisherBase.
auto publisher = publisher_factory.create_typed_publisher(
node_base_, topic_name, publisher_options);
// Setup intra process publishing if requested.
if (use_intra_process) {
auto context = node_base_->get_context();
// Get the intra process manager instance for this context.
auto ipm = context->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
// Register the publisher with the intra process manager.
uint64_t intra_process_publisher_id =
publisher_factory.add_publisher_to_intra_process_manager(ipm.get(), publisher);
// Create a function to be called when publisher to do the intra process publish.
auto shared_publish_callback = publisher_factory.create_shared_publish_callback(ipm);
publisher->setup_intra_process(
intra_process_publisher_id,
shared_publish_callback,
publisher_options);
}
// Return the completed publisher.
return publisher;
}
void
NodeTopics::add_publisher(
rclcpp::publisher::PublisherBase::SharedPtr publisher)
{
// The publisher is not added to a callback group or anthing like that for now.
// It may be stored within the NodeTopics class or the NodeBase class in the future.
(void)publisher;
// Notify the executor that a new publisher was created using the parent Node.
{
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on publisher creation: ") + rmw_get_error_string());
}
}
}
rclcpp::subscription::SubscriptionBase::SharedPtr
NodeTopics::create_subscription(
const std::string & topic_name,
const rclcpp::SubscriptionFactory & subscription_factory,
rcl_subscription_options_t & subscription_options,
bool use_intra_process)
{
auto subscription = subscription_factory.create_typed_subscription(
node_base_, topic_name, subscription_options);
// Setup intra process publishing if requested.
if (use_intra_process) {
auto context = node_base_->get_context();
auto intra_process_manager =
context->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
subscription_factory.setup_intra_process(
intra_process_manager, subscription, subscription_options);
}
// Return the completed subscription.
return subscription;
}
void
NodeTopics::add_subscription(
rclcpp::subscription::SubscriptionBase::SharedPtr subscription,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group)
{
// Assign to a group.
if (callback_group) {
if (!node_base_->callback_group_in_node(callback_group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create subscription, callback group not in node.");
}
callback_group->add_subscription(subscription);
} else {
node_base_->get_default_callback_group()->add_subscription(subscription);
}
// Notify the executor that a new subscription was created using the parent Node.
{
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on subscription creation: ") + rmw_get_error_string()
);
}
}
}

View File

@@ -15,6 +15,7 @@
#include "rclcpp/parameter_client.hpp"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
@@ -250,7 +251,8 @@ std::vector<rclcpp::parameter::ParameterVariant>
SyncParametersClient::get_parameters(const std::vector<std::string> & parameter_names)
{
auto f = async_parameters_client_->get_parameters(parameter_names);
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();
@@ -273,7 +275,8 @@ SyncParametersClient::get_parameter_types(const std::vector<std::string> & param
{
auto f = async_parameters_client_->get_parameter_types(parameter_names);
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();
@@ -287,7 +290,8 @@ SyncParametersClient::set_parameters(
{
auto f = async_parameters_client_->set_parameters(parameters);
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();
@@ -301,7 +305,8 @@ SyncParametersClient::set_parameters_atomically(
{
auto f = async_parameters_client_->set_parameters_atomically(parameters);
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();
@@ -317,7 +322,8 @@ SyncParametersClient::list_parameters(
{
auto f = async_parameters_client_->list_parameters(parameter_prefixes, depth);
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();

View File

@@ -15,6 +15,8 @@
#include "rclcpp/parameter_service.hpp"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
using rclcpp::parameter_service::ParameterService;
@@ -27,7 +29,7 @@ ParameterService::ParameterService(
std::weak_ptr<rclcpp::node::Node> captured_node = node_;
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
get_parameters_service_ = node_->create_service<rcl_interfaces::srv::GetParameters>(
node_->get_name() + "__get_parameters",
std::string(node_->get_name()) + "__get_parameters",
[captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::GetParameters::Request> request,
@@ -46,7 +48,7 @@ ParameterService::ParameterService(
);
get_parameter_types_service_ = node_->create_service<rcl_interfaces::srv::GetParameterTypes>(
node_->get_name() + "__get_parameter_types",
std::string(node_->get_name()) + "__get_parameter_types",
[captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::GetParameterTypes::Request> request,
@@ -66,7 +68,7 @@ ParameterService::ParameterService(
);
set_parameters_service_ = node_->create_service<rcl_interfaces::srv::SetParameters>(
node_->get_name() + "__set_parameters",
std::string(node_->get_name()) + "__set_parameters",
[captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::SetParameters::Request> request,
@@ -88,7 +90,7 @@ ParameterService::ParameterService(
set_parameters_atomically_service_ =
node_->create_service<rcl_interfaces::srv::SetParametersAtomically>(
node_->get_name() + "__set_parameters_atomically",
std::string(node_->get_name()) + "__set_parameters_atomically",
[captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::SetParametersAtomically::Request> request,
@@ -112,7 +114,7 @@ ParameterService::ParameterService(
);
describe_parameters_service_ = node_->create_service<rcl_interfaces::srv::DescribeParameters>(
node_->get_name() + "__describe_parameters",
std::string(node_->get_name()) + "__describe_parameters",
[captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::DescribeParameters::Request> request,
@@ -129,7 +131,7 @@ ParameterService::ParameterService(
);
list_parameters_service_ = node_->create_service<rcl_interfaces::srv::ListParameters>(
node_->get_name() + "__list_parameters",
std::string(node_->get_name()) + "__list_parameters",
[captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::ListParameters::Request> request,

View File

@@ -29,33 +29,72 @@
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node.hpp"
using rclcpp::publisher::PublisherBase;
PublisherBase::PublisherBase(
std::shared_ptr<rcl_node_t> node_handle,
std::string topic,
size_t queue_size)
: node_handle_(node_handle),
topic_(topic), queue_size_(queue_size),
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rosidl_message_type_support_t & type_support,
const rcl_publisher_options_t & publisher_options)
: rcl_node_handle_(node_base->get_shared_rcl_node_handle()),
intra_process_publisher_id_(0), store_intra_process_message_(nullptr)
{
if (rcl_publisher_init(
&publisher_handle_, rcl_node_handle_.get(), &type_support,
topic.c_str(), &publisher_options) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("could not create publisher: ") +
rcl_get_error_string_safe());
}
// Life time of this object is tied to the publisher handle.
rmw_publisher_t * publisher_rmw_handle = rcl_publisher_get_rmw_handle(&publisher_handle_);
if (!publisher_rmw_handle) {
throw std::runtime_error(
std::string("failed to get rmw handle: ") + rcl_get_error_string_safe());
}
if (rmw_get_gid_for_publisher(publisher_rmw_handle, &rmw_gid_) != RMW_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("failed to get publisher gid: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
}
PublisherBase::~PublisherBase()
{
if (rcl_publisher_fini(&intra_process_publisher_handle_, rcl_node_handle_.get()) != RCL_RET_OK) {
fprintf(
stderr,
"Error in destruction of intra process rcl publisher handle: %s\n",
rcl_get_error_string_safe());
}
if (rcl_publisher_fini(&publisher_handle_, rcl_node_handle_.get()) != RCL_RET_OK) {
fprintf(
stderr,
"Error in destruction of rcl publisher handle: %s\n",
rcl_get_error_string_safe());
}
}
const std::string &
const char *
PublisherBase::get_topic_name() const
{
return topic_;
return rcl_publisher_get_topic_name(&publisher_handle_);
}
size_t
PublisherBase::get_queue_size() const
{
return queue_size_;
const rcl_publisher_options_t * publisher_options = rcl_publisher_get_options(&publisher_handle_);
if (!publisher_options) {
throw std::runtime_error(
std::string("failed to get publisher options: ") + rcl_get_error_string_safe());
}
return publisher_options->qos.depth;
}
const rmw_gid_t &
@@ -101,10 +140,11 @@ PublisherBase::setup_intra_process(
StoreMessageCallbackT callback,
const rcl_publisher_options_t & intra_process_options)
{
auto intra_process_topic_name = std::string(this->get_topic_name()) + "__intra";
if (rcl_publisher_init(
&intra_process_publisher_handle_, node_handle_.get(),
&intra_process_publisher_handle_, rcl_node_handle_.get(),
rclcpp::type_support::get_intra_process_message_msg_type_support(),
(topic_ + "__intra").c_str(), &intra_process_options) != RCL_RET_OK)
intra_process_topic_name.c_str(), &intra_process_options) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("could not create intra process publisher: ") +

View File

@@ -29,10 +29,14 @@ using rclcpp::service::ServiceBase;
ServiceBase::ServiceBase(
std::shared_ptr<rcl_node_t> node_handle,
const std::string service_name)
const std::string & service_name)
: node_handle_(node_handle), service_name_(service_name)
{}
ServiceBase::ServiceBase(std::shared_ptr<rcl_node_t> node_handle)
: node_handle_(node_handle)
{}
ServiceBase::~ServiceBase()
{}
@@ -45,5 +49,5 @@ ServiceBase::get_service_name()
const rcl_service_t *
ServiceBase::get_service_handle()
{
return &service_handle_;
return service_handle_;
}

View File

@@ -15,6 +15,7 @@
#include "rclcpp/subscription.hpp"
#include <cstdio>
#include <memory>
#include <string>
#include "rmw/error_handling.h"
@@ -25,14 +26,21 @@ using rclcpp::subscription::SubscriptionBase;
SubscriptionBase::SubscriptionBase(
std::shared_ptr<rcl_node_t> node_handle,
const rosidl_message_type_support_t & type_support_handle,
const std::string & topic_name,
bool ignore_local_publications)
: node_handle_(node_handle),
topic_name_(topic_name),
ignore_local_publications_(ignore_local_publications)
const rcl_subscription_options_t & subscription_options)
: node_handle_(node_handle)
{
// To avoid unused private member warnings.
(void)ignore_local_publications_;
rcl_ret_t ret = rcl_subscription_init(
&subscription_handle_,
node_handle_.get(),
&type_support_handle,
topic_name.c_str(),
&subscription_options);
if (ret != RCL_RET_OK) {
throw std::runtime_error(
std::string("could not create subscription: ") + rcl_get_error_string_safe());
}
}
SubscriptionBase::~SubscriptionBase()
@@ -53,10 +61,10 @@ SubscriptionBase::~SubscriptionBase()
}
}
const std::string &
const char *
SubscriptionBase::get_topic_name() const
{
return this->topic_name_;
return rcl_subscription_get_topic_name(&subscription_handle_);
}
const rcl_subscription_t *

View File

@@ -30,7 +30,7 @@
const rosidl_message_type_support_t *
rclcpp::type_support::get_intra_process_message_msg_type_support()
{
return rosidl_generator_cpp::get_message_type_support_handle<
return rosidl_typesupport_cpp::get_message_type_support_handle<
rcl_interfaces::msg::IntraProcessMessage
>();
}
@@ -38,7 +38,7 @@ rclcpp::type_support::get_intra_process_message_msg_type_support()
const rosidl_message_type_support_t *
rclcpp::type_support::get_parameter_event_msg_type_support()
{
return rosidl_generator_cpp::get_message_type_support_handle<
return rosidl_typesupport_cpp::get_message_type_support_handle<
rcl_interfaces::msg::ParameterEvent
>();
}
@@ -46,7 +46,7 @@ rclcpp::type_support::get_parameter_event_msg_type_support()
const rosidl_message_type_support_t *
rclcpp::type_support::get_set_parameters_result_msg_type_support()
{
return rosidl_generator_cpp::get_message_type_support_handle<
return rosidl_typesupport_cpp::get_message_type_support_handle<
rcl_interfaces::msg::SetParametersResult
>();
}
@@ -54,7 +54,7 @@ rclcpp::type_support::get_set_parameters_result_msg_type_support()
const rosidl_message_type_support_t *
rclcpp::type_support::get_parameter_descriptor_msg_type_support()
{
return rosidl_generator_cpp::get_message_type_support_handle<
return rosidl_typesupport_cpp::get_message_type_support_handle<
rcl_interfaces::msg::ParameterDescriptor
>();
}
@@ -62,7 +62,7 @@ rclcpp::type_support::get_parameter_descriptor_msg_type_support()
const rosidl_message_type_support_t *
rclcpp::type_support::get_list_parameters_result_msg_type_support()
{
return rosidl_generator_cpp::get_message_type_support_handle<
return rosidl_typesupport_cpp::get_message_type_support_handle<
rcl_interfaces::msg::ListParametersResult
>();
}
@@ -70,7 +70,7 @@ rclcpp::type_support::get_list_parameters_result_msg_type_support()
const rosidl_service_type_support_t *
rclcpp::type_support::get_get_parameters_srv_type_support()
{
return rosidl_generator_cpp::get_service_type_support_handle<
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::GetParameters
>();
}
@@ -78,7 +78,7 @@ rclcpp::type_support::get_get_parameters_srv_type_support()
const rosidl_service_type_support_t *
rclcpp::type_support::get_get_parameter_types_srv_type_support()
{
return rosidl_generator_cpp::get_service_type_support_handle<
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::GetParameterTypes
>();
}
@@ -86,7 +86,7 @@ rclcpp::type_support::get_get_parameter_types_srv_type_support()
const rosidl_service_type_support_t *
rclcpp::type_support::get_set_parameters_srv_type_support()
{
return rosidl_generator_cpp::get_service_type_support_handle<
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::SetParameters
>();
}
@@ -94,7 +94,7 @@ rclcpp::type_support::get_set_parameters_srv_type_support()
const rosidl_service_type_support_t *
rclcpp::type_support::get_list_parameters_srv_type_support()
{
return rosidl_generator_cpp::get_service_type_support_handle<
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::ListParameters
>();
}
@@ -102,7 +102,7 @@ rclcpp::type_support::get_list_parameters_srv_type_support()
const rosidl_service_type_support_t *
rclcpp::type_support::get_describe_parameters_srv_type_support()
{
return rosidl_generator_cpp::get_service_type_support_handle<
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::DescribeParameters
>();
}
@@ -110,7 +110,7 @@ rclcpp::type_support::get_describe_parameters_srv_type_support()
const rosidl_service_type_support_t *
rclcpp::type_support::get_set_parameters_atomically_srv_type_support()
{
return rosidl_generator_cpp::get_service_type_support_handle<
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::SetParametersAtomically
>();
}

View File

@@ -86,7 +86,7 @@ signal_handler(int signal_value)
g_signal_status = signal_value;
{
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
for (auto const & kv : g_sigint_guard_cond_handles) {
for (auto & kv : g_sigint_guard_cond_handles) {
rcl_ret_t status = rcl_trigger_guard_condition(&(kv.second));
if (status != RCL_RET_OK) {
fprintf(stderr,
@@ -164,7 +164,7 @@ rclcpp::utilities::shutdown()
g_signal_status = SIGINT;
{
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
for (auto const & kv : g_sigint_guard_cond_handles) {
for (auto & kv : g_sigint_guard_cond_handles) {
if (rcl_trigger_guard_condition(&(kv.second)) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to trigger sigint guard condition: %s\n",

View File

@@ -0,0 +1,3 @@
bool request
---
bool response

View File

@@ -0,0 +1,142 @@
// Copyright 2016 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 <gtest/gtest.h>
#include <string>
#include <memory>
#include "rclcpp/node.hpp"
#include "rclcpp/any_service_callback.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcl/service.h"
#include "rclcpp/srv/mock.hpp"
#include "rclcpp/srv/mock.h"
class TestExternallyDefinedServices : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
};
void
callback(const std::shared_ptr<rclcpp::srv::Mock::Request>/*req*/,
std::shared_ptr<rclcpp::srv::Mock::Response>/*resp*/)
{}
TEST_F(TestExternallyDefinedServices, default_behavior) {
auto node_handle = rclcpp::node::Node::make_shared("base_node");
try {
auto srv = node_handle->create_service<rclcpp::srv::Mock>("test",
callback);
} catch (const std::exception &) {
FAIL();
return;
}
SUCCEED();
}
TEST_F(TestExternallyDefinedServices, extern_defined_uninitialized) {
auto node_handle = rclcpp::node::Node::make_shared("base_node");
// mock for externally defined service
rcl_service_t service_handle = rcl_get_zero_initialized_service();
rclcpp::any_service_callback::AnyServiceCallback<rclcpp::srv::Mock> cb;
// don't initialize the service
// expect fail
try {
rclcpp::service::Service<rclcpp::srv::Mock>(
node_handle->get_node_base_interface()->get_shared_rcl_node_handle(),
&service_handle, cb);
} catch (const std::runtime_error &) {
SUCCEED();
return;
}
FAIL();
}
TEST_F(TestExternallyDefinedServices, extern_defined_initialized) {
auto node_handle = rclcpp::node::Node::make_shared("base_node");
// mock for externally defined service
rcl_service_t service_handle = rcl_get_zero_initialized_service();
rcl_service_options_t service_options = rcl_service_get_default_options();
const rosidl_service_type_support_t * ts = ROSIDL_GET_TYPE_SUPPORT(
rclcpp, srv, Mock);
rcl_ret_t ret = rcl_service_init(
&service_handle,
node_handle->get_node_base_interface()->get_rcl_node_handle(),
ts, "base_node_service", &service_options);
if (ret != RCL_RET_OK) {
FAIL();
return;
}
rclcpp::any_service_callback::AnyServiceCallback<rclcpp::srv::Mock> cb;
try {
rclcpp::service::Service<rclcpp::srv::Mock>(
node_handle->get_node_base_interface()->get_shared_rcl_node_handle(),
&service_handle, cb);
} catch (const std::runtime_error &) {
FAIL();
return;
}
SUCCEED();
}
TEST_F(TestExternallyDefinedServices, extern_defined_destructor) {
auto node_handle = rclcpp::node::Node::make_shared("base_node");
// mock for externally defined service
rcl_service_t service_handle = rcl_get_zero_initialized_service();
rcl_service_options_t service_options = rcl_service_get_default_options();
const rosidl_service_type_support_t * ts = ROSIDL_GET_TYPE_SUPPORT(
rclcpp, srv, Mock);
rcl_ret_t ret = rcl_service_init(
&service_handle,
node_handle->get_node_base_interface()->get_rcl_node_handle(),
ts, "base_node_service", &service_options);
if (ret != RCL_RET_OK) {
FAIL();
return;
}
rclcpp::any_service_callback::AnyServiceCallback<rclcpp::srv::Mock> cb;
{
// Call constructor
rclcpp::service::Service<rclcpp::srv::Mock> srv_cpp(
node_handle->get_node_base_interface()->get_shared_rcl_node_handle(),
&service_handle, cb);
// Call destructor
}
if (!service_handle.impl) {
FAIL();
return;
}
SUCCEED();
}

View File

@@ -0,0 +1,76 @@
// Copyright 2016 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 <gtest/gtest.h>
#include <memory>
#include "rclcpp/strategies/allocator_memory_strategy.hpp"
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/rclcpp.hpp"
class TestFindWeakNodes : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
};
TEST_F(TestFindWeakNodes, allocator_strategy_with_weak_nodes) {
// GIVEN
// A vector of weak pointers to nodes
auto memory_strategy = std::make_shared<
rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy<>>();
auto existing_node = rclcpp::node::Node::make_shared("existing_node");
auto dead_node = rclcpp::node::Node::make_shared("dead_node");
rclcpp::memory_strategy::MemoryStrategy::WeakNodeVector weak_nodes;
weak_nodes.push_back(existing_node->get_node_base_interface());
weak_nodes.push_back(dead_node->get_node_base_interface());
// AND
// Delete dead_node, creating a dangling pointer in weak_nodes
dead_node.reset();
ASSERT_FALSE(weak_nodes[0].expired());
ASSERT_TRUE(weak_nodes[1].expired());
// WHEN
bool has_invalid_weak_nodes = memory_strategy->collect_entities(weak_nodes);
// THEN
// The result of finding dangling node pointers should be true
ASSERT_TRUE(has_invalid_weak_nodes);
}
TEST_F(TestFindWeakNodes, allocator_strategy_no_weak_nodes) {
// GIVEN
// A vector of weak pointers to nodes, all valid
auto memory_strategy = std::make_shared<
rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy<>>();
auto existing_node1 = rclcpp::node::Node::make_shared("existing_node1");
auto existing_node2 = rclcpp::node::Node::make_shared("existing_node2");
rclcpp::memory_strategy::MemoryStrategy::WeakNodeVector weak_nodes;
weak_nodes.push_back(existing_node1->get_node_base_interface());
weak_nodes.push_back(existing_node2->get_node_base_interface());
ASSERT_FALSE(weak_nodes[0].expired());
ASSERT_FALSE(weak_nodes[1].expired());
// WHEN
bool has_invalid_weak_nodes = memory_strategy->collect_entities(weak_nodes);
// THEN
// The result of finding dangling node pointers should be false
ASSERT_FALSE(has_invalid_weak_nodes);
}

View File

@@ -31,14 +31,14 @@ namespace mock
class PublisherBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase);
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
PublisherBase()
: mock_topic_name(""), mock_queue_size(0) {}
const std::string & get_topic_name() const
const char * get_topic_name() const
{
return mock_topic_name;
return mock_topic_name.c_str();
}
size_t get_queue_size() const
{
@@ -66,7 +66,7 @@ public:
using MessageUniquePtr = std::unique_ptr<T, MessageDeleter>;
std::shared_ptr<MessageAlloc> allocator_;
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<T, Alloc>);
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<T, Alloc>)
Publisher()
{
@@ -93,14 +93,14 @@ namespace mock
class SubscriptionBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionBase);
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionBase)
SubscriptionBase()
: mock_topic_name(""), mock_queue_size(0) {}
const std::string & get_topic_name() const
const char * get_topic_name() const
{
return mock_topic_name;
return mock_topic_name.c_str();
}
size_t get_queue_size() const
{

View File

@@ -17,6 +17,8 @@
#define RCLCPP_BUILDING_LIBRARY 1 // Prevent including unavailable symbols
#include <rclcpp/mapped_ring_buffer.hpp>
#include <memory>
/*
Tests get_copy and pop on an empty mrb.
*/

View File

@@ -0,0 +1,99 @@
cmake_minimum_required(VERSION 3.5)
project(rclcpp_lifecycle)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-std=c++14")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rcl_lifecycle REQUIRED)
find_package(rmw_implementation_cmake REQUIRED)
find_package(std_msgs REQUIRED)
find_package(lifecycle_msgs REQUIRED)
include_directories(include)
macro(targets)
get_rclcpp_information("${rmw_implementation}" "rclcpp${target_suffix}")
get_rcl_lifecycle_information("${rmw_implementation}" "rcl_lifecycle${target_suffix}")
### CPP High level library
add_library(rclcpp_lifecycle${target_suffix}
SHARED
src/lifecycle_node.cpp
src/node_interfaces/lifecycle_node_interface.cpp
src/state.cpp
src/transition.cpp
)
ament_target_dependencies(rclcpp_lifecycle${target_suffix}
"rclcpp${target_suffix}"
"rcl_lifecycle${target_suffix}"
"lifecycle_msgs")
# Causes the visibility macros to use dllexport rather than dllimport,
# which is appropriate when building the dll but not consuming it.
target_compile_definitions(rclcpp_lifecycle${target_suffix} PRIVATE "RCLCPP_LIFECYCLE_BUILDING_DLL")
install(TARGETS
rclcpp_lifecycle${target_suffix}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin)
endmacro()
call_for_each_rmw_implementation(targets GENERATE_DEFAULT)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
ament_add_gtest(test_lifecycle_node test/test_lifecycle_node.cpp)
if(TARGET test_lifecycle_node)
target_include_directories(test_lifecycle_node PUBLIC
${rcl_lifecycle_INCLUDE_DIRS}
${rclcpp_INCLUDE_DIRS}
${rclcpp_lifecycle_INCLUDE_DIRS}
)
target_link_libraries(test_lifecycle_node ${PROJECT_NAME})
endif()
ament_add_gtest(test_state_machine_info test/test_state_machine_info.cpp)
if(TARGET test_state_machine_info)
target_include_directories(test_state_machine_info PUBLIC
${rcl_lifecycle_INCLUDE_DIRS}
${rclcpp_INCLUDE_DIRS}
${rclcpp_lifecycle_INCLUDE_DIRS}
)
target_link_libraries(test_state_machine_info ${PROJECT_NAME})
endif()
ament_add_gtest(test_register_custom_callbacks test/test_register_custom_callbacks.cpp)
if(TARGET test_register_custom_callbacks)
target_include_directories(test_register_custom_callbacks PUBLIC
${rcl_lifecycle_INCLUDE_DIRS}
${rclcpp_INCLUDE_DIRS}
${rclcpp_lifecycle_INCLUDE_DIRS}
)
target_link_libraries(test_register_custom_callbacks ${PROJECT_NAME})
endif()
ament_add_gtest(test_callback_exceptions test/test_callback_exceptions.cpp)
if(TARGET test_callback_exceptions)
target_include_directories(test_callback_exceptions PUBLIC
${rcl_lifecycle_INCLUDE_DIRS}
${rclcpp_INCLUDE_DIRS}
${rclcpp_lifecycle_INCLUDE_DIRS}
)
target_link_libraries(test_callback_exceptions ${PROJECT_NAME})
endif()
endif()
ament_export_dependencies(rclcpp)
ament_export_dependencies(rcl_lifecycle)
ament_export_dependencies(lifecycle_msgs)
ament_export_include_directories(include)
ament_export_libraries(${PROJECT_NAME})
ament_package()
install(DIRECTORY include/
DESTINATION include)

View File

@@ -0,0 +1,416 @@
// Copyright 2016 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_LIFECYCLE__LIFECYCLE_NODE_HPP_
#define RCLCPP_LIFECYCLE__LIFECYCLE_NODE_HPP_
#include <string>
#include <map>
#include <vector>
#include <memory>
#include "rcl/error_handling.h"
#include "rcl/node.h"
#include "rcl_interfaces/msg/list_parameters_result.hpp"
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
#include "rcl_interfaces/msg/parameter_event.hpp"
#include "rcl_interfaces/msg/set_parameters_result.hpp"
#include "rclcpp/callback_group.hpp"
#include "rclcpp/client.hpp"
#include "rclcpp/context.hpp"
#include "rclcpp/event.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/message_memory_strategy.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/timer.hpp"
#include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp"
#include "rclcpp_lifecycle/lifecycle_publisher.hpp"
#include "rclcpp_lifecycle/state.hpp"
#include "rclcpp_lifecycle/transition.hpp"
#include "rclcpp_lifecycle/visibility_control.h"
namespace rclcpp_lifecycle
{
/// LifecycleNode for creating lifecycle components
/**
* has lifecycle nodeinterface for configuring this node.
*/
class LifecycleNode : public node_interfaces::LifecycleNodeInterface,
public std::enable_shared_from_this<LifecycleNode>
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(LifecycleNode)
/// Create a new lifecycle node with the specified name.
/**
* \param[in] node_name Name of the node.
* \param[in] use_intra_process_comms True to use the optimized intra-process communication
* pipeline to pass messages between nodes in the same process using shared memory.
*/
RCLCPP_LIFECYCLE_PUBLIC
explicit LifecycleNode(const std::string & node_name, bool use_intra_process_comms = false);
/// Create a node based on the node name and a rclcpp::context::Context.
/**
* \param[in] node_name Name of the node.
* \param[in] context The context for the node (usually represents the state of a process).
* \param[in] use_intra_process_comms True to use the optimized intra-process communication
* pipeline to pass messages between nodes in the same process using shared memory.
*/
RCLCPP_LIFECYCLE_PUBLIC
LifecycleNode(
const std::string & node_name, rclcpp::context::Context::SharedPtr context,
bool use_intra_process_comms = false);
RCLCPP_LIFECYCLE_PUBLIC
virtual ~LifecycleNode();
/// Get the name of the node.
// \return The name of the node.
RCLCPP_LIFECYCLE_PUBLIC
const char *
get_name() const;
/// Create and return a callback group.
RCLCPP_LIFECYCLE_PUBLIC
rclcpp::callback_group::CallbackGroup::SharedPtr
create_callback_group(rclcpp::callback_group::CallbackGroupType group_type);
/// Return the list of callback groups in the node.
RCLCPP_LIFECYCLE_PUBLIC
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
get_callback_groups() const;
/// Create and return a Publisher.
/**
* \param[in] topic_name The topic for this publisher to publish on.
* \param[in] qos_history_depth The depth of the publisher message queue.
* \return Shared pointer to the created publisher.
*/
template<typename MessageT, typename Alloc = std::allocator<void>>
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<MessageT, Alloc>>
create_publisher(
const std::string & topic_name, size_t qos_history_depth,
std::shared_ptr<Alloc> allocator = nullptr);
/// Create and return a LifecyclePublisher.
/**
* \param[in] topic_name The topic for this publisher to publish on.
* \param[in] qos_history_depth The depth of the publisher message queue.
* \return Shared pointer to the created publisher.
*/
template<typename MessageT, typename Alloc = std::allocator<void>>
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<MessageT, Alloc>>
create_publisher(
const std::string & topic_name,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_default,
std::shared_ptr<Alloc> allocator = nullptr);
/// Create and return a Subscription.
/**
* \param[in] topic_name The topic to subscribe on.
* \param[in] callback The user-defined callback function.
* \param[in] qos_profile The quality of service profile to pass on to the rmw implementation.
* \param[in] group The callback group for this subscription. NULL for no callback group.
* \param[in] ignore_local_publications True to ignore local publications.
* \param[in] msg_mem_strat The message memory strategy to use for allocating messages.
* \return Shared pointer to the created subscription.
*/
/* TODO(jacquelinekay):
Windows build breaks when static member function passed as default
argument to msg_mem_strat, nullptr is a workaround.
*/
template<
typename MessageT,
typename CallbackT,
typename Alloc = std::allocator<void>,
typename SubscriptionT = rclcpp::subscription::Subscription<MessageT, Alloc>>
std::shared_ptr<SubscriptionT>
create_subscription(
const std::string & topic_name,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_default,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr,
bool ignore_local_publications = false,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
msg_mem_strat = nullptr,
std::shared_ptr<Alloc> allocator = nullptr);
/// Create and return a Subscription.
/**
* \param[in] topic_name The topic to subscribe on.
* \param[in] qos_history_depth The depth of the subscription's incoming message queue.
* \param[in] callback The user-defined callback function.
* \param[in] group The callback group for this subscription. NULL for no callback group.
* \param[in] ignore_local_publications True to ignore local publications.
* \param[in] msg_mem_strat The message memory strategy to use for allocating messages.
* \return Shared pointer to the created subscription.
*/
/* TODO(jacquelinekay):
Windows build breaks when static member function passed as default
argument to msg_mem_strat, nullptr is a workaround.
*/
template<
typename MessageT,
typename CallbackT,
typename Alloc = std::allocator<void>,
typename SubscriptionT = rclcpp::subscription::Subscription<MessageT, Alloc>>
std::shared_ptr<SubscriptionT>
create_subscription(
const std::string & topic_name,
size_t qos_history_depth,
CallbackT && callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr,
bool ignore_local_publications = false,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
msg_mem_strat = nullptr,
std::shared_ptr<Alloc> allocator = nullptr);
/// Create a timer.
/**
* \param[in] period Time interval between triggers of the callback.
* \param[in] callback User-defined callback function.
* \param[in] group Callback group to execute this timer's callback in.
*/
template<typename DurationT = std::milli, typename CallbackT>
typename rclcpp::timer::WallTimer<CallbackT>::SharedPtr
create_wall_timer(
std::chrono::duration<int64_t, DurationT> period,
CallbackT callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
/* Create and return a Client. */
template<typename ServiceT>
typename rclcpp::client::Client<ServiceT>::SharedPtr
create_client(
const std::string & service_name,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_services_default,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
/* Create and return a Service. */
template<typename ServiceT, typename CallbackT>
typename rclcpp::service::Service<ServiceT>::SharedPtr
create_service(
const std::string & service_name,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_services_default,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
RCLCPP_LIFECYCLE_PUBLIC
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
RCLCPP_LIFECYCLE_PUBLIC
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
RCLCPP_LIFECYCLE_PUBLIC
std::vector<rclcpp::parameter::ParameterVariant>
get_parameters(const std::vector<std::string> & names) const;
RCLCPP_LIFECYCLE_PUBLIC
rclcpp::parameter::ParameterVariant
get_parameter(const std::string & name) const;
RCLCPP_LIFECYCLE_PUBLIC
bool
get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const;
template<typename ParameterT>
bool
get_parameter(const std::string & name, ParameterT & parameter) const;
RCLCPP_LIFECYCLE_PUBLIC
std::vector<rcl_interfaces::msg::ParameterDescriptor>
describe_parameters(const std::vector<std::string> & names) const;
RCLCPP_LIFECYCLE_PUBLIC
std::vector<uint8_t>
get_parameter_types(const std::vector<std::string> & names) const;
RCLCPP_LIFECYCLE_PUBLIC
rcl_interfaces::msg::ListParametersResult
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const;
/// Register the callback for parameter changes
/**
* \param[in] User defined callback function, It is expected to atomically set parameters.
* \note Repeated invocations of this function will overwrite previous callbacks
*/
template<typename CallbackT>
void
register_param_change_callback(CallbackT && callback);
RCLCPP_LIFECYCLE_PUBLIC
std::map<std::string, std::string>
get_topic_names_and_types() const;
RCLCPP_LIFECYCLE_PUBLIC
size_t
count_publishers(const std::string & topic_name) const;
RCLCPP_LIFECYCLE_PUBLIC
size_t
count_subscribers(const std::string & topic_name) const;
/// Return a graph event, which will be set anytime a graph change occurs.
/* The graph Event object is a loan which must be returned.
* The Event object is scoped and therefore to return the load just let it go
* out of scope.
*/
RCLCPP_LIFECYCLE_PUBLIC
rclcpp::event::Event::SharedPtr
get_graph_event();
/// Wait for a graph event to occur by waiting on an Event to become set.
/* The given Event must be acquire through the get_graph_event() method.
*
* \throws InvalidEventError if the given event is nullptr
* \throws EventNotRegisteredError if the given event was not acquired with
* get_graph_event().
*/
RCLCPP_LIFECYCLE_PUBLIC
void
wait_for_graph_change(
rclcpp::event::Event::SharedPtr event,
std::chrono::nanoseconds timeout);
/// Return the Node's internal NodeBaseInterface implementation.
RCLCPP_LIFECYCLE_PUBLIC
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_base_interface();
/// Return the Node's internal NodeGraphInterface implementation.
RCLCPP_LIFECYCLE_PUBLIC
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr
get_node_graph_interface();
/// Return the Node's internal NodeTimersInterface implementation.
RCLCPP_LIFECYCLE_PUBLIC
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr
get_node_timers_interface();
/// Return the Node's internal NodeTopicsInterface implementation.
RCLCPP_LIFECYCLE_PUBLIC
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr
get_node_topics_interface();
/// Return the Node's internal NodeServicesInterface implementation.
RCLCPP_LIFECYCLE_PUBLIC
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr
get_node_services_interface();
/// Return the Node's internal NodeParametersInterface implementation.
RCLCPP_LIFECYCLE_PUBLIC
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr
get_node_parameters_interface();
//
// LIFECYCLE COMPONENTS
//
RCLCPP_LIFECYCLE_PUBLIC
const State &
get_current_state();
RCLCPP_LIFECYCLE_PUBLIC
std::vector<State>
get_available_states();
RCLCPP_LIFECYCLE_PUBLIC
std::vector<Transition>
get_available_transitions();
/// trigger the specified transition
/*
* return the new state after this transition
*/
RCLCPP_LIFECYCLE_PUBLIC
const State &
trigger_transition(const Transition & transition);
RCLCPP_LIFECYCLE_PUBLIC
const State &
trigger_transition(unsigned int transition_id);
RCLCPP_LIFECYCLE_PUBLIC
bool
register_on_configure(std::function<rcl_lifecycle_ret_t(const State &)> fcn);
RCLCPP_LIFECYCLE_PUBLIC
bool
register_on_cleanup(std::function<rcl_lifecycle_ret_t(const State &)> fcn);
RCLCPP_LIFECYCLE_PUBLIC
bool
register_on_shutdown(std::function<rcl_lifecycle_ret_t(const State &)> fcn);
RCLCPP_LIFECYCLE_PUBLIC
bool
register_on_activate(std::function<rcl_lifecycle_ret_t(const State &)> fcn);
RCLCPP_LIFECYCLE_PUBLIC
bool
register_on_deactivate(std::function<rcl_lifecycle_ret_t(const State &)> fcn);
RCLCPP_LIFECYCLE_PUBLIC
bool
register_on_error(std::function<rcl_lifecycle_ret_t(const State &)> fcn);
protected:
RCLCPP_LIFECYCLE_PUBLIC
void
add_publisher_handle(std::shared_ptr<rclcpp_lifecycle::LifecyclePublisherInterface> pub);
RCLCPP_LIFECYCLE_PUBLIC
void
add_timer_handle(std::shared_ptr<rclcpp::timer::TimerBase> timer);
private:
RCLCPP_DISABLE_COPY(LifecycleNode)
RCLCPP_LIFECYCLE_PUBLIC
bool
group_in_node(rclcpp::callback_group::CallbackGroup::SharedPtr group);
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_;
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_;
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr node_timers_;
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_;
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
bool use_intra_process_comms_;
class LifecycleNodeInterfaceImpl;
std::unique_ptr<LifecycleNodeInterfaceImpl> impl_;
};
} // namespace rclcpp_lifecycle
#ifndef RCLCPP_LIFECYCLE__LIFECYCLE_NODE_IMPL_HPP_
// Template implementations
#include "rclcpp_lifecycle/lifecycle_node_impl.hpp"
#endif
#endif // RCLCPP_LIFECYCLE__LIFECYCLE_NODE_HPP_

View File

@@ -0,0 +1,212 @@
// Copyright 2016 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_LIFECYCLE__LIFECYCLE_NODE_IMPL_HPP_
#define RCLCPP_LIFECYCLE__LIFECYCLE_NODE_IMPL_HPP_
#include <memory>
#include <string>
#include <utility>
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/create_publisher.hpp"
#include "rclcpp/create_subscription.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "lifecycle_publisher.hpp"
#include "rclcpp_lifecycle/visibility_control.h"
#include "rclcpp_lifecycle/lifecycle_node.hpp"
namespace rclcpp_lifecycle
{
template<typename MessageT, typename Alloc>
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<MessageT, Alloc>>
LifecycleNode::create_publisher(
const std::string & topic_name, size_t qos_history_depth,
std::shared_ptr<Alloc> allocator)
{
if (!allocator) {
allocator = std::make_shared<Alloc>();
}
rmw_qos_profile_t qos = rmw_qos_profile_default;
qos.depth = qos_history_depth;
return this->create_publisher<MessageT, Alloc>(topic_name, qos, allocator);
}
template<typename MessageT, typename Alloc>
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<MessageT, Alloc>>
LifecycleNode::create_publisher(
const std::string & topic_name,
const rmw_qos_profile_t & qos_profile,
std::shared_ptr<Alloc> allocator)
{
using PublisherT = rclcpp_lifecycle::LifecyclePublisher<MessageT, Alloc>;
// create regular publisher in rclcpp::Node
return rclcpp::create_publisher<MessageT, Alloc, PublisherT>(
this->node_topics_.get(),
topic_name,
qos_profile,
use_intra_process_comms_,
allocator);
}
// TODO(karsten1987): Create LifecycleSubscriber
template<typename MessageT, typename CallbackT, typename Alloc, typename SubscriptionT>
std::shared_ptr<SubscriptionT>
LifecycleNode::create_subscription(
const std::string & topic_name,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group,
bool ignore_local_publications,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
msg_mem_strat,
std::shared_ptr<Alloc> allocator)
{
if (!allocator) {
allocator = std::make_shared<Alloc>();
}
if (!msg_mem_strat) {
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
msg_mem_strat = MessageMemoryStrategy<MessageT, Alloc>::create_default();
}
return rclcpp::create_subscription<
MessageT, CallbackT, Alloc,
rclcpp::subscription::Subscription<MessageT, Alloc>>(
this->node_topics_.get(),
topic_name,
std::forward<CallbackT>(callback),
qos_profile,
group,
ignore_local_publications,
use_intra_process_comms_,
msg_mem_strat,
allocator);
}
template<
typename MessageT,
typename CallbackT,
typename Alloc,
typename SubscriptionT>
std::shared_ptr<SubscriptionT>
LifecycleNode::create_subscription(
const std::string & topic_name,
size_t qos_history_depth,
CallbackT && callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group,
bool ignore_local_publications,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
msg_mem_strat,
std::shared_ptr<Alloc> allocator)
{
rmw_qos_profile_t qos = rmw_qos_profile_default;
qos.depth = qos_history_depth;
return this->create_subscription<MessageT, CallbackT, Alloc>(
topic_name,
std::forward<CallbackT>(callback),
qos,
group,
ignore_local_publications,
msg_mem_strat,
allocator);
}
template<typename DurationT, typename CallbackT>
typename rclcpp::timer::WallTimer<CallbackT>::SharedPtr
LifecycleNode::create_wall_timer(
std::chrono::duration<int64_t, DurationT> period,
CallbackT callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
auto timer = rclcpp::timer::WallTimer<CallbackT>::make_shared(
std::chrono::duration_cast<std::chrono::nanoseconds>(period),
std::move(callback));
node_timers_->add_timer(timer, group);
return timer;
}
template<typename ServiceT>
typename rclcpp::client::Client<ServiceT>::SharedPtr
LifecycleNode::create_client(
const std::string & service_name,
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
rcl_client_options_t options = rcl_client_get_default_options();
options.qos = qos_profile;
using rclcpp::client::Client;
using rclcpp::client::ClientBase;
auto cli = Client<ServiceT>::make_shared(
node_base_.get(),
node_graph_,
service_name,
options);
auto cli_base_ptr = std::dynamic_pointer_cast<ClientBase>(cli);
node_services_->add_client(cli_base_ptr, group);
return cli;
}
template<typename ServiceT, typename CallbackT>
typename rclcpp::service::Service<ServiceT>::SharedPtr
LifecycleNode::create_service(
const std::string & service_name,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
rclcpp::service::AnyServiceCallback<ServiceT> any_service_callback;
any_service_callback.set(std::forward<CallbackT>(callback));
rcl_service_options_t service_options = rcl_service_get_default_options();
service_options.qos = qos_profile;
auto serv = rclcpp::service::Service<ServiceT>::make_shared(
node_base_->get_shared_rcl_node_handle(),
service_name, any_service_callback, service_options);
auto serv_base_ptr = std::dynamic_pointer_cast<rclcpp::service::ServiceBase>(serv);
node_services_->add_service(serv_base_ptr, group);
return serv;
}
template<typename ParameterT>
bool
LifecycleNode::get_parameter(const std::string & name, ParameterT & parameter) const
{
rclcpp::parameter::ParameterVariant parameter_variant(name, parameter);
bool result = get_parameter(name, parameter_variant);
parameter = parameter_variant.get_value<ParameterT>();
return result;
}
template<typename CallbackT>
void
LifecycleNode::register_param_change_callback(CallbackT && callback)
{
this->node_parameters_->register_param_change_callback(std::forward<CallbackT>(callback));
}
} // namespace rclcpp_lifecycle
#endif // RCLCPP_LIFECYCLE__LIFECYCLE_NODE_IMPL_HPP_

View File

@@ -0,0 +1,167 @@
// Copyright 2016 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_LIFECYCLE__LIFECYCLE_PUBLISHER_HPP_
#define RCLCPP_LIFECYCLE__LIFECYCLE_PUBLISHER_HPP_
#include <memory>
#include <string>
#include "rclcpp/publisher.hpp"
namespace rclcpp_lifecycle
{
/// base class with only
/**
* pure virtual functions. A managed
* node can then deactivate or activate
* the publishing.
* It is more a convenient interface class
* than a necessary base class.
*/
class LifecyclePublisherInterface
{
public:
virtual void on_activate() = 0;
virtual void on_deactivate() = 0;
virtual bool is_activated() = 0;
};
/// brief child class of rclcpp Publisher class.
/**
* Overrides all publisher functions to check for
* enabled/disabled state.
*/
template<typename MessageT, typename Alloc = std::allocator<void>>
class LifecyclePublisher : public LifecyclePublisherInterface,
public rclcpp::publisher::Publisher<MessageT, Alloc>
{
public:
using MessageAllocTraits = rclcpp::allocator::AllocRebind<MessageT, Alloc>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using MessageDeleter = rclcpp::allocator::Deleter<MessageAlloc, MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
LifecyclePublisher(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rcl_publisher_options_t & publisher_options,
std::shared_ptr<MessageAlloc> allocator)
: rclcpp::publisher::Publisher<MessageT, Alloc>(
node_base, topic, publisher_options, allocator),
enabled_(false)
{}
~LifecyclePublisher() {}
/// LifecyclePublisher pulish function
/**
* The publish function checks whether the communication
* was enabled or disabled and forwards the message
* to the actual rclcpp Publisher base class
*/
virtual void
publish(std::unique_ptr<MessageT, MessageDeleter> & msg)
{
if (!enabled_) {
return;
}
rclcpp::publisher::Publisher<MessageT, Alloc>::publish(msg);
}
/// LifecyclePublisher pulish function
/**
* The publish function checks whether the communication
* was enabled or disabled and forwards the message
* to the actual rclcpp Publisher base class
*/
virtual void
publish(const std::shared_ptr<MessageT> & msg)
{
if (!enabled_) {
return;
}
rclcpp::publisher::Publisher<MessageT, Alloc>::publish(msg);
}
/// LifecyclePublisher pulish function
/**
* The publish function checks whether the communication
* was enabled or disabled and forwards the message
* to the actual rclcpp Publisher base class
*/
virtual void
publish(std::shared_ptr<const MessageT> msg)
{
if (!enabled_) {
return;
}
rclcpp::publisher::Publisher<MessageT, Alloc>::publish(msg);
}
/// LifecyclePublisher pulish function
/**
* The publish function checks whether the communication
* was enabled or disabled and forwards the message
* to the actual rclcpp Publisher base class
*/
virtual void
publish(const MessageT & msg)
{
if (!enabled_) {
return;
}
rclcpp::publisher::Publisher<MessageT, Alloc>::publish(msg);
}
/// LifecyclePublisher pulish function
/**
* The publish function checks whether the communication
* was enabled or disabled and forwards the message
* to the actual rclcpp Publisher base class
*/
virtual void
publish(std::shared_ptr<const MessageT> & msg)
{
if (!enabled_) {
return;
}
rclcpp::publisher::Publisher<MessageT, Alloc>::publish(msg);
}
virtual void
on_activate()
{
enabled_ = true;
}
virtual void
on_deactivate()
{
enabled_ = false;
}
virtual bool
is_activated()
{
return enabled_;
}
private:
bool enabled_ = false;
};
} // namespace rclcpp_lifecycle
#endif // RCLCPP_LIFECYCLE__LIFECYCLE_PUBLISHER_HPP_

View File

@@ -0,0 +1,98 @@
// Copyright 2016 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_LIFECYCLE__NODE_INTERFACES__LIFECYCLE_NODE_INTERFACE_HPP_
#define RCLCPP_LIFECYCLE__NODE_INTERFACES__LIFECYCLE_NODE_INTERFACE_HPP_
#include "rcl_lifecycle/data_types.h"
#include "rclcpp_lifecycle/state.hpp"
#include "rclcpp_lifecycle/visibility_control.h"
namespace rclcpp_lifecycle
{
namespace node_interfaces
{
/// Interface class for a managed node.
/** Virtual functions as defined in
* http://design.ros2.org/articles/node_lifecycle.html
*
* If the callback function returns successfully,
* the specified transition is completed.
* If the callback function fails or throws an
* uncaught exception, the on_error function is
* called.
* By default, all functions remain optional to overwrite
* and return true. Except the on_error function, which
* returns false and thus goes to shutdown/finalize state.
*/
class LifecycleNodeInterface
{
protected:
RCLCPP_LIFECYCLE_PUBLIC
LifecycleNodeInterface() {}
public:
/// Callback function for configure transition
/*
* \return true by default
*/
RCLCPP_LIFECYCLE_PUBLIC
virtual rcl_lifecycle_ret_t
on_configure(const State & previous_state);
/// Callback function for cleanup transition
/*
* \return true by default
*/
RCLCPP_LIFECYCLE_PUBLIC
virtual rcl_lifecycle_ret_t
on_cleanup(const State & previous_state);
/// Callback function for shutdown transition
/*
* \return true by default
*/
RCLCPP_LIFECYCLE_PUBLIC
virtual rcl_lifecycle_ret_t
on_shutdown(const State & previous_state);
/// Callback function for activate transition
/*
* \return true by default
*/
RCLCPP_LIFECYCLE_PUBLIC
virtual rcl_lifecycle_ret_t
on_activate(const State & previous_state);
/// Callback function for deactivate transition
/*
* \return true by default
*/
RCLCPP_LIFECYCLE_PUBLIC
virtual rcl_lifecycle_ret_t
on_deactivate(const State & previous_state);
/// Callback function for errorneous transition
/*
* \return false by default
*/
RCLCPP_LIFECYCLE_PUBLIC
virtual rcl_lifecycle_ret_t
on_error(const State & previous_state);
};
} // namespace node_interfaces
} // namespace rclcpp_lifecycle
#endif // RCLCPP_LIFECYCLE__NODE_INTERFACES__LIFECYCLE_NODE_INTERFACE_HPP_

View File

@@ -0,0 +1,57 @@
// Copyright 2016 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_LIFECYCLE__STATE_HPP_
#define RCLCPP_LIFECYCLE__STATE_HPP_
#include <string>
#include "rclcpp_lifecycle/visibility_control.h"
// forward declare rcl_state_t
typedef struct rcl_lifecycle_state_t rcl_lifecycle_state_t;
namespace rclcpp_lifecycle
{
class State
{
public:
RCLCPP_LIFECYCLE_PUBLIC
State();
RCLCPP_LIFECYCLE_PUBLIC
State(unsigned int id, const std::string & label);
RCLCPP_LIFECYCLE_PUBLIC
explicit State(const rcl_lifecycle_state_t * rcl_lifecycle_state_handle);
RCLCPP_LIFECYCLE_PUBLIC
virtual ~State();
RCLCPP_LIFECYCLE_PUBLIC
unsigned int
id() const;
RCLCPP_LIFECYCLE_PUBLIC
std::string
label() const;
protected:
bool owns_rcl_state_handle_;
const rcl_lifecycle_state_t * state_handle_;
};
} // namespace rclcpp_lifecycle
#endif // RCLCPP_LIFECYCLE__STATE_HPP_

View File

@@ -0,0 +1,72 @@
// Copyright 2016 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_LIFECYCLE__TRANSITION_HPP_
#define RCLCPP_LIFECYCLE__TRANSITION_HPP_
#include <string>
#include "rclcpp_lifecycle/state.hpp"
#include "rclcpp_lifecycle/visibility_control.h"
// forward declare rcl_transition_t
typedef struct rcl_lifecycle_transition_t rcl_lifecycle_transition_t;
namespace rclcpp_lifecycle
{
class Transition
{
public:
RCLCPP_LIFECYCLE_PUBLIC
Transition() = delete;
RCLCPP_LIFECYCLE_PUBLIC
explicit Transition(unsigned int id, const std::string & label = "");
RCLCPP_LIFECYCLE_PUBLIC
Transition(
unsigned int id, const std::string & label,
State && start, State && goal);
RCLCPP_LIFECYCLE_PUBLIC
explicit Transition(const rcl_lifecycle_transition_t * rcl_lifecycle_transition_handle);
RCLCPP_LIFECYCLE_PUBLIC
virtual ~Transition();
RCLCPP_LIFECYCLE_PUBLIC
unsigned int
id() const;
RCLCPP_LIFECYCLE_PUBLIC
std::string
label() const;
RCLCPP_LIFECYCLE_PUBLIC
State
start_state() const;
RCLCPP_LIFECYCLE_PUBLIC
State
goal_state() const;
protected:
bool owns_rcl_transition_handle_;
const rcl_lifecycle_transition_t * transition_handle_;
};
} // namespace rclcpp_lifecycle
#endif // RCLCPP_LIFECYCLE__TRANSITION_HPP_

View File

@@ -0,0 +1,56 @@
// Copyright 2016 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_LIFECYCLE__TYPE_TRAITS__IS_MANAGEABLE_NODE_HPP_
#define RCLCPP_LIFECYCLE__TYPE_TRAITS__IS_MANAGEABLE_NODE_HPP_
#include <utility>
#include <type_traits>
template<class T, typename = void>
struct has_on_activate
{
static constexpr bool value = false;
};
template<class T>
struct has_on_activate<T, typename std::enable_if<
std::is_same<void, decltype(std::declval<T>().on_activate())>::value>::type>
{
static constexpr bool value = true;
};
template<class T, typename = void>
struct has_on_deactivate
{
static constexpr bool value = false;
};
template<class T>
struct has_on_deactivate<T, typename std::enable_if<
std::is_same<void, decltype(std::declval<T>().on_deactivate())>::value>::type>
{
static constexpr bool value = true;
};
template<class T, typename = void>
struct is_manageable_node : std::false_type
{};
template<class T>
struct is_manageable_node<T, typename std::enable_if<
has_on_activate<T>::value && has_on_deactivate<T>::value>::type>: std::true_type
{};
#endif // RCLCPP_LIFECYCLE__TYPE_TRAITS__IS_MANAGEABLE_NODE_HPP_

View File

@@ -0,0 +1,56 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/* This header must be included by all rclcpp headers which declare symbols
* which are defined in the rclcpp library. When not building the rclcpp
* library, i.e. when using the headers in other package's code, the contents
* of this header change the visibility of certain symbols which the rclcpp
* library cannot have, but the consuming code must have inorder to link.
*/
#ifndef RCLCPP_LIFECYCLE__VISIBILITY_CONTROL_H_
#define RCLCPP_LIFECYCLE__VISIBILITY_CONTROL_H_
// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
// https://gcc.gnu.org/wiki/Visibility
#if defined _WIN32 || defined __CYGWIN__
#ifdef __GNUC__
#define RCLCPP_LIFECYCLE_EXPORT __attribute__ ((dllexport))
#define RCLCPP_LIFECYCLE_IMPORT __attribute__ ((dllimport))
#else
#define RCLCPP_LIFECYCLE_EXPORT __declspec(dllexport)
#define RCLCPP_LIFECYCLE_IMPORT __declspec(dllimport)
#endif
#ifdef RCLCPP_LIFECYCLE_BUILDING_DLL
#define RCLCPP_LIFECYCLE_PUBLIC RCLCPP_LIFECYCLE_EXPORT
#else
#define RCLCPP_LIFECYCLE_PUBLIC RCLCPP_LIFECYCLE_IMPORT
#endif
#define RCLCPP_LIFECYCLE_PUBLIC_TYPE RCLCPP_LIFECYCLE_PUBLIC
#define RCLCPP_LIFECYCLE_LOCAL
#else
#define RCLCPP_LIFECYCLE_EXPORT __attribute__ ((visibility("default")))
#define RCLCPP_LIFECYCLE_IMPORT
#if __GNUC__ >= 4
#define RCLCPP_LIFECYCLE_PUBLIC __attribute__ ((visibility("default")))
#define RCLCPP_LIFECYCLE_LOCAL __attribute__ ((visibility("hidden")))
#else
#define RCLCPP_LIFECYCLE_PUBLIC
#define RCLCPP_LIFECYCLE_LOCAL
#endif
#define RCLCPP_LIFECYCLE_PUBLIC_TYPE
#endif
#endif // RCLCPP_LIFECYCLE__VISIBILITY_CONTROL_H_

View File

@@ -0,0 +1,36 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp_lifecycle</name>
<version>0.0.0</version>
<description>Package containing a prototype for lifecycle implementation</description>
<maintainer email="karsten@osrfoundation.org">Karsten Knese</maintainer>
<license>Apache License 2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<build_depend>rclcpp</build_depend>
<build_depend>rcl_lifecycle</build_depend>
<build_depend>rmw_implementation</build_depend>
<build_depend>rmw_implementation_cmake</build_depend>
<build_depend>rosidl_default_generators</build_depend>
<build_depend>std_msgs</build_depend>
<build_depend>lifecycle_msgs</build_depend>
<exec_depend>rclcpp</exec_depend>
<exec_depend>rcl_lifecycle</exec_depend>
<exec_depend>rclpy</exec_depend>
<exec_depend>rmw_implementation</exec_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<exec_depend>std_msgs</exec_depend>
<exec_depend>lifecycle_msgs</exec_depend>
<test_depend>ament_cmake_gtest</test_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>

View File

@@ -0,0 +1,310 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp_lifecycle/lifecycle_node.hpp"
#include <string>
#include <map>
#include <memory>
#include <vector>
#include <utility>
#include "lifecycle_msgs/msg/state.hpp"
#include "lifecycle_msgs/msg/transition.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/graph_listener.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/node_interfaces/node_base.hpp"
#include "rclcpp/node_interfaces/node_graph.hpp"
#include "rclcpp/node_interfaces/node_parameters.hpp"
#include "rclcpp/node_interfaces/node_services.hpp"
#include "rclcpp/node_interfaces/node_timers.hpp"
#include "rclcpp/node_interfaces/node_topics.hpp"
#include "lifecycle_node_interface_impl.hpp" // implementation
namespace rclcpp_lifecycle
{
LifecycleNode::LifecycleNode(const std::string & node_name, bool use_intra_process_comms)
: LifecycleNode(
node_name,
rclcpp::contexts::default_context::get_global_default_context(),
use_intra_process_comms)
{}
LifecycleNode::LifecycleNode(
const std::string & node_name,
rclcpp::context::Context::SharedPtr context,
bool use_intra_process_comms)
: node_base_(new rclcpp::node_interfaces::NodeBase(node_name, context)),
node_graph_(new rclcpp::node_interfaces::NodeGraph(node_base_.get())),
node_timers_(new rclcpp::node_interfaces::NodeTimers(node_base_.get())),
node_topics_(new rclcpp::node_interfaces::NodeTopics(node_base_.get())),
node_services_(new rclcpp::node_interfaces::NodeServices(node_base_.get())),
node_parameters_(new rclcpp::node_interfaces::NodeParameters(
node_topics_.get(),
use_intra_process_comms
)),
use_intra_process_comms_(use_intra_process_comms),
impl_(new LifecycleNodeInterfaceImpl(node_base_, node_services_))
{
impl_->init();
register_on_configure(std::bind(&LifecycleNodeInterface::on_configure, this,
std::placeholders::_1));
register_on_cleanup(std::bind(&LifecycleNodeInterface::on_cleanup, this, std::placeholders::_1));
register_on_shutdown(std::bind(&LifecycleNodeInterface::on_shutdown, this,
std::placeholders::_1));
register_on_activate(std::bind(&LifecycleNodeInterface::on_activate, this,
std::placeholders::_1));
register_on_deactivate(std::bind(&LifecycleNodeInterface::on_deactivate, this,
std::placeholders::_1));
register_on_error(std::bind(&LifecycleNodeInterface::on_error, this, std::placeholders::_1));
}
LifecycleNode::~LifecycleNode()
{}
const char *
LifecycleNode::get_name() const
{
return node_base_->get_name();
}
rclcpp::callback_group::CallbackGroup::SharedPtr
LifecycleNode::create_callback_group(
rclcpp::callback_group::CallbackGroupType group_type)
{
return node_base_->create_callback_group(group_type);
}
bool
LifecycleNode::group_in_node(rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
return node_base_->callback_group_in_node(group);
}
std::vector<rcl_interfaces::msg::SetParametersResult>
LifecycleNode::set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
return node_parameters_->set_parameters(parameters);
}
rcl_interfaces::msg::SetParametersResult
LifecycleNode::set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
return node_parameters_->set_parameters_atomically(parameters);
}
std::vector<rclcpp::parameter::ParameterVariant>
LifecycleNode::get_parameters(
const std::vector<std::string> & names) const
{
return node_parameters_->get_parameters(names);
}
rclcpp::parameter::ParameterVariant
LifecycleNode::get_parameter(const std::string & name) const
{
return node_parameters_->get_parameter(name);
}
bool LifecycleNode::get_parameter(const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const
{
return node_parameters_->get_parameter(name, parameter);
}
std::vector<rcl_interfaces::msg::ParameterDescriptor>
LifecycleNode::describe_parameters(
const std::vector<std::string> & names) const
{
return node_parameters_->describe_parameters(names);
}
std::vector<uint8_t>
LifecycleNode::get_parameter_types(
const std::vector<std::string> & names) const
{
return node_parameters_->get_parameter_types(names);
}
rcl_interfaces::msg::ListParametersResult
LifecycleNode::list_parameters(
const std::vector<std::string> & prefixes, uint64_t depth) const
{
return node_parameters_->list_parameters(prefixes, depth);
}
std::map<std::string, std::string>
LifecycleNode::get_topic_names_and_types() const
{
return node_graph_->get_topic_names_and_types();
}
size_t
LifecycleNode::count_publishers(const std::string & topic_name) const
{
return node_graph_->count_publishers(topic_name);
}
size_t
LifecycleNode::count_subscribers(const std::string & topic_name) const
{
return node_graph_->count_subscribers(topic_name);
}
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
LifecycleNode::get_callback_groups() const
{
return node_base_->get_callback_groups();
}
rclcpp::event::Event::SharedPtr
LifecycleNode::get_graph_event()
{
return node_graph_->get_graph_event();
}
void
LifecycleNode::wait_for_graph_change(
rclcpp::event::Event::SharedPtr event,
std::chrono::nanoseconds timeout)
{
node_graph_->wait_for_graph_change(event, timeout);
}
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
LifecycleNode::get_node_base_interface()
{
return node_base_;
}
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr
LifecycleNode::get_node_graph_interface()
{
return node_graph_;
}
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr
LifecycleNode::get_node_timers_interface()
{
return node_timers_;
}
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr
LifecycleNode::get_node_topics_interface()
{
return node_topics_;
}
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr
LifecycleNode::get_node_services_interface()
{
return node_services_;
}
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr
LifecycleNode::get_node_parameters_interface()
{
return node_parameters_;
}
////
bool
LifecycleNode::register_on_configure(std::function<rcl_lifecycle_ret_t(const State &)> fcn)
{
return impl_->register_callback(lifecycle_msgs::msg::State::TRANSITION_STATE_CONFIGURING, fcn);
}
bool
LifecycleNode::register_on_cleanup(std::function<rcl_lifecycle_ret_t(const State &)> fcn)
{
return impl_->register_callback(lifecycle_msgs::msg::State::TRANSITION_STATE_CLEANINGUP, fcn);
}
bool
LifecycleNode::register_on_shutdown(std::function<rcl_lifecycle_ret_t(const State &)> fcn)
{
return impl_->register_callback(lifecycle_msgs::msg::State::TRANSITION_STATE_SHUTTINGDOWN, fcn);
}
bool
LifecycleNode::register_on_activate(std::function<rcl_lifecycle_ret_t(const State &)> fcn)
{
return impl_->register_callback(lifecycle_msgs::msg::State::TRANSITION_STATE_ACTIVATING, fcn);
}
bool
LifecycleNode::register_on_deactivate(std::function<rcl_lifecycle_ret_t(const State &)> fcn)
{
return impl_->register_callback(lifecycle_msgs::msg::State::TRANSITION_STATE_DEACTIVATING, fcn);
}
bool
LifecycleNode::register_on_error(std::function<rcl_lifecycle_ret_t(const State &)> fcn)
{
return impl_->register_callback(lifecycle_msgs::msg::State::TRANSITION_STATE_ERRORPROCESSING,
fcn);
}
const State &
LifecycleNode::get_current_state()
{
return impl_->get_current_state();
}
std::vector<State>
LifecycleNode::get_available_states()
{
return impl_->get_available_states();
}
std::vector<Transition>
LifecycleNode::get_available_transitions()
{
return impl_->get_available_transitions();
}
const State &
LifecycleNode::trigger_transition(const Transition & transition)
{
return trigger_transition(transition.id());
}
const State &
LifecycleNode::trigger_transition(unsigned int transition_id)
{
return impl_->trigger_transition(transition_id);
}
void
LifecycleNode::add_publisher_handle(
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisherInterface> pub)
{
impl_->add_publisher_handle(pub);
}
void
LifecycleNode::add_timer_handle(std::shared_ptr<rclcpp::timer::TimerBase> timer)
{
impl_->add_timer_handle(timer);
}
} // namespace rclcpp_lifecycle

View File

@@ -0,0 +1,397 @@
// Copyright 2016 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 LIFECYCLE_NODE_INTERFACE_IMPL_HPP_
#define LIFECYCLE_NODE_INTERFACE_IMPL_HPP_
#include "rclcpp_lifecycle/lifecycle_node.hpp"
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <utility>
#include "lifecycle_msgs/msg/transition_description.hpp"
#include "lifecycle_msgs/msg/transition_event.h" // for getting the c-typesupport
#include "lifecycle_msgs/msg/transition_event.hpp"
#include "lifecycle_msgs/srv/change_state.hpp"
#include "lifecycle_msgs/srv/get_state.hpp"
#include "lifecycle_msgs/srv/get_available_states.hpp"
#include "lifecycle_msgs/srv/get_available_transitions.hpp"
#include "rcl/error_handling.h"
#include "rcl_lifecycle/rcl_lifecycle.h"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
namespace rclcpp_lifecycle
{
class LifecycleNode::LifecycleNodeInterfaceImpl
{
using ChangeStateSrv = lifecycle_msgs::srv::ChangeState;
using GetStateSrv = lifecycle_msgs::srv::GetState;
using GetAvailableStatesSrv = lifecycle_msgs::srv::GetAvailableStates;
using GetAvailableTransitionsSrv = lifecycle_msgs::srv::GetAvailableTransitions;
using TransitionEventMsg = lifecycle_msgs::msg::TransitionEvent;
public:
LifecycleNodeInterfaceImpl(
std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> node_base_interface,
std::shared_ptr<rclcpp::node_interfaces::NodeServicesInterface> node_services_interface)
: node_base_interface_(node_base_interface),
node_services_interface_(node_services_interface)
{}
~LifecycleNodeInterfaceImpl()
{
if (rcl_lifecycle_state_machine_is_initialized(&state_machine_) != RCL_RET_OK) {
fprintf(stderr, "%s:%u, FATAL: rcl_state_machine got destroyed externally.\n",
__FILE__, __LINE__);
} else {
rcl_lifecycle_state_machine_fini(&state_machine_,
node_base_interface_->get_rcl_node_handle());
}
}
void
init()
{
state_machine_ = rcl_lifecycle_get_zero_initialized_state_machine();
// The call to initialize the state machine takes
// currently five different typesupports for all publishers/services
// created within the RCL_LIFECYCLE structure.
// The publisher takes a C-Typesupport since the publishing (i.e. creating
// the message) is done fully in RCL.
// Services are handled in C++, so that it needs a C++ typesupport structure.
rcl_ret_t ret = rcl_lifecycle_state_machine_init(
&state_machine_, node_base_interface_->get_rcl_node_handle(),
ROSIDL_GET_TYPE_SUPPORT(lifecycle_msgs, msg, TransitionEvent),
rosidl_typesupport_cpp::get_service_type_support_handle<ChangeStateSrv>(),
rosidl_typesupport_cpp::get_service_type_support_handle<GetStateSrv>(),
rosidl_typesupport_cpp::get_service_type_support_handle<GetAvailableStatesSrv>(),
rosidl_typesupport_cpp::get_service_type_support_handle<GetAvailableTransitionsSrv>(),
true);
if (ret != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Couldn't initialize state machine for node ") + node_base_interface_->get_name());
}
{ // change_state
auto cb = std::bind(&LifecycleNodeInterfaceImpl::on_change_state, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
rclcpp::any_service_callback::AnyServiceCallback<ChangeStateSrv> any_cb;
any_cb.set(cb);
srv_change_state_ = std::make_shared<rclcpp::service::Service<ChangeStateSrv>>(
node_base_interface_->get_shared_rcl_node_handle(),
&state_machine_.com_interface.srv_change_state,
any_cb);
node_services_interface_->add_service(
std::dynamic_pointer_cast<rclcpp::service::ServiceBase>(srv_change_state_),
nullptr);
}
{ // get_state
auto cb = std::bind(&LifecycleNodeInterfaceImpl::on_get_state, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
rclcpp::any_service_callback::AnyServiceCallback<GetStateSrv> any_cb;
any_cb.set(std::move(cb));
srv_get_state_ = std::make_shared<rclcpp::service::Service<GetStateSrv>>(
node_base_interface_->get_shared_rcl_node_handle(),
&state_machine_.com_interface.srv_get_state,
any_cb);
node_services_interface_->add_service(
std::dynamic_pointer_cast<rclcpp::service::ServiceBase>(srv_get_state_),
nullptr);
}
{ // get_available_states
auto cb = std::bind(&LifecycleNodeInterfaceImpl::on_get_available_states, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
rclcpp::any_service_callback::AnyServiceCallback<GetAvailableStatesSrv> any_cb;
any_cb.set(std::move(cb));
srv_get_available_states_ = std::make_shared<rclcpp::service::Service<GetAvailableStatesSrv>>(
node_base_interface_->get_shared_rcl_node_handle(),
&state_machine_.com_interface.srv_get_available_states,
any_cb);
node_services_interface_->add_service(
std::dynamic_pointer_cast<rclcpp::service::ServiceBase>(srv_get_available_states_),
nullptr);
}
{ // get_available_transitions
auto cb = std::bind(&LifecycleNodeInterfaceImpl::on_get_available_transitions, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
rclcpp::any_service_callback::AnyServiceCallback<GetAvailableTransitionsSrv> any_cb;
any_cb.set(std::move(cb));
srv_get_available_transitions_ =
std::make_shared<rclcpp::service::Service<GetAvailableTransitionsSrv>>(
node_base_interface_->get_shared_rcl_node_handle(),
&state_machine_.com_interface.srv_get_available_transitions,
any_cb);
node_services_interface_->add_service(
std::dynamic_pointer_cast<rclcpp::service::ServiceBase>(srv_get_available_transitions_),
nullptr);
}
}
bool
register_callback(std::uint8_t lifecycle_transition, std::function<rcl_lifecycle_ret_t(
const State &)> & cb)
{
cb_map_[lifecycle_transition] = cb;
return true;
}
void
on_change_state(const std::shared_ptr<rmw_request_id_t> header,
const std::shared_ptr<ChangeStateSrv::Request> req,
std::shared_ptr<ChangeStateSrv::Response> resp)
{
(void)header;
if (rcl_lifecycle_state_machine_is_initialized(&state_machine_) != RCL_RET_OK) {
throw std::runtime_error(
"Can't get state. State machine is not initialized.");
}
resp->success = change_state(req->transition.id);
}
void
on_get_state(const std::shared_ptr<rmw_request_id_t> header,
const std::shared_ptr<GetStateSrv::Request> req,
std::shared_ptr<GetStateSrv::Response> resp)
{
(void)header;
(void)req;
if (rcl_lifecycle_state_machine_is_initialized(&state_machine_) != RCL_RET_OK) {
throw std::runtime_error(
"Can't get state. State machine is not initialized.");
}
resp->current_state.id = static_cast<uint8_t>(state_machine_.current_state->id);
resp->current_state.label = state_machine_.current_state->label;
}
void
on_get_available_states(const std::shared_ptr<rmw_request_id_t> header,
const std::shared_ptr<GetAvailableStatesSrv::Request> req,
std::shared_ptr<GetAvailableStatesSrv::Response> resp)
{
(void)header;
(void)req;
if (rcl_lifecycle_state_machine_is_initialized(&state_machine_) != RCL_RET_OK) {
throw std::runtime_error(
"Can't get available states. State machine is not initialized.");
}
for (unsigned int i = 0; i < state_machine_.transition_map.states_size; ++i) {
lifecycle_msgs::msg::State state;
state.id = static_cast<uint8_t>(state_machine_.transition_map.states[i].id);
state.label = static_cast<std::string>(state_machine_.transition_map.states[i].label);
resp->available_states.push_back(state);
}
}
void
on_get_available_transitions(const std::shared_ptr<rmw_request_id_t> header,
const std::shared_ptr<GetAvailableTransitionsSrv::Request> req,
std::shared_ptr<GetAvailableTransitionsSrv::Response> resp)
{
(void)header;
(void)req;
if (rcl_lifecycle_state_machine_is_initialized(&state_machine_) != RCL_RET_OK) {
throw std::runtime_error(
"Can't get available transitions. State machine is not initialized.");
return;
}
for (unsigned int i = 0; i < state_machine_.transition_map.transitions_size; ++i) {
rcl_lifecycle_transition_t & rcl_transition = state_machine_.transition_map.transitions[i];
lifecycle_msgs::msg::TransitionDescription trans_desc;
trans_desc.transition.id = rcl_transition.id;
trans_desc.transition.label = rcl_transition.label;
trans_desc.start_state.id = rcl_transition.start->id;
trans_desc.start_state.label = rcl_transition.start->label;
trans_desc.goal_state.id = rcl_transition.goal->id;
trans_desc.goal_state.label = rcl_transition.goal->label;
resp->available_transitions.push_back(trans_desc);
}
}
const State &
get_current_state()
{
current_state_ = State(state_machine_.current_state);
return current_state_;
}
std::vector<State>
get_available_states()
{
std::vector<State> states;
for (unsigned int i = 0; i < state_machine_.transition_map.states_size; ++i) {
State state(&state_machine_.transition_map.states[i]);
states.push_back(state);
}
return states;
}
std::vector<Transition>
get_available_transitions()
{
std::vector<Transition> transitions;
for (unsigned int i = 0; i < state_machine_.transition_map.transitions_size; ++i) {
Transition transition(
&state_machine_.transition_map.transitions[i]);
transitions.push_back(transition);
}
return transitions;
}
bool
change_state(std::uint8_t lifecycle_transition)
{
if (rcl_lifecycle_state_machine_is_initialized(&state_machine_) != RCL_RET_OK) {
fprintf(stderr, "%s:%d, Unable to change state for state machine for %s: %s \n",
__FILE__, __LINE__, node_base_interface_->get_name(), rcl_get_error_string_safe());
return false;
}
// keep the initial state to pass to a transition callback
State initial_state(state_machine_.current_state);
unsigned int transition_id = static_cast<unsigned int>(lifecycle_transition);
if (rcl_lifecycle_trigger_transition(&state_machine_, transition_id, true) != RCL_RET_OK) {
fprintf(stderr, "%s:%d, Unable to start transition %u from current state %s: %s\n",
__FILE__, __LINE__, transition_id,
state_machine_.current_state->label, rcl_get_error_string_safe());
return false;
}
rcl_lifecycle_ret_t cb_success = execute_callback(
state_machine_.current_state->id, initial_state);
if (rcl_lifecycle_trigger_transition(
&state_machine_, cb_success, true) != RCL_RET_OK)
{
fprintf(stderr, "Failed to finish transition %u. Current state is now: %s\n",
transition_id, state_machine_.current_state->label);
return false;
}
// error handling ?!
// TODO(karsten1987): iterate over possible ret value
if (cb_success == RCL_LIFECYCLE_RET_ERROR) {
rcl_lifecycle_ret_t error_resolved = execute_callback(state_machine_.current_state->id,
initial_state);
if (error_resolved == RCL_RET_OK) {
fprintf(stderr, "Exception handling was successful\n");
// We call cleanup on the error state
rcl_lifecycle_trigger_transition(
&state_machine_, error_resolved, true);
fprintf(stderr, "current state after error callback%s\n",
state_machine_.current_state->label);
} else {
// We call shutdown on the error state
rcl_lifecycle_trigger_transition(
&state_machine_, error_resolved, true);
}
}
// This true holds in both cases where the actual callback
// was successful or not, since at this point we have a valid transistion
// to either a new primary state or error state
return true;
}
rcl_lifecycle_ret_t
execute_callback(unsigned int cb_id, const State & previous_state)
{
// in case no callback was attached, we forward directly
auto cb_success = RCL_LIFECYCLE_RET_OK;
auto it = cb_map_.find(cb_id);
if (it != cb_map_.end()) {
auto callback = it->second;
try {
cb_success = callback(State(previous_state));
} catch (const std::exception &) {
// TODO(karsten1987): Windows CI doens't let me print the msg here
// the todo is to forward the exception to the on_error callback
// fprintf(stderr, "Caught exception in callback for transition %d\n",
// it->first);
// fprintf(stderr, "Original error msg: %s\n", e.what());
// maybe directly go for error handling here
// and pass exception along with it
cb_success = RCL_LIFECYCLE_RET_ERROR;
}
}
return cb_success;
}
const State &
trigger_transition(unsigned int transition_id)
{
change_state(transition_id);
return get_current_state();
}
void
add_publisher_handle(std::shared_ptr<rclcpp_lifecycle::LifecyclePublisherInterface> pub)
{
weak_pubs_.push_back(pub);
}
void
add_timer_handle(std::shared_ptr<rclcpp::timer::TimerBase> timer)
{
weak_timers_.push_back(timer);
}
rcl_lifecycle_state_machine_t state_machine_;
State current_state_;
std::map<
std::uint8_t,
std::function<rcl_lifecycle_ret_t(const State &)>> cb_map_;
using NodeBasePtr = std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface>;
using NodeServicesPtr = std::shared_ptr<rclcpp::node_interfaces::NodeServicesInterface>;
using ChangeStateSrvPtr = std::shared_ptr<rclcpp::service::Service<ChangeStateSrv>>;
using GetStateSrvPtr = std::shared_ptr<rclcpp::service::Service<GetStateSrv>>;
using GetAvailableStatesSrvPtr =
std::shared_ptr<rclcpp::service::Service<GetAvailableStatesSrv>>;
using GetAvailableTransitionsSrvPtr =
std::shared_ptr<rclcpp::service::Service<GetAvailableTransitionsSrv>>;
NodeBasePtr node_base_interface_;
NodeServicesPtr node_services_interface_;
ChangeStateSrvPtr srv_change_state_;
GetStateSrvPtr srv_get_state_;
GetAvailableStatesSrvPtr srv_get_available_states_;
GetAvailableTransitionsSrvPtr srv_get_available_transitions_;
// to controllable things
std::vector<std::weak_ptr<rclcpp_lifecycle::LifecyclePublisherInterface>> weak_pubs_;
std::vector<std::weak_ptr<rclcpp::timer::TimerBase>> weak_timers_;
};
} // namespace rclcpp_lifecycle
#endif // LIFECYCLE_NODE_INTERFACE_IMPL_HPP_

View File

@@ -0,0 +1,58 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp"
namespace rclcpp_lifecycle
{
namespace node_interfaces
{
rcl_lifecycle_ret_t
LifecycleNodeInterface::on_configure(const State &)
{
return RCL_LIFECYCLE_RET_OK;
}
rcl_lifecycle_ret_t
LifecycleNodeInterface::on_cleanup(const State &)
{
return RCL_LIFECYCLE_RET_OK;
}
rcl_lifecycle_ret_t
LifecycleNodeInterface::on_shutdown(const State &)
{
return RCL_LIFECYCLE_RET_OK;
}
rcl_lifecycle_ret_t
LifecycleNodeInterface::on_activate(const State &)
{
return RCL_LIFECYCLE_RET_OK;
}
rcl_lifecycle_ret_t
LifecycleNodeInterface::on_deactivate(const State &)
{
return RCL_LIFECYCLE_RET_OK;
}
rcl_lifecycle_ret_t
LifecycleNodeInterface::on_error(const State &)
{
return RCL_LIFECYCLE_RET_FAILURE;
}
} // namespace node_interfaces
} // namespace rclcpp_lifecycle

View File

@@ -0,0 +1,68 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp_lifecycle/state.hpp"
#include <lifecycle_msgs/msg/state.hpp>
#include <rcl_lifecycle/data_types.h>
#include <string>
namespace rclcpp_lifecycle
{
State::State()
: State(lifecycle_msgs::msg::State::PRIMARY_STATE_UNKNOWN, "unknown")
{}
State::State(unsigned int id, const std::string & label)
: owns_rcl_state_handle_(true)
{
if (label.empty()) {
throw std::runtime_error("Lifecycle State cannot have an empty label.");
}
auto state_handle = new rcl_lifecycle_state_t;
state_handle->id = id;
state_handle->label = label.c_str();
state_handle_ = state_handle;
}
State::State(const rcl_lifecycle_state_t * rcl_lifecycle_state_handle)
: owns_rcl_state_handle_(false)
{
state_handle_ = rcl_lifecycle_state_handle;
}
State::~State()
{
if (owns_rcl_state_handle_) {
delete state_handle_;
}
}
unsigned int
State::id() const
{
return state_handle_->id;
}
std::string
State::label() const
{
return state_handle_->label;
}
} // namespace rclcpp_lifecycle

View File

@@ -0,0 +1,106 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp_lifecycle/transition.hpp"
#include <lifecycle_msgs/msg/transition.hpp>
#include <rcl_lifecycle/data_types.h>
#include <string>
namespace rclcpp_lifecycle
{
Transition::Transition(unsigned int id, const std::string & label)
: owns_rcl_transition_handle_(true)
{
auto transition_handle = new rcl_lifecycle_transition_t;
transition_handle->id = id;
transition_handle->label = label.c_str();
transition_handle->start = nullptr;
transition_handle->goal = nullptr;
transition_handle_ = transition_handle;
}
Transition::Transition(
unsigned int id, const std::string & label,
State && start, State && goal)
: owns_rcl_transition_handle_(true)
{
auto transition_handle = new rcl_lifecycle_transition_t;
transition_handle->id = id;
transition_handle->label = label.c_str();
auto start_state = new rcl_lifecycle_state_t;
start_state->id = start.id();
start_state->label = start.label().c_str();
auto goal_state = new rcl_lifecycle_state_t;
goal_state->id = goal.id();
goal_state->label = start.label().c_str();
transition_handle->start = start_state;
transition_handle->goal = goal_state;
transition_handle_ = transition_handle;
}
Transition::Transition(const rcl_lifecycle_transition_t * rcl_lifecycle_transition_handle)
: owns_rcl_transition_handle_(false)
{
transition_handle_ = rcl_lifecycle_transition_handle;
}
Transition::~Transition()
{
if (owns_rcl_transition_handle_) {
if (transition_handle_->start) {
delete transition_handle_->start;
}
if (transition_handle_->goal) {
delete transition_handle_->goal;
}
delete transition_handle_;
}
}
unsigned int
Transition::id() const
{
return transition_handle_->id;
}
std::string
Transition::label() const
{
return transition_handle_->label;
}
State
Transition::start_state() const
{
return State(
transition_handle_->start->id,
transition_handle_->start->label);
}
State
Transition::goal_state() const
{
return State(
transition_handle_->goal->id,
transition_handle_->goal->label);
}
} // namespace rclcpp_lifecycle

View File

@@ -0,0 +1,102 @@
// Copyright 2015 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <memory>
#include <string>
#include <utility>
#include "lifecycle_msgs/msg/state.hpp"
#include "lifecycle_msgs/msg/transition.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_lifecycle/lifecycle_node.hpp"
using lifecycle_msgs::msg::State;
using lifecycle_msgs::msg::Transition;
class TestCallbackExceptions : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
};
class PositiveCallbackExceptionNode : public rclcpp_lifecycle::LifecycleNode
{
public:
explicit PositiveCallbackExceptionNode(std::string node_name)
: rclcpp_lifecycle::LifecycleNode(std::move(node_name))
{}
size_t number_of_callbacks = 0;
protected:
rcl_lifecycle_ret_t on_configure(const rclcpp_lifecycle::State &)
{
++number_of_callbacks;
throw std::runtime_error("custom exception raised in configure callback");
}
rcl_lifecycle_ret_t on_error(const rclcpp_lifecycle::State &)
{
++number_of_callbacks;
return RCL_LIFECYCLE_RET_OK;
}
};
TEST_F(TestCallbackExceptions, positive_on_error) {
auto test_node = std::make_shared<PositiveCallbackExceptionNode>("testnode");
EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, test_node->get_current_state().id());
EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, test_node->trigger_transition(
rclcpp_lifecycle::Transition(Transition::TRANSITION_CONFIGURE)).id());
// check if all callbacks were successfully overwritten
EXPECT_EQ(static_cast<size_t>(2), test_node->number_of_callbacks);
}
class NegativeCallbackExceptionNode : public rclcpp_lifecycle::LifecycleNode
{
public:
explicit NegativeCallbackExceptionNode(std::string node_name)
: rclcpp_lifecycle::LifecycleNode(std::move(node_name))
{}
size_t number_of_callbacks = 0;
protected:
rcl_lifecycle_ret_t on_configure(const rclcpp_lifecycle::State &)
{
++number_of_callbacks;
throw std::runtime_error("custom exception raised in configure callback");
}
rcl_lifecycle_ret_t on_error(const rclcpp_lifecycle::State &)
{
++number_of_callbacks;
return RCL_LIFECYCLE_RET_FAILURE;
}
};
TEST_F(TestCallbackExceptions, negative_on_error) {
auto test_node = std::make_shared<NegativeCallbackExceptionNode>("testnode");
EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, test_node->get_current_state().id());
EXPECT_EQ(State::PRIMARY_STATE_FINALIZED, test_node->trigger_transition(
rclcpp_lifecycle::Transition(Transition::TRANSITION_CONFIGURE)).id());
// check if all callbacks were successfully overwritten
EXPECT_EQ(static_cast<size_t>(2), test_node->number_of_callbacks);
}

Some files were not shown because too many files have changed in this diff Show More