Compare commits

...

39 Commits

Author SHA1 Message Date
Dirk Thomas
9dd3d4c3c5 0.0.2 2017-06-30 15:11:46 -07:00
William Woodall
9c008267ef add rcutils tag file reference (#341) 2017-06-27 12:02:41 -07:00
Chris Lalancette
b8d72d682a Remove a constructor that we can't test. (#340)
There are currently no paths that lead to it, and it has
a bug anyway; if a large enough value is passed into sec,
then we will overflow sec on the multiply.  Just remove it
since we can't reach the code anyway.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2017-06-21 09:24:01 -04:00
William Woodall
5a99bbdc6e refactor to support multiple types and no_demangle (#339)
* refactor to support multiple types and no_demangle

* add get_service_names_and_types
2017-06-16 18:03:16 -07:00
Karsten Knese
99441d8494 comply with rcl allocator (#336)
* comply with rcl allocator

* do not throw exception in destructor
2017-06-16 14:44:25 -07:00
Mikael Arguedas
756ef6886d use CMAKE_X_STANDARD and check compiler rather than platform 2017-06-16 13:24:30 -07:00
Karsten Knese
f396ff2bac string array takes allocator (#338) 2017-06-14 11:39:22 -07:00
Karsten Knese
2847e4aefd expose explicit transition calls (#334) 2017-06-05 18:24:12 -07:00
Mikael Arguedas
a4f00dc574 include rcutils/time (#333)
* include rcutils/time

* include rcl/time for time source enum
2017-06-05 14:42:36 -07:00
Dirk Thomas
c0a78bac37 Merge pull request #332 from ros2/move_time
use time from rcutils
2017-06-01 22:11:15 -07:00
Dirk Thomas
bfa09fb78c use steady and system time from rcutils 2017-06-01 21:01:38 -07:00
Karsten Knese
454d38776c Topic names (#331)
* expand topic name before invoking count pub/sub

* convenience function for get_namespace()

* uncrustify

* typo

* add get_namespace() test

* add get_namespace() for lifecycle
2017-06-01 14:01:18 -07:00
William Woodall
b90676871d Use namespaces (#328)
* add expand_topic_or_service_name()

* use namespace in intra process

(actual change to topic names in next commit)

* catch and report name issues

for node names, namespaces, and topic/service names

* address comment
2017-05-30 18:25:11 -07:00
Karsten Knese
708903e5df Warn unused (#327)
* comply with unused warnings

* fix flakiness and add test for transitions

* mark flaky test

* duplicate const char * in State constructor

* linters

* correct year in license

* mark flaky test
2017-05-25 19:52:50 -07:00
William Woodall
5777bbee79 add method to reset a timer in TimerBase (#326) 2017-05-10 00:34:52 -07:00
gerkey
100417a98d fix race conditions in guard condition handling (#325) 2017-04-27 18:47:13 -07:00
dhood
675ad04c76 Update spin error message to match function name (#323) 2017-04-25 09:56:01 -07:00
William Woodall
3e6a6d2781 rename {c_}utilities to rcutils (#322) 2017-04-20 11:15:10 -07:00
William Woodall
d2112b294b refactor to pass allocator to some functions in rcl (#321) 2017-04-19 12:37:55 -07:00
Karsten Knese
81b8255e61 use string_array_t from c_utilities package (#320)
* use c_utilities package

* compare return value to utilities_ret_ok

* fix error handling

* better error handling
2017-04-14 16:18:43 -07:00
William Woodall
e6e1848b97 expose node namespace in API and pass to rcl (#316)
* expose node namespace in API and pass to rcl

* name_space -> namespace_
2017-04-08 02:04:41 -07:00
William Woodall
dbe674deb7 compare unsigned to unsigned (#313) 2017-03-22 00:42:37 -07:00
Karsten Knese
c07aee5cf0 Rclcpp time (#311)
* initial commit for rclcpp::time::now()

* switch between times

* introduce time class

* add rclcpp/time.hpp to rclcpp/rclcpp.hpp

* throw exceptions on time error

* fix test_time to catch exceptions

* explicit one-parameter constructor

* fix msvc compiler warnings

* address review comments

* cleanup includes

* re-add todo for fixing test once ros-time is there
2017-03-21 17:41:49 -07:00
William Woodall
2009ca676b add a new Node::get_parameter() with a default value (#309)
* add a new Node::get_parameter() with a default value

* update function parameter name (signature)

* update function parameter name (definition)

* rename new function to get_parameter_or

* rename arg "parameter" to "value" and fix get_parameter

* add set_parameter_if_not_set

* add some comments to clarify logic in set_parameters_atomically

* uncrustify

* address comments

* add some docs for get_parameter*
2017-03-20 17:04:12 -07:00
Mikael Arguedas
71f5b7fe5b Use -Wpedantic (#306)
* add /W4 flag for windows

* use uint8 like defined in messages: fix warning C4244

* fix sign size_t comparison

* add only pedantic, not W4, deal with windows another day

* another sign compare warning
2017-02-27 21:07:57 -08:00
William Woodall
ce146cfdba add a publish method with the const MessageT * signature (#307) 2017-02-27 18:48:01 -08:00
Karsten Knese
dc6b15983a Rosnode list (#304)
* expose list of nodes

* uncrustify

* review comments

* warn for memory leak

* extend year of copyright
2017-01-30 10:30:57 -08:00
Dirk Thomas
4d8b60feb5 Merge pull request #302 from ros2/use_rmw_impl
use rmw implementation
2017-01-09 08:54:27 -08:00
Dirk Thomas
78dfef75b4 remove obsolete CMake functions 2017-01-06 15:46:08 -08:00
dhood
8ca02c9d37 Fix cmake for appending c/cxx flags (#300) 2017-01-06 07:48:47 +10:00
Dirk Thomas
fee3118bdd use rmw implementation 2017-01-05 12:16:12 -08:00
Mikael Arguedas
9603db7b7d dependency order (#301) 2017-01-05 17:27:53 +01:00
Dirk Thomas
e3d4995383 Merge pull request #299 from ros2/single_ts_shortcut
fix dependencies
2016-12-28 13:13:18 -08:00
Dirk Thomas
197c17ae99 fix dependencies 2016-12-28 12:12:05 -08:00
Dirk Thomas
d9673a556d Merge pull request #298 from ros2/typesupport_c_reloaded
use rosidl_typesupport_c
2016-12-28 09:02:43 -08:00
Rohan Agrawal
c0af872c18 Warn on second call of register_param_change_callback (#297) 2016-12-22 13:56:44 -08:00
Dirk Thomas
9e309db793 use rosidl_typesupport_c 2016-12-22 09:47:57 -08:00
William Woodall
321e0b61b0 Doxygen setup (#293)
* basic doxygen configuration

* fix up documentation

* change default value of PROJECT_NUMBER

* more generalization

* fixup

* fixup

* avoid displaying RCLCPP_PUBLIC on all functions

* main page
2016-12-20 18:30:18 -08:00
Dirk Thomas
0515e7aaf2 replace deprecated <CONFIGURATION> with <CONFIG> 2016-12-20 11:38:44 -08:00
65 changed files with 2072 additions and 433 deletions

1
rclcpp/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
doc_output

View File

@@ -3,17 +3,26 @@ cmake_minimum_required(VERSION 3.5)
project(rclcpp)
find_package(ament_cmake REQUIRED)
find_package(builtin_interfaces REQUIRED)
find_package(rcl REQUIRED)
find_package(rcl_interfaces REQUIRED)
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_c REQUIRED)
find_package(rosidl_typesupport_cpp REQUIRED)
# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
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)
# we dont use add_compile_options with pedantic in message packages
# because the Python C extensions dont comply with it
# TODO(mikaelarguedas) change to add_compile_options
# once this is not a message package anymore
# https://github.com/ros2/system_tests/issues/191
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
endif()
include_directories(include)
@@ -28,6 +37,7 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/exceptions.cpp
src/rclcpp/executor.cpp
src/rclcpp/executors.cpp
src/rclcpp/expand_topic_or_service_name.cpp
src/rclcpp/executors/multi_threaded_executor.cpp
src/rclcpp/executors/single_threaded_executor.cpp
src/rclcpp/graph_listener.cpp
@@ -53,35 +63,31 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/utilities.cpp
)
macro(target)
if(NOT target_suffix STREQUAL "")
get_rcl_information("${rmw_implementation}" "rcl${target_suffix}")
endif()
add_library(${PROJECT_NAME}${target_suffix} SHARED
${${PROJECT_NAME}_SRCS})
ament_target_dependencies(${PROJECT_NAME}${target_suffix}
"rcl${target_suffix}"
"rosidl_generator_cpp"
"rosidl_typesupport_cpp")
add_library(${PROJECT_NAME} SHARED
${${PROJECT_NAME}_SRCS})
ament_target_dependencies(${PROJECT_NAME}
"builtin_interfaces"
"rcl"
"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.
target_compile_definitions(${PROJECT_NAME}${target_suffix}
PRIVATE "RCLCPP_BUILDING_LIBRARY")
# Causes the visibility macros to use dllexport rather than dllimport,
# which is appropriate when building the dll but not consuming it.
target_compile_definitions(${PROJECT_NAME}
PRIVATE "RCLCPP_BUILDING_LIBRARY")
install(
TARGETS ${PROJECT_NAME}${target_suffix}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
endmacro()
call_for_each_rmw_implementation(target GENERATE_DEFAULT)
install(
TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
ament_export_dependencies(ament_cmake)
ament_export_dependencies(builtin_interfaces)
ament_export_dependencies(rcl)
ament_export_dependencies(rosidl_generator_cpp)
ament_export_dependencies(rosidl_typesupport_c)
ament_export_dependencies(rosidl_typesupport_cpp)
ament_export_include_directories(include)
@@ -92,6 +98,28 @@ if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
find_package(rmw_implementation_cmake REQUIRED)
ament_add_gtest(test_client test/test_client.cpp)
if(TARGET test_client)
target_include_directories(test_client PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_client ${PROJECT_NAME})
endif()
ament_add_gtest(test_expand_topic_or_service_name test/test_expand_topic_or_service_name.cpp)
if(TARGET test_expand_topic_or_service_name)
target_include_directories(test_expand_topic_or_service_name PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_expand_topic_or_service_name ${PROJECT_NAME})
endif()
ament_add_gtest(test_function_traits test/test_function_traits.cpp)
if(TARGET test_function_traits)
target_include_directories(test_function_traits PUBLIC
@@ -121,6 +149,26 @@ if(BUILD_TESTING)
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
endif()
ament_add_gtest(test_node test/test_node.cpp)
if(TARGET test_node)
target_include_directories(test_node PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_node ${PROJECT_NAME})
endif()
ament_add_gtest(test_publisher test/test_publisher.cpp)
if(TARGET test_publisher)
target_include_directories(test_publisher PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_publisher ${PROJECT_NAME})
endif()
ament_add_gtest(test_rate test/test_rate.cpp
ENV RCL_ASSERT_RMW_ID_MATCHES=${rmw_implementation})
if(TARGET test_rate)
@@ -134,6 +182,26 @@ if(BUILD_TESTING)
${PROJECT_NAME}
)
endif()
ament_add_gtest(test_service test/test_service.cpp)
if(TARGET test_service)
target_include_directories(test_service PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_service ${PROJECT_NAME})
endif()
ament_add_gtest(test_subscription test/test_subscription.cpp)
if(TARGET test_subscription)
target_include_directories(test_subscription PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_subscription ${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
@@ -155,7 +223,7 @@ if(BUILD_TESTING)
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
if(WIN32)
set(append_library_dirs "${append_library_dirs}/$<CONFIGURATION>")
set(append_library_dirs "${append_library_dirs}/$<CONFIG>")
endif()
ament_add_gtest(test_externally_defined_services test/test_externally_defined_services.cpp
@@ -174,6 +242,14 @@ if(BUILD_TESTING)
mock_msgs ${typesupport_impl_c})
endforeach()
endif()
ament_add_gtest(test_time test/test_time.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_time)
ament_target_dependencies(test_time
"rcl")
target_link_libraries(test_time ${PROJECT_NAME})
endif()
endif()
ament_package(

28
rclcpp/Doxyfile Normal file
View File

@@ -0,0 +1,28 @@
# All settings not listed here will use the Doxygen default values.
PROJECT_NAME = "rclcpp"
PROJECT_NUMBER = master
PROJECT_BRIEF = "C++ ROS Client Library API"
INPUT = ./include
RECURSIVE = YES
OUTPUT_DIRECTORY = doc_output
EXTRACT_ALL = YES
SORT_MEMBER_DOCS = NO
GENERATE_LATEX = NO
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
PREDEFINED = RCLCPP_PUBLIC=
# Tag files that do not exist will produce a warning and cross-project linking will not work.
TAGFILES += "../../../../doxygen_tag_files/cppreference-doxygen-web.tag.xml=http://en.cppreference.com/w/"
# Consider changing "latest" to the version you want to reference (e.g. beta1 or 1.0.0)
TAGFILES += "../../../../doxygen_tag_files/rcl.tag=http://docs.ros2.org/latest/api/rcl/"
TAGFILES += "../../../../doxygen_tag_files/rmw.tag=http://docs.ros2.org/latest/api/rmw/"
TAGFILES += "../../../../doxygen_tag_files/rcutils.tag=http://docs.ros2.org/latest/api/rcutils/"
# Uncomment to generate tag files for cross-project linking.
#GENERATE_TAGFILE = "../../../../doxygen_tag_files/rclcpp.tag"

View File

@@ -1,93 +0,0 @@
# Copyright 2015 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Get all information about rclcpp for a specific RMW implementation.
#
# It sets the common variables _DEFINITIONS, _INCLUDE_DIRS and _LIBRARIES
# with the given prefix.
#
# :param rmw_implementation: the RMW implementation name
# :type rmw_implementation: string
# :param var_prefix: the prefix of all output variable names
# :type var_prefix: string
#
macro(get_rclcpp_information rmw_implementation var_prefix)
# pretend to be a "package"
# so that the variables can be used by various functions / macros
set(${var_prefix}_FOUND TRUE)
# Get rcl using the existing macro
if(NOT target_suffix STREQUAL "")
get_rcl_information("${rmw_implementation}" "rcl${target_suffix}")
endif()
# include directories
normalize_path(${var_prefix}_INCLUDE_DIRS
"${rclcpp_DIR}/../../../include")
# libraries
set(_libs)
# search for library relative to this CMake file
set(_library_target "rclcpp")
get_available_rmw_implementations(_rmw_impls)
list(LENGTH _rmw_impls _rmw_impls_length)
if(_rmw_impls_length GREATER 1)
set(_library_target "${_library_target}__${rmw_implementation}")
endif()
set(_lib "NOTFOUND")
find_library(
_lib NAMES "${_library_target}"
PATHS "${rclcpp_DIR}/../../../lib"
NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH
)
if(NOT _lib)
# warn about not existing library and ignore it
message(WARNING "Package 'rclcpp' doesn't contain the library '${_library_target}'")
elseif(NOT IS_ABSOLUTE "${_lib}")
# the found library must be an absolute path
message(FATAL_ERROR "Package 'rclcpp' found the library '${_library_target}' at '${_lib}' which is not an absolute path")
elseif(NOT EXISTS "${_lib}")
# the found library must exist
message(FATAL_ERROR "Package 'rclcpp' found the library '${_lib}' which doesn't exist")
else()
list(APPEND _libs "${_lib}")
endif()
# dependencies
set(_exported_dependencies
"rcl_interfaces"
"rcl${target_suffix}"
"rosidl_generator_cpp"
"rosidl_typesupport_cpp")
set(${var_prefix}_DEFINITIONS)
foreach(_dep ${_exported_dependencies})
if(NOT ${_dep}_FOUND)
find_package("${_dep}" QUIET REQUIRED)
endif()
if(${_dep}_DEFINITIONS)
list_append_unique(${var_prefix}_DEFINITIONS "${${_dep}_DEFINITIONS}")
endif()
if(${_dep}_INCLUDE_DIRS)
list_append_unique(${var_prefix}_INCLUDE_DIRS "${${_dep}_INCLUDE_DIRS}")
endif()
if(${_dep}_LIBRARIES)
list(APPEND _libs "${${_dep}_LIBRARIES}")
endif()
endforeach()
if(_libs)
ament_libraries_deduplicate(_libs "${_libs}")
endif()
set(${var_prefix}_LIBRARIES "${_libs}")
endmacro()

View File

@@ -27,11 +27,13 @@
#include "rcl/error_handling.h"
#include "rcl/wait.h"
#include "rclcpp/exceptions.hpp"
#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/expand_topic_or_service_name.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/error_handling.h"
@@ -138,14 +140,24 @@ public:
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(),
service_type_support_handle, service_name.c_str(), &client_options) != RCL_RET_OK)
{
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("could not create client: ") +
rcl_get_error_string_safe());
// *INDENT-ON*
rcl_ret_t ret = rcl_client_init(
&client_handle_,
this->get_rcl_node_handle(),
service_type_support_handle,
service_name.c_str(),
&client_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_SERVICE_NAME_INVALID) {
auto rcl_node_handle = this->get_rcl_node_handle();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
service_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
true);
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create client");
}
}
@@ -153,7 +165,8 @@ public:
{
if (rcl_client_fini(&client_handle_, this->get_rcl_node_handle()) != RCL_RET_OK) {
fprintf(stderr,
"Error in destruction of rmw client handle: %s\n", rmw_get_error_string_safe());
"Error in destruction of rcl client handle: %s\n", rcl_get_error_string_safe());
rcl_reset_error();
}
}
@@ -212,11 +225,9 @@ public:
{
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
int64_t sequence_number;
if (RCL_RET_OK != rcl_send_request(get_client_handle(), request.get(), &sequence_number)) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("failed to send request: ") + rcl_get_error_string_safe());
// *INDENT-ON*
rcl_ret_t ret = rcl_send_request(get_client_handle(), request.get(), &sequence_number);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send request");
}
SharedPromise call_promise = std::make_shared<Promise>();

View File

@@ -35,18 +35,88 @@ public:
: std::runtime_error("node is invalid") {}
};
/// Thrown when a any kind of name (node, namespace, topic, etc.) is invalid.
class NameValidationError : public std::invalid_argument
{
public:
NameValidationError(
const char * name_type_,
const char * name_,
const char * error_msg_,
size_t invalid_index_)
: std::invalid_argument(format_error(name_type_, name_, error_msg_, invalid_index_)),
name_type(name_type_), name(name_), error_msg(error_msg_), invalid_index(invalid_index_)
{}
static std::string
format_error(
const char * name_type,
const char * name,
const char * error_msg,
size_t invalid_index);
const std::string name_type;
const std::string name;
const std::string error_msg;
const size_t invalid_index;
};
/// Thrown when a node name is invalid.
class InvalidNodeNameError : public NameValidationError
{
public:
InvalidNodeNameError(const char * node_name, const char * error_msg, size_t invalid_index)
: NameValidationError("node name", node_name, error_msg, invalid_index)
{}
};
/// Thrown when a node namespace is invalid.
class InvalidNamespaceError : public NameValidationError
{
public:
InvalidNamespaceError(const char * namespace_, const char * error_msg, size_t invalid_index)
: NameValidationError("namespace", namespace_, error_msg, invalid_index)
{}
};
/// Thrown when a topic name is invalid.
class InvalidTopicNameError : public NameValidationError
{
public:
InvalidTopicNameError(const char * namespace_, const char * error_msg, size_t invalid_index)
: NameValidationError("topic name", namespace_, error_msg, invalid_index)
{}
};
/// Thrown when a service name is invalid.
class InvalidServiceNameError : public NameValidationError
{
public:
InvalidServiceNameError(const char * namespace_, const char * error_msg, size_t invalid_index)
: NameValidationError("service name", namespace_, error_msg, invalid_index)
{}
};
/// Throw a C++ std::exception which was created based on an rcl error.
/*
/**
* Passing nullptr for reset_error is safe and will avoid calling any function
* to reset the error.
*
* \param ret the return code for the current error state
* \param prefix string to prefix to the error if applicable (not all errors have custom messages)
* \param reset_error if true rcl_reset_error() is called before returning
* \param error_state error state to create exception from, if nullptr rcl_get_error_state is used
* \param reset_error function to be called before throwing which whill clear the error state
* \throws std::invalid_argument if ret is RCL_RET_OK
* \throws std::runtime_error if the rcl_get_error_state returns 0
* \throws RCLErrorBase some child class exception based on ret
*/
RCLCPP_PUBLIC
void
throw_from_rcl_error(rcl_ret_t ret, const std::string & prefix = "", bool reset_error = true);
throw_from_rcl_error(
rcl_ret_t ret,
const std::string & prefix = "",
const rcl_error_state_t * error_state = nullptr,
void (* reset_error)() = rcl_reset_error);
class RCLErrorBase
{

View File

@@ -202,14 +202,13 @@ public:
/// Spin (blocking) until the future is complete, it times out waiting, or rclcpp is interrupted.
/**
* \param[in] executor The executor which will spin the node.
* \param[in] node_ptr The node to spin.
* \param[in] future The future to wait on. If SUCCESS, the future is safe to access after this
function.
* function.
* \param[in] timeout Optional timeout parameter, which gets passed to Executor::spin_node_once.
-1 is block forever, 0 is non-blocking.
If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return code.
* \return The return code, one of SUCCESS, INTERRUPTED, or TIMEOUT.
* `-1` is block forever, `0` is non-blocking.
* If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return
* code.
* \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
*/
template<typename ResponseT, typename TimeT = std::milli>
FutureReturnCode

View File

@@ -28,7 +28,7 @@ namespace rclcpp
{
/// Create a default single-threaded executor and execute any immediately available work.
// \param[in] node_ptr Shared pointer to the node to spin.
/** \param[in] node_ptr Shared pointer to the node to spin. */
RCLCPP_PUBLIC
void
spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
@@ -38,7 +38,7 @@ 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.
/** \param[in] node_ptr Shared pointer to the node to spin. */
RCLCPP_PUBLIC
void
spin(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
@@ -57,11 +57,13 @@ using rclcpp::executors::single_threaded_executor::SingleThreadedExecutor;
/**
* \param[in] executor The executor which will spin the node.
* \param[in] node_ptr The node to spin.
* \param[in] future The future to wait on. If SUCCESS, the future is safe to access after this function
* \param[in] timeout Optional timeout parameter, which gets passed to Executor::spin_node_once.
-1 is block forever, 0 is non-blocking.
If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return code.
* \return The return code, one of SUCCESS, INTERRUPTED, or TIMEOUT.
* \param[in] future The future to wait on. If `SUCCESS`, the future is safe to
* access after this function
* \param[in] timeout Optional timeout parameter, which gets passed to
* Executor::spin_node_once.
* `-1` is block forever, `0` is non-blocking.
* If the time spent inside the blocking loop exceeds this timeout, return a `TIMEOUT` return code.
* \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
*/
template<typename ResponseT, typename TimeT = std::milli>
rclcpp::executor::FutureReturnCode

View File

@@ -0,0 +1,63 @@
// Copyright 2017 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__EXPAND_TOPIC_OR_SERVICE_NAME_HPP_
#define RCLCPP__EXPAND_TOPIC_OR_SERVICE_NAME_HPP_
#include <string>
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// Expand a topic or service name and throw if it is not valid.
/**
* This function can be used to "just" validate a topic or service name too,
* since expanding the topic name is required to fully validate a name.
*
* If the name is invalid, then InvalidTopicNameError is thrown or
* InvalidServiceNameError if is_service is true.
*
* This function can take any form of a topic or service name, i.e. it does not
* have to be a fully qualified name.
* The node name and namespace are used to expand it if necessary while
* validating it.
*
* Expansion is done with rcl_expand_topic_name.
* The validation is doen with rcl_validate_topic_name and
* rmw_validate_full_topic_name, so details about failures can be found in the
* documentation for those functions.
*
* \param name the topic or service name to be validated
* \param node_name the name of the node associated with the name
* \param namespace_ the namespace of the node associated with the name
* \param is_service if true InvalidServiceNameError is thrown instead
* \returns expanded (and validated) topic name
* \throws InvalidTopicNameError if name is invalid and is_service is false
* \throws InvalidServiceNameError if name is invalid and is_service is true
* \throws std::bad_alloc if memory cannot be allocated
* \throws RCLError if an unexpect error occurs
*/
RCLCPP_PUBLIC
std::string
expand_topic_or_service_name(
const std::string & name,
const std::string & node_name,
const std::string & namespace_,
bool is_service = false);
} // namespace rclcpp
#endif // RCLCPP__EXPAND_TOPIC_OR_SERVICE_NAME_HPP_

View File

@@ -68,7 +68,8 @@ public:
virtual ~GraphListener();
/// Start the graph listener's listen thread if it hasn't been started.
/* This function is thread-safe.
/**
* This function is thread-safe.
*
* \throws GraphListenerShutdownError if the GraphListener is shutdown
*/
@@ -78,7 +79,7 @@ public:
start_if_not_started();
/// Add a node to the graph listener's list of nodes.
/*
/**
* \throws GraphListenerShutdownError if the GraphListener is shutdown
* \throws NodeAlreadyAddedError if the given node is already in the list
* \throws std::invalid_argument if node is nullptr
@@ -90,7 +91,8 @@ public:
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.
/**
* Also return false if given nullptr.
*
* \throws std::system_error anything std::mutex::lock() throws
*/
@@ -100,7 +102,8 @@ public:
has_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph);
/// Remove a node from the graph listener's list of nodes.
/*
/**
*
* \throws NodeNotFoundError if the given node is not in the list
* \throws std::invalid_argument if node is nullptr
* \throws std::system_error anything std::mutex::lock() throws
@@ -111,7 +114,8 @@ public:
remove_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph);
/// Stop the listening thread.
/* The thread cannot be restarted, and the class is defunct after calling.
/**
* The thread cannot be restarted, and the class is defunct after calling.
* This function is called by the ~GraphListener() and does nothing if
* shutdown() was already called.
* This function exists separately from the ~GraphListener() so that it can

View File

@@ -40,7 +40,8 @@ namespace intra_process_manager
{
/// This class facilitates intra process communication between nodes.
/* This class is used in the creation of publishers and subscriptions.
/**
* This class is used in the creation of publishers and subscriptions.
* A singleton instance of this class is owned by a rclcpp::Context and a
* rclcpp::Node can use an associated Context to get an instance of this class.
* Nodes which do not have a common Context will not exchange intra process
@@ -54,7 +55,7 @@ namespace intra_process_manager
* When a publisher is created, it advertises on the topic the user provided,
* as well as a "shadowing" topic of type rcl_interfaces/IntraProcessMessage.
* For instance, if the user specified the topic '/namespace/chatter', then the
* corresponding intra process topic might be '/namespace/chatter__intra'.
* corresponding intra process topic might be '/namespace/chatter/_intra'.
* The publisher is also allocated an id which is unique among all publishers
* and subscriptions in this process.
* Additionally, when registered with this class a ring buffer is created and
@@ -133,7 +134,8 @@ public:
virtual ~IntraProcessManager();
/// Register a subscription with the manager, returns subscriptions unique id.
/* In addition to generating a unique intra process id for the subscription,
/**
* In addition to generating a unique intra process id for the subscription,
* this method also stores the topic name of the subscription.
*
* This method is normally called during the creation of a subscription,
@@ -149,7 +151,8 @@ public:
add_subscription(subscription::SubscriptionBase::SharedPtr subscription);
/// Unregister a subscription using the subscription's unique id.
/* This method does not allocate memory.
/**
* This method does not allocate memory.
*
* \param intra_process_subscription_id id of the subscription to remove.
*/
@@ -158,7 +161,8 @@ public:
remove_subscription(uint64_t intra_process_subscription_id);
/// Register a publisher with the manager, returns the publisher unique id.
/* In addition to generating and returning a unique id for the publisher,
/**
* In addition to generating and returning a unique id for the publisher,
* this method creates internal ring buffer storage for "in-flight" intra
* process messages which are stored when store_intra_process_message is
* called with this publisher's unique id.
@@ -194,7 +198,8 @@ public:
}
/// Unregister a publisher using the publisher's unique id.
/* This method does not allocate memory.
/**
* This method does not allocate memory.
*
* \param intra_process_publisher_id id of the publisher to remove.
*/
@@ -203,7 +208,8 @@ public:
remove_publisher(uint64_t intra_process_publisher_id);
/// Store a message in the manager, and return the message sequence number.
/* The given message is stored in internal storage using the given publisher
/**
* The given message is stored in internal storage using the given publisher
* id and the newly generated message sequence, which is also returned.
* The combination of publisher id and message sequence number can later
* be used with a subscription id to retrieve the message by calling
@@ -261,7 +267,8 @@ public:
}
/// Take an intra process message.
/* The intra_process_publisher_id and message_sequence_number parameters
/**
* The intra_process_publisher_id and message_sequence_number parameters
* uniquely identify a message instance, which should be taken.
*
* The requesting_subscriptions_intra_process_id parameter is used to make

View File

@@ -18,7 +18,8 @@
#ifndef RCLCPP__MACROS_HPP_
#define RCLCPP__MACROS_HPP_
/* Disables the copy constructor and operator= for the given class.
/**
* Disables the copy constructor and operator= for the given class.
*
* Use in the private section of the class.
*/
@@ -26,38 +27,41 @@
__VA_ARGS__(const __VA_ARGS__ &) = delete; \
__VA_ARGS__ & operator=(const __VA_ARGS__ &) = delete;
/* Defines aliases and static functions for using the Class with smart pointers.
/**
* Defines aliases and static functions for using the Class with smart pointers.
*
* Use in the public section of the class.
* Make sure to include <memory> in the header when using this.
* Make sure to include `<memory>` in the header when using this.
*/
#define RCLCPP_SMART_PTR_DEFINITIONS(...) \
RCLCPP_SHARED_PTR_DEFINITIONS(__VA_ARGS__) \
RCLCPP_WEAK_PTR_DEFINITIONS(__VA_ARGS__) \
RCLCPP_UNIQUE_PTR_DEFINITIONS(__VA_ARGS__)
/* Defines aliases and static functions for using the Class with smart pointers.
/**
* Defines aliases and static functions for using the Class with smart pointers.
*
* Same as RCLCPP_SMART_PTR_DEFINITIONS expect it excludes the static
* Class::make_unique() method definition which does not work on classes which
* are not CopyConstructable.
*
* Use in the public section of the class.
* Make sure to include <memory> in the header when using this.
* Make sure to include `<memory>` in the header when using this.
*/
#define RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(...) \
RCLCPP_SHARED_PTR_DEFINITIONS(__VA_ARGS__) \
RCLCPP_WEAK_PTR_DEFINITIONS(__VA_ARGS__) \
__RCLCPP_UNIQUE_PTR_ALIAS(__VA_ARGS__)
/* Defines aliases only for using the Class with smart pointers.
/**
* 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.
* Make sure to include `<memory>` in the header when using this.
*/
#define RCLCPP_SMART_PTR_ALIASES_ONLY(...) \
__RCLCPP_SHARED_PTR_ALIAS(__VA_ARGS__) \

View File

@@ -39,7 +39,8 @@ public:
};
/// Ring buffer container of unique_ptr's of T, which can be accessed by a key.
/* T must be a CopyConstructable and CopyAssignable.
/**
* T must be a CopyConstructable and CopyAssignable.
* This class can be used in a container by using the base class MappedRingBufferBase.
* This class must have a positive, non-zero size.
* This class cannot be resized nor can it reserve additional space after construction.
@@ -66,9 +67,11 @@ public:
using ElemUniquePtr = std::unique_ptr<T, ElemDeleter>;
/// Constructor.
/* The constructor will allocate memory while reserving space.
/**
* The constructor will allocate memory while reserving space.
*
* \param size size of the ring buffer; must be positive and non-zero.
* \param allocator optional custom allocator
*/
explicit MappedRingBuffer(size_t size, std::shared_ptr<Alloc> allocator = nullptr)
: elements_(size), head_(0)
@@ -86,7 +89,8 @@ public:
virtual ~MappedRingBuffer() {}
/// Return a copy of the value stored in the ring buffer at the given key.
/* The key is matched if an element in the ring buffer has a matching key.
/**
* The key is matched if an element in the ring buffer has a matching key.
* This method will allocate in order to return a copy.
*
* The key is not guaranteed to be unique, see the class docs for more.
@@ -110,7 +114,8 @@ public:
}
/// Return ownership of the value stored in the ring buffer, leaving a copy.
/* The key is matched if an element in the ring bufer has a matching key.
/**
* The key is matched if an element in the ring bufer has a matching key.
* This method will allocate in order to store a copy.
*
* The key is not guaranteed to be unique, see the class docs for more.
@@ -147,7 +152,8 @@ public:
}
/// Return ownership of the value stored in the ring buffer at the given key.
/* The key is matched if an element in the ring buffer has a matching key.
/**
* The key is matched if an element in the ring buffer has a matching key.
*
* The key is not guaranteed to be unique, see the class docs for more.
*
@@ -169,7 +175,8 @@ public:
}
/// Insert a key-value pair, displacing an existing pair if necessary.
/* The key's uniqueness is not checked on insertion.
/**
* The key's uniqueness is not checked on insertion.
* It is up to the user to ensure the key is unique.
* This method should not allocate memory.
*

View File

@@ -28,7 +28,7 @@ namespace message_memory_strategy
{
/// Default allocation strategy for messages received by subscriptions.
// A message memory strategy must be templated on the type of the subscription it belongs to.
/** A message memory strategy must be templated on the type of the subscription it belongs to. */
template<typename MessageT, typename Alloc = std::allocator<void>>
class MessageMemoryStrategy
{
@@ -56,14 +56,14 @@ public:
}
/// By default, dynamically allocate a new message.
// \return Shared pointer to the new message.
/** \return Shared pointer to the new message. */
virtual std::shared_ptr<MessageT> borrow_message()
{
return std::allocate_shared<MessageT, MessageAlloc>(*message_allocator_.get());
}
/// Release ownership of the message, which will deallocate it if it has no more owners.
// \param[in] Shared pointer to the message we are returning.
/** \param[in] msg Shared pointer to the message we are returning. */
virtual void return_message(std::shared_ptr<MessageT> & msg)
{
msg.reset();

View File

@@ -67,33 +67,46 @@ public:
/// Create a new node with the specified name.
/**
* \param[in] node_name Name of the node.
* \param[in] namespace_ Namespace 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_PUBLIC
explicit Node(const std::string & node_name, bool use_intra_process_comms = false);
explicit Node(
const std::string & node_name,
const std::string & namespace_ = "",
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] namespace_ Namespace 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_PUBLIC
Node(
const std::string & node_name, rclcpp::context::Context::SharedPtr context,
const std::string & node_name,
const std::string & namespace_,
rclcpp::context::Context::SharedPtr context,
bool use_intra_process_comms = false);
RCLCPP_PUBLIC
virtual ~Node();
/// Get the name of the node.
// \return The name of the node.
/** \return The name of the node. */
RCLCPP_PUBLIC
const char *
get_name() const;
/// Get the namespace of the node.
/** \return The namespace of the node. */
RCLCPP_PUBLIC
const char *
get_namespace() const;
/// Create and return a callback group.
RCLCPP_PUBLIC
rclcpp::callback_group::CallbackGroup::SharedPtr
@@ -108,6 +121,7 @@ public:
/**
* \param[in] topic_name The topic for this publisher to publish on.
* \param[in] qos_history_depth The depth of the publisher message queue.
* \param[in] allocator Optional custom allocator.
* \return Shared pointer to the created publisher.
*/
template<
@@ -122,6 +136,7 @@ public:
/**
* \param[in] topic_name The topic for this publisher to publish on.
* \param[in] qos_profile The quality of service profile to pass on to the rmw implementation.
* \param[in] allocator Optional custom allocator.
* \return Shared pointer to the created publisher.
*/
template<
@@ -141,6 +156,7 @@ public:
* \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.
* \param[in] allocator Optional custom allocator.
* \return Shared pointer to the created subscription.
*/
/* TODO(jacquelinekay):
@@ -171,6 +187,7 @@ public:
* \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.
* \param[in] allocator Optional custom allocator.
* \return Shared pointer to the created subscription.
*/
/* TODO(jacquelinekay):
@@ -231,6 +248,12 @@ public:
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
template<typename ParameterT>
void
set_parameter_if_not_set(
const std::string & name,
const ParameterT & value);
RCLCPP_PUBLIC
std::vector<rclcpp::parameter::ParameterVariant>
get_parameters(const std::vector<std::string> & names) const;
@@ -245,10 +268,36 @@ public:
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const;
/// Assign the value of the parameter if set into the parameter argument.
/**
* If the parameter was not set, then the "parameter" argument is never assigned a value.
*
* \param[in] name The name of the parameter to get.
* \param[out] parameter The output where the value of the parameter should be assigned.
* \returns true if the parameter was set, false otherwise
*/
template<typename ParameterT>
bool
get_parameter(const std::string & name, ParameterT & parameter) const;
/// Get the parameter value, or the "alternative value" if not set, and assign it to "value".
/**
* If the parameter was not set, then the "value" argument is assigned
* the "alternative_value".
* In all cases, the parameter remains not set after this function is called.
*
* \param[in] name The name of the parameter to get.
* \param[out] value The output where the value of the parameter should be assigned.
* \param[in] alternative_value Value to be stored in output if the parameter was not set.
* \returns true if the parameter was set, false otherwise
*/
template<typename ParameterT>
bool
get_parameter_or(
const std::string & name,
ParameterT & value,
const ParameterT & alternative_value) const;
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::ParameterDescriptor>
describe_parameters(const std::vector<std::string> & names) const;
@@ -263,7 +312,8 @@ public:
/// Register the callback for parameter changes
/**
* \param[in] User defined callback function, It is expected to atomically set parameters.
* \param[in] callback User defined callback function.
* It is expected to atomically set parameters.
* \note Repeated invocations of this function will overwrite previous callbacks
*/
template<typename CallbackT>
@@ -271,9 +321,13 @@ public:
register_param_change_callback(CallbackT && callback);
RCLCPP_PUBLIC
std::map<std::string, std::string>
std::map<std::string, std::vector<std::string>>
get_topic_names_and_types() const;
RCLCPP_PUBLIC
std::map<std::string, std::vector<std::string>>
get_service_names_and_types() const;
RCLCPP_PUBLIC
size_t
count_publishers(const std::string & topic_name) const;
@@ -292,7 +346,8 @@ public:
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.
/**
* 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

View File

@@ -208,16 +208,46 @@ Node::register_param_change_callback(CallbackT && callback)
}
template<typename ParameterT>
bool
Node::get_parameter(const std::string & name, ParameterT & parameter) const
void
Node::set_parameter_if_not_set(
const std::string & name,
const ParameterT & value)
{
rclcpp::parameter::ParameterVariant parameter_variant(name, parameter);
rclcpp::parameter::ParameterVariant parameter_variant;
if (!this->get_parameter(name, parameter_variant)) {
this->set_parameters({
rclcpp::parameter::ParameterVariant(name, value),
});
}
}
template<typename ParameterT>
bool
Node::get_parameter(const std::string & name, ParameterT & value) const
{
rclcpp::parameter::ParameterVariant parameter_variant;
bool result = get_parameter(name, parameter_variant);
parameter = parameter_variant.get_value<ParameterT>();
if (result) {
value = parameter_variant.get_value<ParameterT>();
}
return result;
}
template<typename ParameterT>
bool
Node::get_parameter_or(
const std::string & name,
ParameterT & value,
const ParameterT & alternative_value) const
{
bool got_parameter = get_parameter(name, value);
if (!got_parameter) {
value = alternative_value;
}
return got_parameter;
}
} // namespace node
} // namespace rclcpp

View File

@@ -36,7 +36,10 @@ public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBaseInterface)
RCLCPP_PUBLIC
NodeBase(const std::string & node_name, rclcpp::context::Context::SharedPtr context);
NodeBase(
const std::string & node_name,
const std::string & namespace_,
rclcpp::context::Context::SharedPtr context);
RCLCPP_PUBLIC
virtual
@@ -47,6 +50,11 @@ public:
const char *
get_name() const;
RCLCPP_PUBLIC
virtual
const char *
get_namespace() const;
RCLCPP_PUBLIC
virtual
rclcpp::context::Context::SharedPtr

View File

@@ -39,14 +39,21 @@ public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBaseInterface)
/// Return the name of the node.
// \return The name of the node.
/** \return The name of the node. */
RCLCPP_PUBLIC
virtual
const char *
get_name() const = 0;
/// Return the namespace of the node.
/** \return The namespace of the node. */
RCLCPP_PUBLIC
virtual
const char *
get_namespace() const = 0;
/// Return the context of the node.
// \return SharedPtr to the node's context.
/** \return SharedPtr to the node's context. */
RCLCPP_PUBLIC
virtual
rclcpp::context::Context::SharedPtr
@@ -65,7 +72,8 @@ public:
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.
/**
* 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
@@ -74,7 +82,8 @@ public:
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.
/**
* 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
@@ -113,7 +122,8 @@ public:
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.
/**
* 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
*/
@@ -123,7 +133,7 @@ public:
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. */
/** This should be used when triggering the notify guard condition. */
RCLCPP_PUBLIC
virtual
std::unique_lock<std::recursive_mutex>

View File

@@ -1,4 +1,4 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
// Copyright 2016-2017 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.
@@ -56,8 +56,18 @@ public:
RCLCPP_PUBLIC
virtual
std::map<std::string, std::string>
get_topic_names_and_types() const;
std::map<std::string, std::vector<std::string>>
get_topic_names_and_types(bool no_demangle = false) const;
RCLCPP_PUBLIC
virtual
std::map<std::string, std::vector<std::string>>
get_service_names_and_types() const;
RCLCPP_PUBLIC
virtual
std::vector<std::string>
get_node_names() const;
RCLCPP_PUBLIC
virtual
@@ -119,7 +129,7 @@ private:
/// 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_ */
/** graph_users_count_ is atomic so that it can be accessed without acquiring the graph_mutex_ */
std::atomic_size_t graph_users_count_;
};

View File

@@ -1,4 +1,4 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
// Copyright 2016-2017 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.
@@ -18,6 +18,7 @@
#include <chrono>
#include <map>
#include <string>
#include <vector>
#include "rcl/guard_condition.h"
@@ -36,14 +37,34 @@ 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
/// Return a map of existing topic names to list of topic types.
/**
* 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.
*
* \param[in] no_demangle if true, topic names and types are not demangled
*/
RCLCPP_PUBLIC
virtual
std::map<std::string, std::string>
get_topic_names_and_types() const = 0;
std::map<std::string, std::vector<std::string>>
get_topic_names_and_types(bool no_demangle = false) const = 0;
/// Return a map of existing service names to list of service types.
/**
* A service is considered to exist when at least one service server or
* service client exists for it, whether they be local or remote to this
* process.
*/
RCLCPP_PUBLIC
virtual
std::map<std::string, std::vector<std::string>>
get_service_names_and_types() const = 0;
/// Return a vector of existing node names (string).
RCLCPP_PUBLIC
virtual
std::vector<std::string>
get_node_names() const = 0;
/// Return the number of publishers that are advertised on a given topic.
RCLCPP_PUBLIC
@@ -64,7 +85,8 @@ public:
get_graph_guard_condition() const = 0;
/// Notify threads waiting on graph changes.
/* Affects threads waiting on the notify guard condition, see:
/**
* 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().
*
@@ -84,7 +106,8 @@ public:
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 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.
*/
@@ -94,7 +117,8 @@ public:
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.
/**
* 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
@@ -108,7 +132,8 @@ public:
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.
/**
* This is typically only used by the rclcpp::graph_listener::GraphListener.
*/
RCLCPP_PUBLIC
virtual

View File

@@ -216,7 +216,7 @@ private:
};
/* Return a json encoded version of the parameter intended for a dict. */
/// Return a json encoded version of the parameter intended for a dict.
RCLCPP_PUBLIC
std::string
_to_json_dict_entry(const ParameterVariant & param);
@@ -235,12 +235,12 @@ operator<<(std::ostream & os, const std::vector<ParameterVariant> & parameters);
namespace std
{
/* Return a json encoded version of the parameter intended for a list. */
/// Return a json encoded version of the parameter intended for a list.
RCLCPP_PUBLIC
std::string
to_string(const rclcpp::parameter::ParameterVariant & param);
/* Return a json encoded version of a vector of parameters, as a string*/
/// Return a json encoded version of a vector of parameters, as a string.
RCLCPP_PUBLIC
std::string
to_string(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);

View File

@@ -75,25 +75,25 @@ public:
virtual ~PublisherBase();
/// Get the topic that this publisher publishes on.
// \return The topic name.
/** \return The topic name. */
RCLCPP_PUBLIC
const char *
get_topic_name() const;
/// Get the queue size for this publisher.
// \return The queue size.
/** \return The queue size. */
RCLCPP_PUBLIC
size_t
get_queue_size() const;
/// Get the global identifier for this publisher (used in rmw and by DDS).
// \return The gid.
/** \return The gid. */
RCLCPP_PUBLIC
const rmw_gid_t &
get_gid() const;
/// Get the global identifier for this publisher used by intra-process communication.
// \return The intra-process gid.
/** \return The intra-process gid. */
RCLCPP_PUBLIC
const rmw_gid_t &
get_intra_process_gid() const;
@@ -261,6 +261,15 @@ public:
return this->publish(unique_msg);
}
virtual void
publish(const MessageT * msg)
{
if (!msg) {
throw std::runtime_error("msg argument is nullptr");
}
return this->publish(*msg);
}
std::shared_ptr<MessageAlloc> get_allocator() const
{
return message_allocator_;

View File

@@ -32,7 +32,8 @@ namespace rclcpp
{
/// Factory with functions used to create a MessageT specific PublisherT.
/* This factory class is used to encapsulate the template generated functions
/**
* 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.
*

View File

@@ -12,6 +12,113 @@
// See the License for the specific language governing permissions and
// limitations under the License.
/** \mainpage rclcpp: ROS Client Library for C++
*
* `rclcpp` provides the canonical C++ API for interacting with ROS.
* It consists of these main components:
*
* - Nodes
* - rclcpp::node::Node
* - rclcpp/node.hpp
* - Publisher
* - rclcpp::node::Node::create_publisher()
* - rclcpp::publisher::Publisher
* - rclcpp::publisher::Publisher::publish()
* - rclcpp/publisher.hpp
* - Subscription
* - rclcpp::node::Node::create_subscription()
* - rclcpp::subscription::Subscription
* - rclcpp/subscription.hpp
* - Service Client
* - rclcpp::node::Node::create_client()
* - rclcpp::client::Client
* - rclcpp/client.hpp
* - Service Server
* - rclcpp::node::Node::create_service()
* - rclcpp::service::Service
* - rclcpp/service.hpp
* - Timer
* - rclcpp::node::Node::create_wall_timer()
* - rclcpp::timer::WallTimer
* - rclcpp::timer::TimerBase
* - rclcpp/timer.hpp
* - Parameters:
* - rclcpp::node::Node::set_parameters()
* - rclcpp::node::Node::get_parameters()
* - rclcpp::node::Node::get_parameter()
* - rclcpp::node::Node::describe_parameters()
* - rclcpp::node::Node::list_parameters()
* - rclcpp::node::Node::register_param_change_callback()
* - rclcpp::parameter::ParameterVariant
* - rclcpp::parameter_client::AsyncParametersClient
* - rclcpp::parameter_client::SyncParametersClient
* - rclcpp/parameter.hpp
* - rclcpp/parameter_client.hpp
* - rclcpp/parameter_service.hpp
* - Rate:
* - rclcpp::rate::Rate
* - rclcpp::rate::WallRate
* - rclcpp/rate.hpp
*
* There are also some components which help control the execution of callbacks:
*
* - Executors (responsible for execution of callbacks through a blocking spin):
* - rclcpp::spin()
* - rclcpp::spin_some()
* - rclcpp::spin_until_future_complete()
* - rclcpp::executors::single_threaded_executor::SingleThreadedExecutor
* - rclcpp::executors::single_threaded_executor::SingleThreadedExecutor::add_node()
* - rclcpp::executors::single_threaded_executor::SingleThreadedExecutor::spin()
* - rclcpp::executors::multi_threaded_executor::MultiThreadedExecutor
* - rclcpp::executors::multi_threaded_executor::MultiThreadedExecutor::add_node()
* - rclcpp::executors::multi_threaded_executor::MultiThreadedExecutor::spin()
* - rclcpp/executor.hpp
* - rclcpp/executors.hpp
* - rclcpp/executors/single_threaded_executor.hpp
* - rclcpp/executors/multi_threaded_executor.hpp
* - CallbackGroups (mechanism for enforcing concurrency rules for callbacks):
* - rclcpp::node::Node::create_callback_group()
* - rclcpp::callback_group::CallbackGroup
* - rclcpp/callback_group.hpp
*
* Additionally, there are some methods for introspecting the ROS graph:
*
* - Graph Events (a waitable event object that wakes up when the graph changes):
* - rclcpp::node::Node::get_graph_event()
* - rclcpp::node::Node::wait_for_graph_change()
* - rclcpp::event::Event
* - List topic names and types:
* - rclcpp::node::Node::get_topic_names_and_types()
* - Get the number of publishers or subscribers on a topic:
* - rclcpp::node::Node::count_publishers()
* - rclcpp::node::Node::count_subscribers()
*
* Finally, there are many internal API's and utilities:
*
* - Exceptions:
* - rclcpp/exceptions.hpp
* - Allocator related items:
* - rclcpp/allocator/allocator_common.hpp
* - rclcpp/allocator/allocator_deleter.hpp
* - Memory management tools:
* - rclcpp/memory_strategies.hpp
* - rclcpp/memory_strategy.hpp
* - rclcpp/message_memory_strategy.hpp
* - rclcpp/strategies/allocator_memory_strategy.hpp
* - rclcpp/strategies/message_pool_memory_strategy.hpp
* - Context object which is shared amongst multiple Nodes:
* - rclcpp::context::Context
* - rclcpp/context.hpp
* - rclcpp/contexts/default_context.hpp
* - Various utilities:
* - rclcpp/function_traits.hpp
* - rclcpp/macros.hpp
* - rclcpp/scope_exit.hpp
* - rclcpp/time.hpp
* - rclcpp/utilities.hpp
* - rclcpp/visibility_control.hpp
*/
#ifndef RCLCPP__RCLCPP_HPP_
#define RCLCPP__RCLCPP_HPP_
@@ -24,6 +131,7 @@
#include "rclcpp/parameter_client.hpp"
#include "rclcpp/parameter_service.hpp"
#include "rclcpp/rate.hpp"
#include "rclcpp/time.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"

View File

@@ -25,8 +25,10 @@
#include "rcl/service.h"
#include "rclcpp/any_service_callback.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
@@ -70,6 +72,10 @@ public:
protected:
RCLCPP_DISABLE_COPY(ServiceBase)
RCLCPP_PUBLIC
rcl_node_t *
get_rcl_node_handle() const;
std::shared_ptr<rcl_node_t> node_handle_;
rcl_service_t * service_handle_ = nullptr;
@@ -109,12 +115,25 @@ public:
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_options) != RCL_RET_OK)
{
throw std::runtime_error(std::string("could not create service: ") +
rcl_get_error_string_safe());
rcl_ret_t ret = rcl_service_init(
service_handle_,
node_handle.get(),
service_type_support_handle,
service_name.c_str(),
&service_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_SERVICE_NAME_INVALID) {
auto rcl_node_handle = get_rcl_node_handle();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
service_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
true);
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create service");
}
}
@@ -151,6 +170,7 @@ public:
ss << "Error in destruction of rcl service_handle_ handle: " <<
rcl_get_error_string_safe() << '\n';
(std::cerr << ss.str()).flush();
rcl_reset_error();
}
}
}
@@ -183,10 +203,7 @@ public:
rcl_ret_t status = rcl_send_response(get_service_handle(), req_id.get(), response.get());
if (status != RCL_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("failed to send response: ") + rcl_get_error_string_safe());
// *INDENT-ON*
rclcpp::exceptions::throw_from_rcl_error(status, "failed to send response");
}
}

View File

@@ -29,10 +29,12 @@
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/message_memory_strategy.hpp"
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
@@ -85,7 +87,7 @@ public:
get_intra_process_subscription_handle() const;
/// Borrow a new message.
// \return Shared pointer to the fresh message.
/** \return Shared pointer to the fresh message. */
virtual std::shared_ptr<void>
create_message() = 0;
/// Check if we need to handle the message, and execute the callback if we do.
@@ -97,9 +99,10 @@ public:
handle_message(std::shared_ptr<void> & message, const rmw_message_info_t & message_info) = 0;
/// Return the message borrowed in create_message.
// \param[in] Shared pointer to the returned message.
/** \param[in] message Shared pointer to the returned message. */
virtual void
return_message(std::shared_ptr<void> & message) = 0;
virtual void
handle_intra_process_message(
rcl_interfaces::msg::IntraProcessMessage & ipm,
@@ -192,6 +195,8 @@ public:
any_callback_.dispatch(typed_message, message_info);
}
/// Return the loaned message.
/** \param message message to be returned */
void return_message(std::shared_ptr<void> & message)
{
auto typed_message = std::static_pointer_cast<MessageT>(message);
@@ -235,16 +240,25 @@ public:
MatchesAnyPublishersCallbackType matches_any_publisher_callback,
const rcl_subscription_options_t & intra_process_options)
{
if (rcl_subscription_init(
&intra_process_subscription_handle_, node_handle_.get(),
rclcpp::type_support::get_intra_process_message_msg_type_support(),
(std::string(get_topic_name()) + "__intra").c_str(),
&intra_process_options) != RCL_RET_OK)
{
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("could not create intra process subscription: ") + rcl_get_error_string_safe());
// *INDENT-ON*
std::string intra_process_topic_name = std::string(get_topic_name()) + "/_intra";
rcl_ret_t ret = rcl_subscription_init(
&intra_process_subscription_handle_,
node_handle_.get(),
rclcpp::type_support::get_intra_process_message_msg_type_support(),
intra_process_topic_name.c_str(),
&intra_process_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
auto rcl_node_handle = node_handle_.get();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
intra_process_topic_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle));
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create intra process subscription");
}
intra_process_subscription_id_ = intra_process_subscription_id;

View File

@@ -33,7 +33,8 @@ namespace rclcpp
{
/// Factory with functions used to create a Subscription<MessageT>.
/* This factory class is used to encapsulate the template generated functions
/**
* 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.
*

View File

@@ -0,0 +1,72 @@
// Copyright 2017 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__TIME_HPP_
#define RCLCPP__TIME_HPP_
#include <utility>
#include "builtin_interfaces/msg/time.hpp"
#include "rcl/time.h"
#include "rcutils/time.h"
#include "rclcpp/exceptions.hpp"
namespace rclcpp
{
class Time
{
public:
template<rcl_time_source_type_t ClockT = RCL_SYSTEM_TIME>
static Time
now()
{
rcutils_time_point_value_t rcutils_now = 0;
rcutils_ret_t ret = RCUTILS_RET_ERROR;
if (ClockT == RCL_ROS_TIME) {
throw std::runtime_error("RCL_ROS_TIME is currently not implemented.");
ret = false;
} else if (ClockT == RCL_SYSTEM_TIME) {
ret = rcutils_system_time_now(&rcutils_now);
} else if (ClockT == RCL_STEADY_TIME) {
ret = rcutils_steady_time_now(&rcutils_now);
}
if (ret != RCUTILS_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(
ret, "Could not get current time: ");
}
return Time(std::move(rcutils_now));
}
operator builtin_interfaces::msg::Time() const
{
builtin_interfaces::msg::Time msg_time;
msg_time.sec = static_cast<std::int32_t>(RCL_NS_TO_S(rcl_time_));
msg_time.nanosec = static_cast<std::uint32_t>(rcl_time_ % (1000 * 1000 * 1000));
return msg_time;
}
private:
rcl_time_point_value_t rcl_time_;
explicit Time(rcl_time_point_value_t && rcl_time)
: rcl_time_(std::forward<decltype(rcl_time)>(rcl_time))
{}
};
} // namespace rclcpp
#endif // RCLCPP__TIME_HPP_

View File

@@ -55,6 +55,10 @@ public:
void
cancel();
RCLCPP_PUBLIC
void
reset();
RCLCPP_PUBLIC
virtual void
execute_callback() = 0;
@@ -64,13 +68,13 @@ public:
get_timer_handle();
/// Check how long the timer has until its next scheduled callback.
// \return A std::chrono::duration representing the relative time until the next callback.
/** \return A std::chrono::duration representing the relative time until the next callback. */
RCLCPP_PUBLIC
std::chrono::nanoseconds
time_until_trigger();
/// Is the clock steady (i.e. is the time between ticks constant?)
// \return True if the clock used by this timer is steady.
/** \return True if the clock used by this timer is steady. */
virtual bool is_steady() = 0;
/// Check if the timer is ready to trigger the callback.

View File

@@ -57,7 +57,7 @@ void
init(int argc, char * argv[]);
/// Check rclcpp's status.
// \return True if SIGINT hasn't fired yet, false otherwise.
/** \return True if SIGINT hasn't fired yet, false otherwise. */
RCLCPP_PUBLIC
bool
ok();
@@ -68,7 +68,7 @@ void
shutdown();
/// Register a function to be called when shutdown is called.
/* Calling the callbacks is the last thing shutdown() does. */
/** Calling the callbacks is the last thing shutdown() does. */
RCLCPP_PUBLIC
void
on_shutdown(std::function<void(void)> callback);
@@ -81,7 +81,7 @@ on_shutdown(std::function<void(void)> callback);
* that the same guard condition is not reused across waitsets (e.g., when
* using multiple executors in the same process). Will throw an exception if
* initialization of the guard condition fails.
* \param[waitset] waitset Pointer to the rcl_wait_set_t that will be using the
* \param waitset Pointer to the rcl_wait_set_t that will be using the
* resulting guard condition.
* \return Pointer to the guard condition.
*/
@@ -95,7 +95,7 @@ get_sigint_guard_condition(rcl_wait_set_t * waitset);
* to get a sigint guard condition, then you should call release_sigint_guard_condition()
* when you're done, to free that condition. Will throw an exception if
* get_sigint_guard_condition() wasn't previously called for the given waitset.
* \param[waitset] waitset Pointer to the rcl_wait_set_t that was using the
* \param waitset Pointer to the rcl_wait_set_t that was using the
* resulting guard condition.
*/
RCLCPP_PUBLIC

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp</name>
<version>0.0.0</version>
<version>0.0.2</version>
<description>The ROS client library in C++.</description>
<maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
<license>Apache License 2.0</license>
@@ -11,12 +11,15 @@
<build_export_depend>rmw</build_export_depend>
<build_depend>builtin_interfaces</build_depend>
<build_depend>rcl_interfaces</build_depend>
<build_depend>rmw_implementation_cmake</build_depend>
<build_depend>rosidl_generator_cpp</build_depend>
<build_depend>rosidl_typesupport_c</build_depend>
<build_depend>rosidl_typesupport_cpp</build_depend>
<build_export_depend>builtin_interfaces</build_export_depend>
<build_export_depend>rcl_interfaces</build_export_depend>
<build_export_depend>rosidl_generator_cpp</build_export_depend>
<build_export_depend>rosidl_typesupport_c</build_export_depend>
<build_export_depend>rosidl_typesupport_cpp</build_export_depend>
<depend>rcl</depend>
@@ -28,6 +31,7 @@
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<test_depend>rmw</test_depend>
<test_depend>rmw_implementation_cmake</test_depend>
<export>
<build_type>ament_cmake</build_type>

View File

@@ -25,6 +25,5 @@ macro(_rclcpp_register_package_hook)
endif()
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

@@ -70,6 +70,12 @@ bool
ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
{
auto start = std::chrono::steady_clock::now();
// make an event to reuse, rather than create a new one each time
auto node_ptr = node_graph_.lock();
if (!node_ptr) {
throw InvalidNodeError();
}
auto event = node_ptr->get_graph_event();
// check to see if the server is ready immediately
if (this->service_is_ready()) {
return true;
@@ -78,12 +84,6 @@ ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
// check was non-blocking, return immediately
return false;
}
// make an event to reuse, rather than create a new one each time
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 spent in the first call
// to this->server_is_ready()
std::chrono::nanoseconds time_to_wait = timeout - (std::chrono::steady_clock::now() - start);

View File

@@ -18,18 +18,40 @@
#include <functional>
#include <string>
using namespace std::string_literals;
namespace rclcpp
{
namespace exceptions
{
std::string
NameValidationError::format_error(
const char * name_type,
const char * name,
const char * error_msg,
size_t invalid_index)
{
std::string msg = "";
msg += "Invalid "s + name_type + ": " + error_msg + ":\n";
msg += " '"s + name + "'\n";
msg += " "s + std::string(invalid_index, ' ') + "^\n";
return msg;
}
void
throw_from_rcl_error(rcl_ret_t ret, const std::string & prefix, bool reset_error)
throw_from_rcl_error(
rcl_ret_t ret,
const std::string & prefix,
const rcl_error_state_t * error_state,
void (* reset_error)())
{
if (RCL_RET_OK == ret) {
throw std::invalid_argument("ret is RCL_RET_OK");
}
const rcl_error_state_t * error_state = rcl_get_error_state();
if (!error_state) {
error_state = rcl_get_error_state();
}
if (!error_state) {
throw std::runtime_error("rcl error state is not set");
}
@@ -39,7 +61,7 @@ throw_from_rcl_error(rcl_ret_t ret, const std::string & prefix, bool reset_error
}
RCLErrorBase base_exc(ret, error_state);
if (reset_error) {
rcl_reset_error();
reset_error();
}
switch (ret) {
case RCL_RET_BAD_ALLOC:

View File

@@ -26,7 +26,7 @@ void
SingleThreadedExecutor::spin()
{
if (spinning.exchange(true)) {
throw std::runtime_error("spin_some() called while already spinning");
throw std::runtime_error("spin() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
while (rclcpp::utilities::ok() && spinning.load()) {

View File

@@ -0,0 +1,196 @@
// Copyright 2017 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/expand_topic_or_service_name.hpp"
#include <string>
#include "rcl/expand_topic_name.h"
#include "rcl/validate_topic_name.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rcutils/types/string_map.h"
#include "rmw/error_handling.h"
#include "rmw/validate_namespace.h"
#include "rmw/validate_node_name.h"
#include "rmw/validate_full_topic_name.h"
using rclcpp::exceptions::throw_from_rcl_error;
std::string
rclcpp::expand_topic_or_service_name(
const std::string & name,
const std::string & node_name,
const std::string & namespace_,
bool is_service)
{
char * expanded_topic = nullptr;
rcl_allocator_t allocator = rcl_get_default_allocator();
rcutils_allocator_t rcutils_allocator = rcutils_get_default_allocator();
rcutils_string_map_t substitutions_map = rcutils_get_zero_initialized_string_map();
rcutils_ret_t rcutils_ret = rcutils_string_map_init(&substitutions_map, 0, rcutils_allocator);
if (rcutils_ret != RCUTILS_RET_OK) {
if (rcutils_ret == RCUTILS_RET_BAD_ALLOC) {
throw_from_rcl_error(RCL_RET_BAD_ALLOC, "", rcutils_get_error_state(), rcutils_reset_error);
} else {
throw_from_rcl_error(RCL_RET_ERROR, "", rcutils_get_error_state(), rcutils_reset_error);
}
}
rcl_ret_t ret = rcl_get_default_topic_name_substitutions(&substitutions_map);
if (ret != RCL_RET_OK) {
rcutils_error_state_t error_state;
if (rcutils_error_state_copy(rcl_get_error_state(), &error_state) != RCUTILS_RET_OK) {
throw std::bad_alloc();
}
auto error_state_scope_exit = rclcpp::make_scope_exit([&error_state]() {
rcutils_error_state_fini(&error_state);
});
// finalize the string map before throwing
rcutils_ret = rcutils_string_map_fini(&substitutions_map);
if (rcutils_ret != RCUTILS_RET_OK) {
fprintf(stderr,
"[rclcpp|" RCUTILS_STRINGIFY(__FILE__) ":" RCUTILS_STRINGIFY(__LINE__) "]: "
"failed to fini string_map (%d) during error handling: %s\n",
rcutils_ret,
rcutils_get_error_string_safe());
rcutils_reset_error();
}
throw_from_rcl_error(ret, "", &error_state);
}
ret = rcl_expand_topic_name(
name.c_str(),
node_name.c_str(),
namespace_.c_str(),
&substitutions_map,
allocator,
&expanded_topic);
std::string result;
if (ret == RCL_RET_OK) {
result = expanded_topic;
allocator.deallocate(expanded_topic, allocator.state);
}
rcutils_ret = rcutils_string_map_fini(&substitutions_map);
if (rcutils_ret != RCUTILS_RET_OK) {
throw_from_rcl_error(RCL_RET_ERROR, "", rcutils_get_error_state(), rcutils_reset_error);
}
// expansion failed
if (ret != RCL_RET_OK) {
// if invalid topic or unknown substitution
if (ret == RCL_RET_TOPIC_NAME_INVALID || ret == RCL_RET_UNKNOWN_SUBSTITUTION) {
rcl_reset_error(); // explicitly discard error from rcl_expand_topic_name()
int validation_result;
size_t invalid_index;
rcl_ret_t ret = rcl_validate_topic_name(name.c_str(), &validation_result, &invalid_index);
if (ret != RCL_RET_OK) {
throw_from_rcl_error(ret);
}
if (validation_result == RCL_TOPIC_NAME_VALID) {
throw std::runtime_error("topic name unexpectedly valid");
}
const char * validation_message = rcl_topic_name_validation_result_string(validation_result);
if (!validation_message) {
throw std::runtime_error("unable to get validation error message");
}
if (is_service) {
using rclcpp::exceptions::InvalidServiceNameError;
throw InvalidServiceNameError(name.c_str(), validation_message, invalid_index);
} else {
using rclcpp::exceptions::InvalidTopicNameError;
throw InvalidTopicNameError(name.c_str(), validation_message, invalid_index);
}
// if invalid node name
} else if (ret == RCL_RET_NODE_INVALID_NAME) {
rcl_reset_error(); // explicitly discard error from rcl_expand_topic_name()
int validation_result;
size_t invalid_index;
rmw_ret_t rmw_ret =
rmw_validate_node_name(node_name.c_str(), &validation_result, &invalid_index);
if (rmw_ret != RMW_RET_OK) {
if (rmw_ret == RMW_RET_INVALID_ARGUMENT) {
throw_from_rcl_error(
RCL_RET_INVALID_ARGUMENT, "failed to validate node name",
rmw_get_error_state(), rmw_reset_error);
}
throw_from_rcl_error(
RCL_RET_ERROR, "failed to validate node name",
rmw_get_error_state(), rmw_reset_error);
}
throw rclcpp::exceptions::InvalidNodeNameError(
node_name.c_str(),
rmw_node_name_validation_result_string(validation_result),
invalid_index);
// if invalid namespace
} else if (ret == RCL_RET_NODE_INVALID_NAMESPACE) {
rcl_reset_error(); // explicitly discard error from rcl_expand_topic_name()
int validation_result;
size_t invalid_index;
rmw_ret_t rmw_ret =
rmw_validate_namespace(namespace_.c_str(), &validation_result, &invalid_index);
if (rmw_ret != RMW_RET_OK) {
if (rmw_ret == RMW_RET_INVALID_ARGUMENT) {
throw_from_rcl_error(
RCL_RET_INVALID_ARGUMENT, "failed to validate namespace",
rmw_get_error_state(), rmw_reset_error);
}
throw_from_rcl_error(
RCL_RET_ERROR, "failed to validate namespace",
rmw_get_error_state(), rmw_reset_error);
}
throw rclcpp::exceptions::InvalidNamespaceError(
namespace_.c_str(),
rmw_namespace_validation_result_string(validation_result),
invalid_index);
// something else happened
} else {
throw_from_rcl_error(ret);
}
}
// expand succeeded, but full name validation may fail still
int validation_result;
size_t invalid_index;
rmw_ret_t rmw_ret =
rmw_validate_full_topic_name(result.c_str(), &validation_result, &invalid_index);
if (rmw_ret != RMW_RET_OK) {
if (rmw_ret == RMW_RET_INVALID_ARGUMENT) {
throw_from_rcl_error(
RCL_RET_INVALID_ARGUMENT, "failed to validate full topic name",
rmw_get_error_state(), rmw_reset_error);
}
throw_from_rcl_error(
RCL_RET_ERROR, "failed to validate full topic name",
rmw_get_error_state(), rmw_reset_error);
}
if (validation_result != RMW_TOPIC_VALID) {
if (is_service) {
throw rclcpp::exceptions::InvalidServiceNameError(
result.c_str(),
rmw_full_topic_name_validation_result_string(validation_result),
invalid_index);
} else {
throw rclcpp::exceptions::InvalidTopicNameError(
result.c_str(),
rmw_full_topic_name_validation_result_string(validation_result),
invalid_index);
}
}
return result;
}

View File

@@ -33,18 +33,23 @@
using rclcpp::node::Node;
using rclcpp::exceptions::throw_from_rcl_error;
Node::Node(const std::string & node_name, bool use_intra_process_comms)
Node::Node(
const std::string & node_name,
const std::string & namespace_,
bool use_intra_process_comms)
: Node(
node_name,
namespace_,
rclcpp::contexts::default_context::get_global_default_context(),
use_intra_process_comms)
{}
Node::Node(
const std::string & node_name,
const std::string & namespace_,
rclcpp::context::Context::SharedPtr context,
bool use_intra_process_comms)
: node_base_(new rclcpp::node_interfaces::NodeBase(node_name, context)),
: node_base_(new rclcpp::node_interfaces::NodeBase(node_name, namespace_, 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())),
@@ -66,6 +71,12 @@ Node::get_name() const
return node_base_->get_name();
}
const char *
Node::get_namespace() const
{
return node_base_->get_namespace();
}
rclcpp::callback_group::CallbackGroup::SharedPtr
Node::create_callback_group(
rclcpp::callback_group::CallbackGroupType group_type)
@@ -133,12 +144,18 @@ Node::list_parameters(
return node_parameters_->list_parameters(prefixes, depth);
}
std::map<std::string, std::string>
std::map<std::string, std::vector<std::string>>
Node::get_topic_names_and_types() const
{
return node_graph_->get_topic_names_and_types();
}
std::map<std::string, std::vector<std::string>>
Node::get_service_names_and_types() const
{
return node_graph_->get_service_names_and_types();
}
size_t
Node::count_publishers(const std::string & topic_name) const
{

View File

@@ -20,6 +20,8 @@
#include "rclcpp/node_interfaces/node_base.hpp"
#include "rclcpp/exceptions.hpp"
#include "rmw/validate_node_name.h"
#include "rmw/validate_namespace.h"
using rclcpp::exceptions::throw_from_rcl_error;
@@ -27,6 +29,7 @@ using rclcpp::node_interfaces::NodeBase;
NodeBase::NodeBase(
const std::string & node_name,
const std::string & namespace_,
rclcpp::context::Context::SharedPtr context)
: context_(context),
node_handle_(nullptr),
@@ -84,11 +87,47 @@ NodeBase::NodeBase(
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);
ret = rcl_node_init(rcl_node, node_name.c_str(), namespace_.c_str(), &options);
if (ret != RCL_RET_OK) {
// Finalize the interrupt guard condition.
finalize_notify_guard_condition();
if (ret == RCL_RET_NODE_INVALID_NAME) {
rcl_reset_error(); // discard rcl_node_init error
int validation_result;
size_t invalid_index;
rmw_ret_t rmw_ret =
rmw_validate_node_name(node_name.c_str(), &validation_result, &invalid_index);
if (rmw_ret != RMW_RET_OK) {
if (rmw_ret == RMW_RET_INVALID_ARGUMENT) {
throw_from_rcl_error(RCL_RET_INVALID_ARGUMENT, "failed to validate node name");
}
throw_from_rcl_error(RCL_RET_ERROR, "failed to validate node name");
}
throw rclcpp::exceptions::InvalidNodeNameError(
node_name.c_str(),
rmw_node_name_validation_result_string(validation_result),
invalid_index);
}
if (ret == RCL_RET_NODE_INVALID_NAMESPACE) {
rcl_reset_error(); // discard rcl_node_init error
int validation_result;
size_t invalid_index;
rmw_ret_t rmw_ret =
rmw_validate_namespace(namespace_.c_str(), &validation_result, &invalid_index);
if (rmw_ret != RMW_RET_OK) {
if (rmw_ret == RMW_RET_INVALID_ARGUMENT) {
throw_from_rcl_error(RCL_RET_INVALID_ARGUMENT, "failed to validate namespace");
}
throw_from_rcl_error(RCL_RET_ERROR, "failed to validate namespace");
}
throw rclcpp::exceptions::InvalidNamespaceError(
namespace_.c_str(),
rmw_namespace_validation_result_string(validation_result),
invalid_index);
}
throw_from_rcl_error(ret, "failed to initialize rcl node");
}
@@ -127,6 +166,12 @@ NodeBase::get_name() const
return rcl_node_get_name(node_handle_.get());
}
const char *
NodeBase::get_namespace() const
{
return rcl_node_get_namespace(node_handle_.get());
}
rclcpp::context::Context::SharedPtr
NodeBase::get_context()
{

View File

@@ -16,6 +16,7 @@
#include <map>
#include <string>
#include <vector>
#include "rcl/graph.h"
#include "rclcpp/exceptions.hpp"
@@ -44,44 +45,137 @@ NodeGraph::~NodeGraph()
}
}
std::map<std::string, std::string>
NodeGraph::get_topic_names_and_types() const
std::map<std::string, std::vector<std::string>>
NodeGraph::get_topic_names_and_types(bool no_demangle) const
{
rcl_topic_names_and_types_t topic_names_and_types =
rcl_get_zero_initialized_topic_names_and_types();
rcl_names_and_types_t topic_names_and_types = rcl_get_zero_initialized_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) {
rcl_allocator_t allocator = rcl_get_default_allocator();
auto ret = rcl_get_topic_names_and_types(
node_base_->get_rcl_node_handle(),
&allocator,
no_demangle,
&topic_names_and_types);
if (ret != RCL_RET_OK) {
auto error_msg = std::string("failed to get topic names and types: ") +
rcl_get_error_string_safe();
rcl_reset_error();
if (rcl_names_and_types_fini(&topic_names_and_types) != RCL_RET_OK) {
error_msg += std::string(", failed also to cleanup topic names and types, leaking memory: ") +
rcl_get_error_string_safe();
}
throw std::runtime_error(error_msg + rcl_get_error_string_safe());
}
std::map<std::string, std::vector<std::string>> topics_and_types;
for (size_t i = 0; i < topic_names_and_types.names.size; ++i) {
std::string topic_name = topic_names_and_types.names.data[i];
for (size_t j = 0; j < topic_names_and_types.types[i].size; ++j) {
topics_and_types[topic_name].emplace_back(topic_names_and_types.types[i].data[j]);
}
}
ret = rcl_names_and_types_fini(&topic_names_and_types);
if (ret != RCL_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not get topic names and types: ") + rmw_get_error_string_safe());
std::string("could not destroy topic names and types: ") + rcl_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];
return topics_and_types;
}
std::map<std::string, std::vector<std::string>>
NodeGraph::get_service_names_and_types() const
{
rcl_names_and_types_t service_names_and_types = rcl_get_zero_initialized_names_and_types();
rcl_allocator_t allocator = rcl_get_default_allocator();
auto ret = rcl_get_service_names_and_types(
node_base_->get_rcl_node_handle(),
&allocator,
&service_names_and_types);
if (ret != RCL_RET_OK) {
auto error_msg = std::string("failed to get service names and types: ") +
rcl_get_error_string_safe();
rcl_reset_error();
if (rcl_names_and_types_fini(&service_names_and_types) != RCL_RET_OK) {
error_msg +=
std::string(", failed also to cleanup service names and types, leaking memory: ") +
rcl_get_error_string_safe();
}
throw std::runtime_error(error_msg + rcl_get_error_string_safe());
}
ret = rmw_destroy_topic_names_and_types(&topic_names_and_types);
if (ret != RMW_RET_OK) {
std::map<std::string, std::vector<std::string>> services_and_types;
for (size_t i = 0; i < service_names_and_types.names.size; ++i) {
std::string service_name = service_names_and_types.names.data[i];
for (size_t j = 0; j < service_names_and_types.types[i].size; ++j) {
services_and_types[service_name].emplace_back(service_names_and_types.types[i].data[j]);
}
}
ret = rcl_names_and_types_fini(&service_names_and_types);
if (ret != RCL_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not destroy topic names and types: ") + rmw_get_error_string_safe());
std::string("could not destroy service names and types: ") + rcl_get_error_string_safe());
// *INDENT-ON*
}
return topics;
return services_and_types;
}
std::vector<std::string>
NodeGraph::get_node_names() const
{
rcutils_string_array_t node_names_c =
rcutils_get_zero_initialized_string_array();
auto allocator = rcl_get_default_allocator();
auto ret = rcl_get_node_names(
node_base_->get_rcl_node_handle(),
allocator,
&node_names_c);
if (ret != RCL_RET_OK) {
auto error_msg = std::string("failed to get node names: ") + rcl_get_error_string_safe();
rcl_reset_error();
if (rcutils_string_array_fini(&node_names_c) != RCUTILS_RET_OK) {
error_msg += std::string(", failed also to cleanup node names, leaking memory: ") +
rcl_get_error_string_safe();
}
// TODO(karsten1987): Append rcutils_error_message once it's in master
throw std::runtime_error(error_msg);
}
std::vector<std::string> node_names(&node_names_c.data[0],
&node_names_c.data[0 + node_names_c.size]);
ret = rcutils_string_array_fini(&node_names_c);
if (ret != RCUTILS_RET_OK) {
// *INDENT-OFF*
// TODO(karsten1987): Append rcutils_error_message once it's in master
throw std::runtime_error(
std::string("could not destroy node names: "));
// *INDENT-ON*
}
return node_names;
}
size_t
NodeGraph::count_publishers(const std::string & topic_name) const
{
auto rmw_node_handle = rcl_node_get_rmw_handle(node_base_->get_rcl_node_handle());
auto fqdn = rclcpp::expand_topic_or_service_name(
topic_name,
rmw_node_handle->name,
rmw_node_handle->namespace_,
false); // false = not a service
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);
auto ret = rmw_count_publishers(rmw_node_handle, fqdn.c_str(), &count);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
@@ -94,10 +188,16 @@ NodeGraph::count_publishers(const std::string & topic_name) const
size_t
NodeGraph::count_subscribers(const std::string & topic_name) const
{
auto rmw_node_handle = rcl_node_get_rmw_handle(node_base_->get_rcl_node_handle());
auto fqdn = rclcpp::expand_topic_or_service_name(
topic_name,
rmw_node_handle->name,
rmw_node_handle->namespace_,
false); // false = not a service
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);
auto ret = rmw_count_subscribers(rmw_node_handle, fqdn.c_str(), &count);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
@@ -163,13 +263,13 @@ NodeGraph::get_graph_event()
{
auto event = rclcpp::event::Event::make_shared();
std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
graph_events_.push_back(event);
graph_users_count_++;
// 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;
}
@@ -196,10 +296,13 @@ NodeGraph::wait_for_graph_change(
throw EventNotRegisteredError();
}
}
auto pred = [&event]() {
return event->check() || !rclcpp::utilities::ok();
};
std::unique_lock<std::mutex> graph_lock(graph_mutex_);
graph_cv_.wait_for(graph_lock, timeout, [&event]() {
return event->check() || !rclcpp::utilities::ok();
});
if (!pred()) {
graph_cv_.wait_for(graph_lock, timeout, pred);
}
}
size_t

View File

@@ -83,11 +83,18 @@ NodeParameters::set_parameters_atomically(
for (auto p : parameters) {
if (parameters_.find(p.get_name()) == parameters_.end()) {
if (p.get_type() != rclcpp::parameter::ParameterType::PARAMETER_NOT_SET) {
// case: parameter not set before, and input is something other than "NOT_SET"
parameter_event->new_parameters.push_back(p.to_parameter());
}
} else if (p.get_type() != rclcpp::parameter::ParameterType::PARAMETER_NOT_SET) {
// case: parameter was set before, and input is something other than "NOT_SET"
parameter_event->changed_parameters.push_back(p.to_parameter());
} else {
// case: parameter was set before, and input is "NOT_SET"
// therefore we will "unset" the previously set parameter
// it is not necessary to erase the parameter from parameters_
// because the new value for this key (p.get_name()) will be a
// ParameterVariant with type "NOT_SET"
parameter_event->deleted_parameters.push_back(p.to_parameter());
}
tmp_map[p.get_name()] = p;
@@ -226,5 +233,9 @@ NodeParameters::list_parameters(const std::vector<std::string> & prefixes, uint6
void
NodeParameters::register_param_change_callback(ParametersCallbackFunction callback)
{
if (parameters_callback_) {
fprintf(stderr, "Warning: param_change_callback already registered, "
"overwriting previous callback\n");
}
parameters_callback_ = callback;
}

View File

@@ -28,8 +28,10 @@
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
using rclcpp::publisher::PublisherBase;
@@ -41,25 +43,36 @@ PublisherBase::PublisherBase(
: 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());
rcl_ret_t ret = rcl_publisher_init(
&publisher_handle_,
rcl_node_handle_.get(),
&type_support,
topic.c_str(),
&publisher_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
auto rcl_node_handle = rcl_node_handle_.get();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
topic,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle));
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create publisher");
}
// 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());
auto msg = std::string("failed to get rmw handle: ") + rcl_get_error_string_safe();
rcl_reset_error();
throw std::runtime_error(msg);
}
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*
auto msg = std::string("failed to get publisher gid: ") + rmw_get_error_string_safe();
rmw_reset_error();
throw std::runtime_error(msg);
}
}
@@ -70,6 +83,7 @@ PublisherBase::~PublisherBase()
stderr,
"Error in destruction of intra process rcl publisher handle: %s\n",
rcl_get_error_string_safe());
rcl_reset_error();
}
if (rcl_publisher_fini(&publisher_handle_, rcl_node_handle_.get()) != RCL_RET_OK) {
@@ -77,6 +91,7 @@ PublisherBase::~PublisherBase()
stderr,
"Error in destruction of rcl publisher handle: %s\n",
rcl_get_error_string_safe());
rcl_reset_error();
}
}
@@ -91,8 +106,9 @@ PublisherBase::get_queue_size() const
{
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());
auto msg = std::string("failed to get publisher options: ") + rcl_get_error_string_safe();
rcl_reset_error();
throw std::runtime_error(msg);
}
return publisher_options->qos.depth;
}
@@ -121,14 +137,16 @@ PublisherBase::operator==(const rmw_gid_t * gid) const
bool result = false;
auto ret = rmw_compare_gids_equal(gid, &this->get_gid(), &result);
if (ret != RMW_RET_OK) {
throw std::runtime_error(
std::string("failed to compare gids: ") + rmw_get_error_string_safe());
auto msg = std::string("failed to compare gids: ") + rmw_get_error_string_safe();
rmw_reset_error();
throw std::runtime_error(msg);
}
if (!result) {
ret = rmw_compare_gids_equal(gid, &this->get_intra_process_gid(), &result);
if (ret != RMW_RET_OK) {
throw std::runtime_error(
std::string("failed to compare gids: ") + rmw_get_error_string_safe());
auto msg = std::string("failed to compare gids: ") + rmw_get_error_string_safe();
rmw_reset_error();
throw std::runtime_error(msg);
}
}
return result;
@@ -140,15 +158,25 @@ 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_, rcl_node_handle_.get(),
rclcpp::type_support::get_intra_process_message_msg_type_support(),
intra_process_topic_name.c_str(), &intra_process_options) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("could not create intra process publisher: ") +
rcl_get_error_string_safe());
auto intra_process_topic_name = std::string(this->get_topic_name()) + "/_intra";
rcl_ret_t ret = rcl_publisher_init(
&intra_process_publisher_handle_,
rcl_node_handle_.get(),
rclcpp::type_support::get_intra_process_message_msg_type_support(),
intra_process_topic_name.c_str(),
&intra_process_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
auto rcl_node_handle = rcl_node_handle_.get();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
intra_process_topic_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle));
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create intra process publisher");
}
intra_process_publisher_id_ = intra_process_publisher_id;
@@ -157,16 +185,16 @@ PublisherBase::setup_intra_process(
rmw_publisher_t * publisher_rmw_handle = rcl_publisher_get_rmw_handle(
&intra_process_publisher_handle_);
if (publisher_rmw_handle == nullptr) {
throw std::runtime_error(std::string(
"Failed to get rmw publisher handle") + rcl_get_error_string_safe());
auto msg = std::string("Failed to get rmw publisher handle") + rcl_get_error_string_safe();
rcl_reset_error();
throw std::runtime_error(msg);
}
auto ret = rmw_get_gid_for_publisher(
auto rmw_ret = rmw_get_gid_for_publisher(
publisher_rmw_handle, &intra_process_rmw_gid_);
if (ret != RMW_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("failed to create intra process publisher gid: ") +
rmw_get_error_string_safe());
// *INDENT-ON*
if (rmw_ret != RMW_RET_OK) {
auto msg =
std::string("failed to create intra process publisher gid: ") + rmw_get_error_string_safe();
rmw_reset_error();
throw std::runtime_error(msg);
}
}

View File

@@ -51,3 +51,9 @@ ServiceBase::get_service_handle()
{
return service_handle_;
}
rcl_node_t *
ServiceBase::get_rcl_node_handle() const
{
return node_handle_.get();
}

View File

@@ -18,6 +18,9 @@
#include <memory>
#include <string>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
@@ -38,8 +41,17 @@ SubscriptionBase::SubscriptionBase(
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());
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
auto rcl_node_handle = node_handle_.get();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
topic_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle));
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create subscription");
}
}

View File

@@ -40,6 +40,14 @@ TimerBase::cancel()
}
}
void
TimerBase::reset()
{
if (rcl_timer_reset(&timer_handle_) != RCL_RET_OK) {
throw std::runtime_error(std::string("Couldn't reset timer: ") + rcl_get_error_string_safe());
}
}
bool
TimerBase::is_ready()
{

View File

@@ -0,0 +1,60 @@
// Copyright 2017 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/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcl_interfaces/srv/list_parameters.hpp"
class TestClient : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
{
node = std::make_shared<rclcpp::node::Node>("my_node", "/ns");
}
void TearDown()
{
node.reset();
}
rclcpp::node::Node::SharedPtr node;
};
/*
Testing client construction and destruction.
*/
TEST_F(TestClient, construction_and_destruction) {
using rcl_interfaces::srv::ListParameters;
{
auto client = node->create_client<ListParameters>("service");
}
{
ASSERT_THROW({
auto client = node->create_client<ListParameters>("invalid_service?");
}, rclcpp::exceptions::InvalidServiceNameError);
}
}

View File

@@ -0,0 +1,74 @@
// Copyright 2017 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 "rclcpp/exceptions.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
/*
Testing expand_topic_or_service_name.
*/
TEST(TestExpandTopicOrServiceName, normal) {
using rclcpp::expand_topic_or_service_name;
{
ASSERT_EQ("/ns/chatter", expand_topic_or_service_name("chatter", "node", "/ns"));
}
}
/*
Testing exceptions of expand_topic_or_service_name.
*/
TEST(TestExpandTopicOrServiceName, exceptions) {
using rclcpp::expand_topic_or_service_name;
{
ASSERT_THROW({
expand_topic_or_service_name("chatter", "invalid_node?", "/ns");
}, rclcpp::exceptions::InvalidNodeNameError);
}
{
ASSERT_THROW({
expand_topic_or_service_name("chatter", "node", "/invalid_ns?");
}, rclcpp::exceptions::InvalidNamespaceError);
}
{
ASSERT_THROW({
expand_topic_or_service_name("chatter/42invalid", "node", "/ns");
}, rclcpp::exceptions::InvalidTopicNameError);
}
{
ASSERT_THROW({
// this one will only fail on the "full" topic name validation check
expand_topic_or_service_name("chatter/{ns}/invalid", "node", "/ns");
}, rclcpp::exceptions::InvalidTopicNameError);
}
{
ASSERT_THROW({
// is_service = true
expand_topic_or_service_name("chatter/42invalid", "node", "/ns", true);
}, rclcpp::exceptions::InvalidServiceNameError);
}
{
ASSERT_THROW({
// is_service = true
// this one will only fail on the "full" topic name validation check
expand_topic_or_service_name("chatter/{ns}/invalid", "node", "/ns", true);
}, rclcpp::exceptions::InvalidServiceNameError);
}
}

View File

@@ -83,8 +83,8 @@ TEST_F(TestExternallyDefinedServices, extern_defined_initialized) {
// 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);
const rosidl_service_type_support_t * ts =
rosidl_typesupport_cpp::get_service_type_support_handle<rclcpp::srv::Mock>();
rcl_ret_t ret = rcl_service_init(
&service_handle,
node_handle->get_node_base_interface()->get_rcl_node_handle(),
@@ -114,8 +114,8 @@ TEST_F(TestExternallyDefinedServices, extern_defined_destructor) {
// 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);
const rosidl_service_type_support_t * ts =
rosidl_typesupport_cpp::get_service_type_support_handle<rclcpp::srv::Mock>();
rcl_ret_t ret = rcl_service_init(
&service_handle,
node_handle->get_node_base_interface()->get_rcl_node_handle(),

75
rclcpp/test/test_node.cpp Normal file
View File

@@ -0,0 +1,75 @@
// Copyright 2017 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/exceptions.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/rclcpp.hpp"
class TestNode : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
};
/*
Testing node construction and destruction.
*/
TEST_F(TestNode, construction_and_destruction) {
{
auto node = std::make_shared<rclcpp::node::Node>("my_node", "/ns");
}
{
ASSERT_THROW({
auto node = std::make_shared<rclcpp::node::Node>("invalid_node?", "/ns");
}, rclcpp::exceptions::InvalidNodeNameError);
}
{
ASSERT_THROW({
auto node = std::make_shared<rclcpp::node::Node>("my_node", "/invalid_ns?");
}, rclcpp::exceptions::InvalidNamespaceError);
}
}
TEST_F(TestNode, get_name_and_namespace) {
{
auto node = std::make_shared<rclcpp::node::Node>("my_node", "/ns");
EXPECT_STREQ("my_node", node->get_name());
EXPECT_STREQ("/ns", node->get_namespace());
}
{
auto node = std::make_shared<rclcpp::node::Node>("my_node", "ns");
EXPECT_STREQ("my_node", node->get_name());
EXPECT_STREQ("/ns", node->get_namespace());
}
{
auto node = std::make_shared<rclcpp::node::Node>("my_node", "/my/ns");
EXPECT_STREQ("my_node", node->get_name());
EXPECT_STREQ("/my/ns", node->get_namespace());
}
{
auto node = std::make_shared<rclcpp::node::Node>("my_node", "my/ns");
EXPECT_STREQ("my_node", node->get_name());
EXPECT_STREQ("/my/ns", node->get_namespace());
}
}

View File

@@ -0,0 +1,60 @@
// Copyright 2017 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/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcl_interfaces/msg/intra_process_message.hpp"
class TestPublisher : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
{
node = std::make_shared<rclcpp::node::Node>("my_node", "/ns");
}
void TearDown()
{
node.reset();
}
rclcpp::node::Node::SharedPtr node;
};
/*
Testing publisher construction and destruction.
*/
TEST_F(TestPublisher, construction_and_destruction) {
using rcl_interfaces::msg::IntraProcessMessage;
{
auto publisher = node->create_publisher<IntraProcessMessage>("topic");
}
{
ASSERT_THROW({
auto publisher = node->create_publisher<IntraProcessMessage>("invalid_topic?");
}, rclcpp::exceptions::InvalidTopicNameError);
}
}

View File

@@ -0,0 +1,63 @@
// Copyright 2017 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/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcl_interfaces/srv/list_parameters.hpp"
class TestService : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
{
node = std::make_shared<rclcpp::node::Node>("my_node", "/ns");
}
void TearDown()
{
node.reset();
}
rclcpp::node::Node::SharedPtr node;
};
/*
Testing service construction and destruction.
*/
TEST_F(TestService, construction_and_destruction) {
using rcl_interfaces::srv::ListParameters;
auto callback =
[](const ListParameters::Request::SharedPtr, ListParameters::Response::SharedPtr) {
};
{
auto service = node->create_service<ListParameters>("service", callback);
}
{
ASSERT_THROW({
auto service = node->create_service<ListParameters>("invalid_service?", callback);
}, rclcpp::exceptions::InvalidServiceNameError);
}
}

View File

@@ -0,0 +1,63 @@
// Copyright 2017 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/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcl_interfaces/msg/intra_process_message.hpp"
class TestSubscription : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
{
node = std::make_shared<rclcpp::node::Node>("my_node", "/ns");
}
void TearDown()
{
node.reset();
}
rclcpp::node::Node::SharedPtr node;
};
/*
Testing subscription construction and destruction.
*/
TEST_F(TestSubscription, construction_and_destruction) {
using rcl_interfaces::msg::IntraProcessMessage;
auto callback = [](const IntraProcessMessage::SharedPtr msg) {
(void)msg;
};
{
auto sub = node->create_subscription<IntraProcessMessage>("topic", callback);
}
{
ASSERT_THROW({
auto sub = node->create_subscription<IntraProcessMessage>("invalid_topic?", callback);
}, rclcpp::exceptions::InvalidTopicNameError);
}
}

50
rclcpp/test/test_time.cpp Normal file
View File

@@ -0,0 +1,50 @@
// Copyright 2017 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 "rcl/error_handling.h"
#include "rcl/time.h"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/time.hpp"
class TestTime : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
};
TEST(TestTime, rate_basics) {
using builtin_interfaces::msg::Time;
// TODO(Karsten1987): Fix this test once ROS_TIME is implemented
EXPECT_ANY_THROW(rclcpp::Time::now<RCL_ROS_TIME>());
Time system_now = rclcpp::Time::now<RCL_SYSTEM_TIME>();
EXPECT_NE(0, system_now.sec);
EXPECT_NE(0u, system_now.nanosec);
Time steady_now = rclcpp::Time::now<RCL_STEADY_TIME>();
EXPECT_NE(0, steady_now.sec);
EXPECT_NE(0u, steady_now.nanosec);
// default
Time default_now = rclcpp::Time::now();
EXPECT_NE(0, default_now.sec);
EXPECT_NE(0u, default_now.nanosec);
}

View File

@@ -2,49 +2,44 @@ cmake_minimum_required(VERSION 3.5)
project(rclcpp_lifecycle)
# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
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
SHARED
src/lifecycle_node.cpp
src/node_interfaces/lifecycle_node_interface.cpp
src/state.cpp
src/transition.cpp
)
ament_target_dependencies(rclcpp_lifecycle
"lifecycle_msgs"
"rcl_lifecycle"
"rclcpp")
### 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 PRIVATE "RCLCPP_LIFECYCLE_BUILDING_DLL")
# 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)
install(TARGETS
rclcpp_lifecycle
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
@@ -86,6 +81,15 @@ if(BUILD_TESTING)
)
target_link_libraries(test_callback_exceptions ${PROJECT_NAME})
endif()
ament_add_gtest(test_state_wrapper test/test_state_wrapper.cpp)
if(TARGET test_state_wrapper)
target_include_directories(test_state_wrapper PUBLIC
${rcl_lifecycle_INCLUDE_DIRS}
${rclcpp_INCLUDE_DIRS}
${rclcpp_lifecycle_INCLUDE_DIRS}
)
target_link_libraries(test_state_wrapper ${PROJECT_NAME})
endif()
endif()
ament_export_dependencies(rclcpp)

View File

@@ -68,22 +68,29 @@ public:
/// Create a new lifecycle node with the specified name.
/**
* \param[in] node_name Name of the node.
* \param[in] node_name Namespace 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);
explicit LifecycleNode(
const std::string & node_name,
const std::string & namespace_ = "",
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] node_name Namespace 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,
const std::string & node_name,
const std::string & namespace_,
rclcpp::context::Context::SharedPtr context,
bool use_intra_process_comms = false);
RCLCPP_LIFECYCLE_PUBLIC
@@ -95,6 +102,12 @@ public:
const char *
get_name() const;
/// Get the namespace of the node.
// \return The namespace of the node.
RCLCPP_LIFECYCLE_PUBLIC
const char *
get_namespace() const;
/// Create and return a callback group.
RCLCPP_LIFECYCLE_PUBLIC
rclcpp::callback_group::CallbackGroup::SharedPtr
@@ -268,8 +281,12 @@ public:
register_param_change_callback(CallbackT && callback);
RCLCPP_LIFECYCLE_PUBLIC
std::map<std::string, std::string>
get_topic_names_and_types() const;
std::map<std::string, std::vector<std::string>>
get_topic_names_and_types(bool no_demangle = false) const;
RCLCPP_LIFECYCLE_PUBLIC
std::map<std::string, std::vector<std::string>>
get_service_names_and_types() const;
RCLCPP_LIFECYCLE_PUBLIC
size_t
@@ -356,23 +373,48 @@ public:
RCLCPP_LIFECYCLE_PUBLIC
const State &
trigger_transition(unsigned int transition_id);
trigger_transition(uint8_t transition_id);
RCLCPP_LIFECYCLE_PUBLIC
const State &
configure();
RCLCPP_LIFECYCLE_PUBLIC
const State &
cleanup();
RCLCPP_LIFECYCLE_PUBLIC
const State &
activate();
RCLCPP_LIFECYCLE_PUBLIC
const State &
deactivate();
RCLCPP_LIFECYCLE_PUBLIC
const State &
shutdown();
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);

View File

@@ -125,6 +125,15 @@ public:
rclcpp::publisher::Publisher<MessageT, Alloc>::publish(msg);
}
virtual void
publish(const MessageT * msg)
{
if (!msg) {
throw std::runtime_error("msg argument is nullptr");
}
this->publish(*msg);
}
/// LifecyclePublisher pulish function
/**
* The publish function checks whether the communication

View File

@@ -32,7 +32,7 @@ public:
State();
RCLCPP_LIFECYCLE_PUBLIC
State(unsigned int id, const std::string & label);
State(uint8_t id, const std::string & label);
RCLCPP_LIFECYCLE_PUBLIC
explicit State(const rcl_lifecycle_state_t * rcl_lifecycle_state_handle);
@@ -41,7 +41,7 @@ public:
virtual ~State();
RCLCPP_LIFECYCLE_PUBLIC
unsigned int
uint8_t
id() const;
RCLCPP_LIFECYCLE_PUBLIC

View File

@@ -33,11 +33,11 @@ public:
Transition() = delete;
RCLCPP_LIFECYCLE_PUBLIC
explicit Transition(unsigned int id, const std::string & label = "");
explicit Transition(uint8_t id, const std::string & label = "");
RCLCPP_LIFECYCLE_PUBLIC
Transition(
unsigned int id, const std::string & label,
uint8_t id, const std::string & label,
State && start, State && goal);
RCLCPP_LIFECYCLE_PUBLIC
@@ -47,7 +47,7 @@ public:
virtual ~Transition();
RCLCPP_LIFECYCLE_PUBLIC
unsigned int
uint8_t
id() const;
RCLCPP_LIFECYCLE_PUBLIC

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp_lifecycle</name>
<version>0.0.0</version>
<version>0.0.2</version>
<description>Package containing a prototype for lifecycle implementation</description>
<maintainer email="karsten@osrfoundation.org">Karsten Knese</maintainer>
<license>Apache License 2.0</license>
@@ -13,7 +13,6 @@
<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>

View File

@@ -38,18 +38,23 @@
namespace rclcpp_lifecycle
{
LifecycleNode::LifecycleNode(const std::string & node_name, bool use_intra_process_comms)
LifecycleNode::LifecycleNode(
const std::string & node_name,
const std::string & namespace_,
bool use_intra_process_comms)
: LifecycleNode(
node_name,
namespace_,
rclcpp::contexts::default_context::get_global_default_context(),
use_intra_process_comms)
{}
LifecycleNode::LifecycleNode(
const std::string & node_name,
const std::string & namespace_,
rclcpp::context::Context::SharedPtr context,
bool use_intra_process_comms)
: node_base_(new rclcpp::node_interfaces::NodeBase(node_name, context)),
: node_base_(new rclcpp::node_interfaces::NodeBase(node_name, namespace_, 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())),
@@ -84,6 +89,12 @@ LifecycleNode::get_name() const
return node_base_->get_name();
}
const char *
LifecycleNode::get_namespace() const
{
return node_base_->get_namespace();
}
rclcpp::callback_group::CallbackGroup::SharedPtr
LifecycleNode::create_callback_group(
rclcpp::callback_group::CallbackGroupType group_type)
@@ -151,10 +162,16 @@ LifecycleNode::list_parameters(
return node_parameters_->list_parameters(prefixes, depth);
}
std::map<std::string, std::string>
LifecycleNode::get_topic_names_and_types() const
std::map<std::string, std::vector<std::string>>
LifecycleNode::get_topic_names_and_types(bool no_demangle) const
{
return node_graph_->get_topic_names_and_types();
return node_graph_->get_topic_names_and_types(no_demangle);
}
std::map<std::string, std::vector<std::string>>
LifecycleNode::get_service_names_and_types() const
{
return node_graph_->get_service_names_and_types();
}
size_t
@@ -289,11 +306,46 @@ LifecycleNode::trigger_transition(const Transition & transition)
}
const State &
LifecycleNode::trigger_transition(unsigned int transition_id)
LifecycleNode::trigger_transition(uint8_t transition_id)
{
return impl_->trigger_transition(transition_id);
}
const State &
LifecycleNode::configure()
{
return impl_->trigger_transition(
lifecycle_msgs::msg::Transition::TRANSITION_CONFIGURE);
}
const State &
LifecycleNode::cleanup()
{
return impl_->trigger_transition(
lifecycle_msgs::msg::Transition::TRANSITION_CLEANUP);
}
const State &
LifecycleNode::activate()
{
return impl_->trigger_transition(
lifecycle_msgs::msg::Transition::TRANSITION_ACTIVATE);
}
const State &
LifecycleNode::deactivate()
{
return impl_->trigger_transition(
lifecycle_msgs::msg::Transition::TRANSITION_DEACTIVATE);
}
const State &
LifecycleNode::shutdown()
{
return impl_->trigger_transition(
lifecycle_msgs::msg::Transition::TRANSITION_SHUTDOWN);
}
void
LifecycleNode::add_publisher_handle(
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisherInterface> pub)

View File

@@ -35,6 +35,7 @@
#include "rcl/error_handling.h"
#include "rcl_lifecycle/rcl_lifecycle.h"
#include "rcl_lifecycle/transition_map.h"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
@@ -60,18 +61,21 @@ public:
~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());
rcl_node_t * node_handle = node_base_interface_->get_rcl_node_handle();
const rcl_node_options_t * node_options = rcl_node_get_options(node_handle);
auto ret = rcl_lifecycle_state_machine_fini(
&state_machine_, node_handle, &node_options->allocator);
if (ret != RCL_RET_OK) {
fprintf(stderr, "FATAL: failed to destroy rcl_state_machine\n");
}
}
void
init()
{
rcl_node_t * node_handle = node_base_interface_->get_rcl_node_handle();
const rcl_node_options_t * node_options =
rcl_node_get_options(node_base_interface_->get_rcl_node_handle());
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
@@ -80,17 +84,19 @@ public:
// 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),
&state_machine_,
node_handle,
ROSIDL_GET_MSG_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);
true,
&node_options->allocator);
if (ret != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Couldn't initialize state machine for node ") + node_base_interface_->get_name());
std::string("Couldn't initialize state machine for node ") +
node_base_interface_->get_name());
}
{ // change_state
@@ -202,7 +208,7 @@ public:
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) {
for (uint8_t 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);
@@ -223,7 +229,7 @@ public:
return;
}
for (unsigned int i = 0; i < state_machine_.transition_map.transitions_size; ++i) {
for (uint8_t 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;
@@ -247,7 +253,7 @@ public:
get_available_states()
{
std::vector<State> states;
for (unsigned int i = 0; i < state_machine_.transition_map.states_size; ++i) {
for (uint8_t i = 0; i < state_machine_.transition_map.states_size; ++i) {
State state(&state_machine_.transition_map.states[i]);
states.push_back(state);
}
@@ -259,7 +265,7 @@ public:
{
std::vector<Transition> transitions;
for (unsigned int i = 0; i < state_machine_.transition_map.transitions_size; ++i) {
for (uint8_t i = 0; i < state_machine_.transition_map.transitions_size; ++i) {
Transition transition(
&state_machine_.transition_map.transitions[i]);
transitions.push_back(transition);
@@ -279,7 +285,7 @@ public:
// 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);
uint8_t transition_id = 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,
@@ -304,16 +310,20 @@ public:
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");
// 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);
if (rcl_lifecycle_trigger_transition(&state_machine_, error_resolved, true) != RCL_RET_OK) {
fprintf(stderr, "Failed to call cleanup on error state\n");
return false;
}
// 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);
if (rcl_lifecycle_trigger_transition(&state_machine_, error_resolved, true) != RCL_RET_OK) {
fprintf(stderr, "Failed to call cleanup on error state\n");
return false;
}
}
}
// This true holds in both cases where the actual callback
@@ -348,7 +358,7 @@ public:
}
const State &
trigger_transition(unsigned int transition_id)
trigger_transition(uint8_t transition_id)
{
change_state(transition_id);
return get_current_state();

View File

@@ -15,6 +15,9 @@
#include "rclcpp_lifecycle/state.hpp"
#include <lifecycle_msgs/msg/state.hpp>
#include <rcutils/allocator.h>
#include <rcutils/strdup.h>
#include <rcl_lifecycle/data_types.h>
#include <string>
@@ -26,7 +29,7 @@ State::State()
: State(lifecycle_msgs::msg::State::PRIMARY_STATE_UNKNOWN, "unknown")
{}
State::State(unsigned int id, const std::string & label)
State::State(uint8_t id, const std::string & label)
: owns_rcl_state_handle_(true)
{
if (label.empty()) {
@@ -35,7 +38,8 @@ State::State(unsigned int id, const std::string & label)
auto state_handle = new rcl_lifecycle_state_t;
state_handle->id = id;
state_handle->label = label.c_str();
state_handle->label =
rcutils_strndup(label.c_str(), label.size(), rcutils_get_default_allocator());
state_handle_ = state_handle;
}
@@ -53,15 +57,21 @@ State::~State()
}
}
unsigned int
uint8_t
State::id() const
{
if (!state_handle_) {
throw std::runtime_error("Error in state! Internal state_handle is NULL.");
}
return state_handle_->id;
}
std::string
State::label() const
{
if (!state_handle_) {
throw std::runtime_error("Error in state! Internal state_handle is NULL.");
}
return state_handle_->label;
}

View File

@@ -22,7 +22,7 @@
namespace rclcpp_lifecycle
{
Transition::Transition(unsigned int id, const std::string & label)
Transition::Transition(uint8_t id, const std::string & label)
: owns_rcl_transition_handle_(true)
{
auto transition_handle = new rcl_lifecycle_transition_t;
@@ -35,7 +35,7 @@ Transition::Transition(unsigned int id, const std::string & label)
}
Transition::Transition(
unsigned int id, const std::string & label,
uint8_t id, const std::string & label,
State && start, State && goal)
: owns_rcl_transition_handle_(true)
{
@@ -75,7 +75,7 @@ Transition::~Transition()
}
}
unsigned int
uint8_t
Transition::id() const
{
return transition_handle_->id;
@@ -90,17 +90,13 @@ Transition::label() const
State
Transition::start_state() const
{
return State(
transition_handle_->start->id,
transition_handle_->start->label);
return State(transition_handle_->start);
}
State
Transition::goal_state() const
{
return State(
transition_handle_->goal->id,
transition_handle_->goal->label);
return State(transition_handle_->goal);
}
} // namespace rclcpp_lifecycle

View File

@@ -123,7 +123,8 @@ rcl_lifecycle_ret_t MoodyLifecycleNode<BadMood>::on_error(const rclcpp_lifecycle
TEST_F(TestDefaultStateMachine, empty_initializer) {
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
EXPECT_STREQ("testnode", test_node->get_name());
EXPECT_STREQ("/", test_node->get_namespace());
EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, test_node->get_current_state().id());
}

View File

@@ -34,40 +34,40 @@ TEST_F(TestStateMachineInfo, available_states) {
auto test_node = std::make_shared<rclcpp_lifecycle::LifecycleNode>("testnode");
std::vector<rclcpp_lifecycle::State> available_states =
test_node->get_available_states();
EXPECT_EQ((unsigned int)11, available_states.size());
EXPECT_EQ(11u, available_states.size());
// Primary States
EXPECT_EQ((unsigned int)0, available_states[0].id()); // unknown
EXPECT_EQ((unsigned int)1, available_states[1].id()); // unconfigured
EXPECT_EQ((unsigned int)2, available_states[2].id()); // inactive
EXPECT_EQ((unsigned int)3, available_states[3].id()); // active
EXPECT_EQ((unsigned int)4, available_states[4].id()); // finalized
EXPECT_EQ(0, available_states[0].id()); // unknown
EXPECT_EQ(1, available_states[1].id()); // unconfigured
EXPECT_EQ(2, available_states[2].id()); // inactive
EXPECT_EQ(3, available_states[3].id()); // active
EXPECT_EQ(4, available_states[4].id()); // finalized
// Transition States
EXPECT_EQ((unsigned int)10, available_states[5].id()); // configuring
EXPECT_EQ((unsigned int)11, available_states[6].id()); // cleaningup
EXPECT_EQ((unsigned int)12, available_states[7].id()); // shuttingdown
EXPECT_EQ((unsigned int)13, available_states[8].id()); // activating
EXPECT_EQ((unsigned int)14, available_states[9].id()); // deactivating
EXPECT_EQ((unsigned int)15, available_states[10].id()); // errorprocessing
EXPECT_EQ(10, available_states[5].id()); // configuring
EXPECT_EQ(11, available_states[6].id()); // cleaningup
EXPECT_EQ(12, available_states[7].id()); // shuttingdown
EXPECT_EQ(13, available_states[8].id()); // activating
EXPECT_EQ(14, available_states[9].id()); // deactivating
EXPECT_EQ(15, available_states[10].id()); // errorprocessing
}
TEST_F(TestStateMachineInfo, available_transitions) {
auto test_node = std::make_shared<rclcpp_lifecycle::LifecycleNode>("testnode");
std::vector<rclcpp_lifecycle::Transition> available_transitions =
test_node->get_available_transitions();
EXPECT_EQ((unsigned int)25, available_transitions.size());
EXPECT_EQ(25u, available_transitions.size());
for (rclcpp_lifecycle::Transition & transition : available_transitions) {
EXPECT_FALSE(transition.label().empty());
EXPECT_TRUE(transition.start_state().id() <= (unsigned int)4 ||
(transition.start_state().id() >= (unsigned int)10 &&
(transition.start_state().id() <= (unsigned int)15)));
EXPECT_TRUE(transition.start_state().id() <= 4 ||
(transition.start_state().id() >= 10 &&
(transition.start_state().id() <= 15)));
EXPECT_FALSE(transition.start_state().label().empty());
EXPECT_TRUE(transition.goal_state().id() <= (unsigned int)4 ||
(transition.goal_state().id() >= (unsigned int)10 &&
(transition.goal_state().id() <= (unsigned int)15)));
EXPECT_TRUE(transition.goal_state().id() <= 4 ||
(transition.goal_state().id() >= 10 &&
(transition.goal_state().id() <= 15)));
EXPECT_FALSE(transition.goal_state().label().empty());
}
}

View File

@@ -0,0 +1,77 @@
// Copyright 2017 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 <vector>
#include "rclcpp_lifecycle/lifecycle_node.hpp"
class TestStateWrapper : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
}
};
TEST_F(TestStateWrapper, wrapper) {
{
rclcpp_lifecycle::State state(1, "my_state");
EXPECT_EQ(1, state.id());
EXPECT_FALSE(state.label().empty());
EXPECT_STREQ("my_state", state.label().c_str());
}
{
rcl_lifecycle_state_t lc_state = {"my_c_state", 2, NULL, NULL, 0};
rclcpp_lifecycle::State c_state(lc_state.id, lc_state.label);
EXPECT_EQ(2, c_state.id());
EXPECT_FALSE(c_state.label().empty());
EXPECT_STREQ("my_c_state", c_state.label().c_str());
}
{
rcl_lifecycle_state_t lc_state = {"my_c_state", 2, NULL, NULL, 0};
rclcpp_lifecycle::State c_state(&lc_state);
EXPECT_EQ(2, c_state.id());
EXPECT_FALSE(c_state.label().empty());
EXPECT_STREQ("my_c_state", c_state.label().c_str());
}
{
rcl_lifecycle_state_t * lc_state =
new rcl_lifecycle_state_t {"my_c_state", 3, NULL, NULL, 0};
rclcpp_lifecycle::State c_state(lc_state->id, lc_state->label);
EXPECT_EQ(3, c_state.id());
EXPECT_FALSE(c_state.label().empty());
EXPECT_STREQ("my_c_state", c_state.label().c_str());
}
// introduces flakiness
// unsupported behavior!
// fails when compiled with memory sanitizer
// {
// rcl_lifecycle_state_t * lc_state
// = new rcl_lifecycle_state_t {"my_c_state", 3, NULL, NULL, 0};
// rclcpp_lifecycle::State c_state(lc_state);
// delete lc_state;
// lc_state = NULL;
// EXPECT_EQ(3, c_state.id());
// EXPECT_STREQ("my_c_state", c_state.label().c_str());
// }
}