Compare commits

..

91 Commits

Author SHA1 Message Date
Steven! Ragnarök
c8f3fd3b0e 0.6.0 2018-11-19 07:47:26 -08:00
William Woodall
33a755c535 use new error handling API from rcutils (#577)
Signed-off-by: William Woodall <william@osrfoundation.org>
2018-11-01 21:08:54 -05:00
Karsten Knese
ec834d321b delete TRANSITION_SHUTDOWN (#576) 2018-10-31 19:20:03 -07:00
Francisco Martín Rico
e30f31551e issue a warning if publishing on a not active publisher (#574)
* issue a warning if publishing on a not active publisher

* Adding a logger private member in LifecyclePublisher for avoiding creating a new one echa call
2018-10-27 17:35:17 -07:00
Francisco Martín Rico
b600c18121 Providing logging macro signature that accepts std::string (#573)
* Providing logging macro signature that accepts std::string

* - RCLCPP_ prefix to macros Add
- New tests added

* - Added doc to the functions and macros
- Functions declared as RCLCPP_PUBLIC

* - Small typo in doc corrected

* Fixed error when compiling with clang

* touch up docs
2018-10-25 15:49:38 -07:00
cho3
144c24c8fd Add SMART_PTRS_DEF to LifecyclePublisher (#569) 2018-10-11 17:33:41 -07:00
Karsten Knese
3353ffbb15 service for transition graph (#555)
* service for transition graph

* remove keys, transition id unique, label ambiguous

* semicolon for macro call
2018-10-11 14:03:57 -07:00
Chris Lalancette
bedb3ae361 Add virtual destructors to classes with virtual functions. (#566)
This fixes the build on MacOS High Sierra and later, and
is the more correct thing to do anyway.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2018-10-08 11:02:08 -04:00
Chris Lalancette
b3cbf06c09 Add semicolons to all RCLCPP and RCUTILS macros. (#565)
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2018-10-05 17:30:22 -04:00
Anis Ladram
eb439ddc73 Removing std::binary_function usage (#561)
Deprecated in C++11, removed in C++17
2018-10-02 09:49:17 +02:00
dhood
6ff3ff43fe Don't auto-activate ROS time if clock topic is being published (#559)
* Don't auto-activate ROS time if clock topic is being published

* Destroy subscription when not needed, avoid re-creating it

* Additional tests

* Always reset pointer

* Initialise sub in initialiser list
2018-09-25 08:34:25 -07:00
Mikael Arguedas
f6c2f5ba0d use add_compile_options instead of setting only cxx flags 2018-09-20 11:13:23 -07:00
Shane Loretz
e8d3fdd56c Fix cpplint on xenial (#556)
* Change variable name to fix cpplint on xenial

* Set variable to null to satisfy cpplint

* additional null
2018-09-20 09:19:45 -07:00
Chris Lalancette
be8c05ed9e Implement get_parameter_or_set_default. (#551)
* Implement get_parameter_or_set_default.

This is syntactic sugar to allow the user to get a parameter.
If the parameter is already set on the node, it gets the value
of the parameter.  If it is not set, then it gets the alternative
value and sets it on the node, ensuring that it exists.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>

* Review fixes (one sentence per line).

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>

* Rename get_parameter_or_set_default -> get_parameter_or_set

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2018-09-20 09:21:24 -04:00
Shane Loretz
b860b899e5 Add max_duration to spin_some() (#558)
With max_duration spin_some will execute work until it has spent more
time than the elapsed duration.
2018-09-17 15:51:15 -07:00
dhood
86cc8fdb3a Output rcl error message when yaml parsing fails (#557) 2018-09-13 17:46:56 -07:00
dhood
80595f37d1 Link to ticket re rcl_yaml_param_parser avoiding circular dependency 2018-09-13 17:10:39 -07:00
Shane Loretz
b1af28047c Make sure timer is fini'd before clock (#553)
* Make sure timer is fini'd before clock
2018-09-07 17:28:10 -07:00
Michael Carroll
8c6f38a0fa Get node names and namespaces (#545)
* Rework to account for new get_node_names signiture.

* cpplint.

* Address reviewer feedback.
2018-09-06 08:02:44 -05:00
William Woodall
198c6daf49 Doc fixups (#546)
* add missing docs for number_of_threads parameter

* add missing docs for start_parameter_services parameter

* add missing docs for parameters, rename short variable name

* doc fixups
2018-08-31 18:32:20 -07:00
Shane Loretz
a55e320e6e Use rcl_clock_t jump callbacks (#543)
* Use rcl_clock_t jump callbacks

Relieves rclcpp::TimeSource responsibility of calling jump callbacks.
2018-08-28 10:12:12 -07:00
Shane Loretz
4653bfcce6 Rcl consolidate wait set functions (#540)
* Use consolidated rcl_wait_set_clear()

* Use consolidated rcl_wait_set_resize()
2018-08-27 11:55:04 -07:00
Sagnik Basu
18ad26e654 Add TIME_MAX and DURATION_MAX functions (#538)
* Add TIME_MAX and DURATION_MAX functions

* Fix Linting Errors

* change funtion name as per coding style

* change function name as per coding style

* Update duration.cpp

* Update time.cpp

* Update test_duration.cpp

* Update time.hpp

* remove extra empty line
2018-08-27 11:44:25 -07:00
Karsten Knese
354d933870 publish shared_ptr of rcl_serialized_message (#541)
* publish shared_ptr of rcl_serialized_message

* const parameter
2018-08-24 14:34:51 -05:00
Dirk Thomas
25a9b4e339 add Time::is_zero and Duration::seconds (#536)
add Duration::seconds
2018-08-20 08:58:32 -07:00
Karsten Knese
45d74ba4dc log error message instead of throwing exception in destructor (#535) 2018-08-17 10:17:37 -07:00
dhood
e409e44413 Relax tolerance of now test because timing affected by OS scheduling (#533) 2018-08-17 10:03:45 -07:00
Shane Loretz
6b34bcc94c Remove incorrect exception on sec < 0 (#527)
* Remove incorrect exception on sec < 0
2018-08-09 09:23:33 -07:00
Shane Loretz
ea047655d8 Add rclcpp::Time::seconds() (#526)
* Get seconds since epoch as double
2018-08-08 16:04:35 -07:00
Dirk Thomas
4ddb76f466 construct TimerBase/GenericTimer with Clock (#523)
* construct TimerBase/GenericTimer with Clock

* pass rcl_time_point_value_t to rcl_clock_get_now

* update docblocks
2018-07-27 18:27:25 -07:00
chapulina
fba891c0df Implement rclcpp::is_initialized() (#522)
* Implement rclcpp::is_initialized()

* linter
2018-07-26 13:17:33 -07:00
dhood
8f2052d65a Support jump handlers with only pre- or post-jump callback (#517)
* Add failing tests for partial jump handlers

* Code deduplication

* Check callbacks before calling them
2018-07-18 07:11:35 +10:00
Mikael Arguedas
3067a72a2a nothing uses std_msgs anymore (#513) 2018-07-17 13:24:04 -07:00
Dirk Thomas
0ad17575a2 remove use of uninitialized CMake var (#512) 2018-07-11 14:08:09 -07:00
Mikael Arguedas
ae6f8e3e9a Uncrustify 0.67 (#510)
* fix indentation to comply with uncrusity 0.67

* fix spacing before opening brackets

* space between reference and variable name in signature

* questionable space between pointer marker and variable name
2018-07-11 08:31:11 -07:00
Dirk Thomas
d36910d2d7 remove use of uninitialized CMake var (#511) 2018-07-10 16:51:09 -07:00
Sriram Raghunathan
93e2945802 Expose get_node_names API from node. (#508)
* Exposing get_node_names from node handle

Signed-off-by: Sriram Raghunathan <rsriram7@visteon.com>

* Exposing get_node_names from node handle for lifecycle_nodes

Signed-off-by: Sriram Raghunathan <rsriram7@visteon.com>

* Fix stray demangle type
2018-07-05 17:45:09 -07:00
Mikael Arguedas
4507d7a40b Fix rosidl dependencies (#507)
* [rclcpp_lifecycle] remove rosidl deps as this package doesnt generate any messages

* depend on rosidl_typesupport_cpp
2018-07-05 13:01:23 -07:00
Dirk Thomas
1869b84a0c 0.5.1 2018-06-28 16:34:33 -07:00
William Woodall
a49281cff3 keep shared pointer reference to rcl_node handle for subscription fini (#505) 2018-06-27 13:37:49 -07:00
William Woodall
4d67a8671b 0.5.0 2018-06-25 16:31:01 -07:00
William Woodall
f5c3854585 changelogs 2018-06-25 16:30:51 -07:00
William Woodall
39c22c8508 typo (#502) 2018-06-19 12:43:06 -07:00
Michael Carroll
bf89dc0797 Further expand test tolerance to address flakiness. (#501)
* Further expand test tolerance to address flakiness.

* Remove newline.
2018-06-19 13:06:30 -05:00
William Woodall
62c8c5b762 executor could take more than once incorrectly (#383)
* Baseline test and force threads to yield.

* Add timer tracking for executor.

* Add locking and test happy-path exit conditions.

* Move logic to multi_threaded_executor

* Address reviewer feedback by reducing scope of set.

* Expand tolerance on testing.

* comment fixup

Otherwise it seemed to me like it would yield twice.
2018-06-18 22:46:28 -05:00
Karsten Knese
d33a46c3b6 comply to new subscription template (#500) 2018-06-19 00:20:55 +02:00
William Woodall
bfbb263f3c fix tests due to changes in rcutils (#452) 2018-06-16 13:48:01 -07:00
Karsten Knese
ec17d68b41 *_raw function (#388)
* publish_raw function

* subscription traits

* listener raw

* rebased

* cleanup and linters

* explicit test for deleter in unique_ptr

* add missing copyright

* cleanup

* add rmw_serialize functions

* linters

* explicit differentiation between take and take_raw

* cleanup debug messages

* rename to rmw_message_init`

* address comments

* address review comments

* raw->serialized

* use size_t (#497)
2018-06-16 10:36:00 +02:00
William Woodall
1556b6edf4 always get service name from rcl to account for remapping (#498) 2018-06-14 21:42:00 -07:00
Mikael Arguedas
1b87970d8e add missing set_parameters_atomically client (#494) 2018-06-12 01:53:40 +02:00
Shane Loretz
4886b2485c Initialize params via yaml from command line (#488)
* Initialize params from yaml files
2018-06-08 17:24:29 -07:00
Shane Loretz
9b294ec720 Get parameters that aren't set (#493)
* Document get_parameters()

* Return NOT_SET params in get params service
2018-06-06 10:23:42 -07:00
Shane Loretz
84c8d58612 Pass initial parameter values to node constructor (#486)
* Pass parameter values to node constructor
2018-06-05 15:29:20 -07:00
Shane Loretz
8f793fdb4a Convert rcl_params_t to ParameterMap (#485)
Convert from rcl_params_t to map of node parameters

Adds rclcpp::parameter_map_from(const rcl_params_t * const)
Adds rclcpp::parameter_value_from(const rcl_variant_t * const)
Adds dependency on rcl_yaml_param_parser
2018-06-05 10:54:08 -07:00
Dirk Thomas
5ab6bde1db fully delete parameters (#489)
* allow ParameterValue class to be copy constructed with type not-set

* actually remove deleted parameters from the map
2018-06-04 15:06:01 -07:00
dhood
2a17232ad0 Subscription tests using bind in member callback (#480)
* Add tests for member callbacks

* Add tests for member callback in Test class (not working with gcc7)

* Uncomment test that was failing

* Linter fixup
2018-06-04 12:48:02 -07:00
Shane Loretz
d298fa4445 Split ParameterVariant into Parameter and ParameterValue (#481)
* Split ParametrVariant into Parameter and ParameterValue
* Test expects ParameterTypeException
* get_parameter_value() -> get_value_message()
* Make to_parameter() const and rename to to_parameter_msg()
2018-06-01 11:48:56 -07:00
Esteve Fernandez
97575fd59b Relax template matching rules for std::bind and GNU C++ >= 7.1 (#484)
* Relax template matching rules for std::bind and GNU C++ >= 7.1

* Document reason test was added
2018-06-01 13:28:45 -04:00
Ernesto Corbellini
d6057270f2 Use rosgraph_msgs/Clock for /clock topic. (#474)
* Use rosgraph_msgs/Clock for /clock topic.

* Update the test cases.
2018-05-31 01:32:08 +02:00
Shane Loretz
4efcd330fe Spin before checking if clock changed (#483) 2018-05-30 15:09:30 -07:00
Shane Loretz
d82ce9666c Autostart parameter services (#478)
* Autostart parameter services
* Add bool start_parameter_services
2018-05-25 13:07:59 -07:00
dhood
f9a78df9fe Workaround for wait_for_service lasting the full timeout with connext (#476)
* Limit wait_for_graph_change timeout as alternative workaround for connext

* Increase max wait time to 100ms
2018-05-22 21:01:47 -04:00
Tom Moore
15d505ec1f Adding parameter array support (#443)
* Adding parameter array support

* PR feedback

* Matching changes in upstream branch

* EXPECT_EQ takes expected value a first argument and actual as second
2018-05-10 16:05:52 -07:00
Michael Carroll
1be4d2d914 Fix bug when mixing shared_ptr and bind. (#470) 2018-05-04 13:10:47 -05:00
Shane Loretz
7cd8429534 Fini arguments passed to rcl_node_init() (#468)
* Fix memory leak in node_base
* Always free arguments
2018-05-01 12:59:15 -07:00
Mikael Arguedas
66a7c62531 update to rcutils logging in commented code as well (#466) 2018-04-27 15:31:08 -07:00
William Woodall
1610fc3973 pass AnyExecutable objects as reference to avoid memory allocation (#463)
* pass AnyExecutable objects as reference to avoid memory allocation

* remove style change
2018-04-17 21:54:42 -05:00
Shane Loretz
360f1b9425 Add CLI args to Node constructor (#461)
* Add CLI args to Node constructor

Adds arguments and use_global_arguments to NodeBase

* Check for integer overflow
2018-04-17 10:52:49 -07:00
Michael Carroll
07e5be7621 Correctly clean up arguments structure. (#459)
* Correctly clean up arguments structure.
* Use zero-initialized structure.
2018-04-11 12:43:18 -05:00
Dirk Thomas
45dcd0c6e5 handle node names which are null (#435) 2018-03-30 10:56:06 -07:00
Matthew
fa81d95e33 Add argument for thread count to multithreaded executor (#442) 2018-03-29 17:18:00 -07:00
Michael Carroll
ef17ec6248 Remove ros arguments (#454)
* Mark arguments vector as const.

* Add C++ version of rcl_remove_ros_arguments
2018-03-27 14:57:23 -07:00
Dirk Thomas
5f1fc660ea change export order for static linking (#446) 2018-03-22 16:26:42 -07:00
jwang11
947e3f7e67 Fix time type issue in time unittest (#453)
Obviously it mean RCL_SYSTEM_TIME but not RCL_ROS_TIME in some test cases

Signed-off-by: jwang <jing.j.wang@intel.com>
2018-03-20 15:43:40 -07:00
William Woodall
9ce5aaa792 Revert "Revert "Store the subscriber, client, service and timer"" (#449)
* Revert "Revert "Store the subscriber, client, service and timer (#431)" (#448)"

This reverts commit 168d75cf1e.

* Convert all rcl_*_t types to shared pointers

Converts all rcl_*_t types in the memory allocation strategy to shared pointers to prevent crash happening when a subscriber is reset.

Issue: #349

* fixups
2018-03-19 21:05:26 -07:00
jwang
af6e86c522 Make rclcpp::Duration support scale operation
Duration scale is a convinient operation which had supported in ROS.
This commit make ROS2 support it.

Signed-off-by: jwang <jing.j.wang@intel.com>
2018-03-16 00:56:32 -07:00
William Woodall
d8abea55ec make log location parameter const (#451) 2018-03-15 18:05:15 -07:00
William Woodall
168d75cf1e Revert "Store the subscriber, client, service and timer (#431)" (#448)
This reverts commit 36526469c7.
2018-03-13 18:37:52 -07:00
Denise Eng
36526469c7 Store the subscriber, client, service and timer (#431)
* Convert all rcl_*_t types to shared pointers

Converts all rcl_*_t types in the memory allocation strategy to shared pointers to prevent crash happening when a subscriber is reset.

Issue: #349

* fixup! Convert all rcl_*_t types to shared pointers

* fix { use on function definitions

We always put the { on a new line for function definitions and class declarations.
2018-03-12 11:26:11 -07:00
Dirk Thomas
1a604b0c28 update style (#445) 2018-03-01 08:58:52 -08:00
Mikael Arguedas
2b7cb21cbd advise to ask questions on ROS answers 2018-02-26 22:01:36 -08:00
dhood
787de6ebf1 Get node's logger name from rcl (#433)
* Get logger name from rcl [direct]

* Get logger name from rcl [indirect]

* Update tests

* fixup on variable usage

* Move get_logger_name to NodeLogging interface
2018-02-26 14:36:10 -08:00
dhood
3786c91deb Use ament_cmake_ros (#444)
* Control shared/static linking via BUILD_SHARED_LIBS

* Remove rmw dependency

* Add for rclcpp_lifecycle too

* exec depend on ament_cmake is for normalize_path
2018-02-26 09:46:44 -08:00
Guillaume Autran
0e79842b6b rclcpp logging still uses fprintf all over the place. (#439)
* rclcpp logging still uses fprintf all over the place.

Remove all printf log lines and replace with RCLUTILS_LOG_XXX macros.

Issue: #438

* fixup include order
2018-02-23 17:24:37 -08:00
William Woodall
f88ade7a2a avoid using invalid iterator when erasing items using an iterator in a loop (#436)
* avoid using invalid iterator when erasing items using an iterator in a loop

* do not overwrite iterator in cases where it will be unused
2018-02-02 16:46:24 -08:00
serge-nikulin
3a503685bf change rcutils_time_point_value_t type from uint64_t to int64_t (#429)
* change rcutils_time_point_value_t type from uint64_t to int64_t

* small style changes

* fix test time datatype

* Update time primatives to int64_t

* change time primitive datatype to signed

* A few more instances of UL to L
2018-02-01 13:50:33 -08:00
Mikael Arguedas
e4b5c0bbb9 Byte array parameter rename (#428)
* rename bytes_value to byte_values

* adapt enum to new names

* rename byte_values to byte_array_values

* linters
2018-01-26 15:03:22 -08:00
jwang11
e08c80052a Move clear wait set from after rcl_wait to ahead (#427)
* Move clear wait set from after rcl_wait to ahead

Current code clear wait set after rcl_wait, it is not respond latency
friendly. In fact, clear wait set operation is not urgent, making sure it is
done before next rcl_wait should be fine.

Signed-off-by: jwang <jing.j.wang@intel.com>

* remove trailing whitespace
2018-01-18 18:49:52 -08:00
Ethan Gao
b81f55e5df Fix the dereference to NULL (#405)
* Fix the dereference to NULL
rmw_*_validation_result_string(validation_result) may return NULL,
and it's dereferenced by passing arg to NameValidationError

Signed-off-by: Ethan Gao <ethan.gao@linux.intel.com>

* address NULL case of undefined type

Signed-off-by: Ethan Gao <ethan.gao@linux.intel.com>

* Address the issue to deference to NULL with adapt
to the change of API rmw_*_validation_result_string

Signed-off-by: Ethan Gao <ethan.gao@linux.intel.com>

* revise the typo

* throw exception when valid rmw check but invalid rcl check

Signed-off-by: Ethan Gao <ethan.gao@linux.intel.com>
2018-01-09 06:02:23 +11:00
Sriram Raghunathan
2bf688827b Change rmw_count_publishers API, to rcl equivalent rcl_count_publishe… (#425)
* Change rmw_count_publishers API, to rcl equivalent rcl_count_publishers and remove the TODO line.

Signed-off-by: Sriram Raghunathan <rsriram7@visteon.com>

* Remove rmw_handle and refer to rcl_node_handle, change the API signature to topic_names.

Signed-off-by: Sriram Raghunathan <rsriram7@visteon.com>

* Use rcl_* specific functions to derive the fully qualified topic name.

Signed-off-by: Sriram Raghunathan <rsriram7@visteon.com>

* [nitpick] remove unnecessary variable storage
2017-12-19 19:45:58 -08:00
Ethan Gao
199a26984d Fix the potential application crash issues (#426)
* err msg

* err msg

* Fix the potential application crash issues

Signed-off-by: Ethan Gao <ethan.gao@linux.intel.com>

* minor tweak the code structure

Signed-off-by: Ethan Gao <ethan.gao@linux.intel.com>
2017-12-19 16:39:56 -05:00
111 changed files with 5642 additions and 1747 deletions

View File

@@ -1,5 +1,6 @@
<!--
For general questions, please post on discourse: https://discourse.ros.org/c/ng-ros
For general questions, please ask on ROS answers: https://answers.ros.org, make sure to include at least the `ros2` tag and the rosdistro version you are running, e.g. `ardent`.
For general design discussions, please post on discourse: https://discourse.ros.org/c/ng-ros
Not sure if this is the right repository? Open an issue on https://github.com/ros2/ros2/issues
For Bug report or feature requests, please fill out the relevant category below
-->

79
rclcpp/CHANGELOG.rst Normal file
View File

@@ -0,0 +1,79 @@
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
0.6.0 (2018-11-19)
------------------
* Updated to use new error handling API from rcutils (`#577 <https://github.com/ros2/rclcpp/issues/577>`_)
* Added a warning when publishing if publisher is not active (`#574 <https://github.com/ros2/rclcpp/issues/574>`_)
* Added logging macro signature that accepts std::string (`#573 <https://github.com/ros2/rclcpp/issues/573>`_)
* Added virtual destructors to classes with virtual functions. (`#566 <https://github.com/ros2/rclcpp/issues/566>`_)
* Added semicolons to all RCLCPP and RCUTILS macros. (`#565 <https://github.com/ros2/rclcpp/issues/565>`_)
* Removed std::binary_function usage (`#561 <https://github.com/ros2/rclcpp/issues/561>`_)
* Updated to avoid auto-activating ROS time if clock topic is being published (`#559 <https://github.com/ros2/rclcpp/issues/559>`_)
* Fixed cpplint on xenial (`#556 <https://github.com/ros2/rclcpp/issues/556>`_)
* Added get_parameter_or_set_default. (`#551 <https://github.com/ros2/rclcpp/issues/551>`_)
* Added max_duration to spin_some() (`#558 <https://github.com/ros2/rclcpp/issues/558>`_)
* Updated to output rcl error message when yaml parsing fails (`#557 <https://github.com/ros2/rclcpp/issues/557>`_)
* Updated to make sure timer is fini'd before clock (`#553 <https://github.com/ros2/rclcpp/issues/553>`_)
* Get node names and namespaces (`#545 <https://github.com/ros2/rclcpp/issues/545>`_)
* Fixed and improved documentation (`#546 <https://github.com/ros2/rclcpp/issues/546>`_)
* Updated to use rcl_clock_t jump callbacks (`#543 <https://github.com/ros2/rclcpp/issues/543>`_)
* Updated to use rcl consolidated wait set functions (`#540 <https://github.com/ros2/rclcpp/issues/540>`_)
* Addeed TIME_MAX and DURATION_MAX functions (`#538 <https://github.com/ros2/rclcpp/issues/538>`_)
* Updated to publish shared_ptr of rcl_serialized_message (`#541 <https://github.com/ros2/rclcpp/issues/541>`_)
* Added Time::is_zero and Duration::seconds (`#536 <https://github.com/ros2/rclcpp/issues/536>`_)
* Changed to log an error message instead of throwing exception in destructor (`#535 <https://github.com/ros2/rclcpp/issues/535>`_)
* Updated to relax tolerance of now test because timing affected by OS scheduling (`#533 <https://github.com/ros2/rclcpp/issues/533>`_)
* Removed incorrect exception on sec < 0 (`#527 <https://github.com/ros2/rclcpp/issues/527>`_)
* Added rclcpp::Time::seconds() (`#526 <https://github.com/ros2/rclcpp/issues/526>`_)
* Updated Timer API to construct TimerBase/GenericTimer with Clock (`#523 <https://github.com/ros2/rclcpp/issues/523>`_)
* Added rclcpp::is_initialized() (`#522 <https://github.com/ros2/rclcpp/issues/522>`_)
* Added support for jump handlers with only pre- or post-jump callback (`#517 <https://github.com/ros2/rclcpp/issues/517>`_)
* Removed use of uninitialized CMake var (`#512 <https://github.com/ros2/rclcpp/issues/512>`_)
* Updated for Uncrustify 0.67 (`#510 <https://github.com/ros2/rclcpp/issues/510>`_)
* Added get_node_names API from node. (`#508 <https://github.com/ros2/rclcpp/issues/508>`_)
* Contributors: Anis Ladram, Chris Lalancette, Dirk Thomas, Francisco Martín Rico, Karsten Knese, Michael Carroll, Mikael Arguedas, Sagnik Basu, Shane Loretz, Sriram Raghunathan, William Woodall, chapulina, dhood
0.5.0 (2018-06-25)
------------------
* Fixed a bug in the multi-threaded executor which could cause it to take a timer (potentially other types of wait-able items) more than once to be worked one. (`#383 <https://github.com/ros2/rclcpp/issues/383>`_)
* Specifically this could result in a timer getting called more often that it should when using the multi-threaded executor.
* Added functions that allow you to publish serialized messages and received serialized messages in your subscription callback. (`#388 <https://github.com/ros2/rclcpp/issues/388>`_)
* Changed code to always get the Service name from ``rcl`` to ensure the remapped name is returned. (`#498 <https://github.com/ros2/rclcpp/issues/498>`_)
* Added previously missing ``set_parameters_atomically()`` method to the Service client interface. (`#494 <https://github.com/ros2/rclcpp/issues/494>`_)
* Added ability to initialize parameter values in a Node via a YAML file passed on the command line. (`#488 <https://github.com/ros2/rclcpp/issues/488>`_)
* Fixed the ROS parameter interface which got parameters that aren't set. (`#493 <https://github.com/ros2/rclcpp/issues/493>`_)
* Added ability to initialize parameter values in a node with an argument to the Node constructor. (`#486 <https://github.com/ros2/rclcpp/issues/486>`_)
* Added a ``Subscription`` tests which uses ``std::bind`` to a class member callback. (`#480 <https://github.com/ros2/rclcpp/issues/480>`_)
* Refactored the ``ParameterVariant`` class into the ``Parameter`` and ``ParameterValue`` classes. (`#481 <https://github.com/ros2/rclcpp/issues/481>`_)
* Relaxed template matching rules for ``std::bind`` and ``GNU C++ >= 7.1``. (`#484 <https://github.com/ros2/rclcpp/issues/484>`_)
* Changed to use the new ``rosgraph_msgs/Clock`` message type for the ``/clock`` topic. (`#474 <https://github.com/ros2/rclcpp/issues/474>`_)
* Fixed a flaky ROS time test due to not spinning before getting the time. (`#483 <https://github.com/ros2/rclcpp/issues/483>`_)
* Nodes now autostart the ROS parameter services which let you get, set, and list parameters in a node. (`#478 <https://github.com/ros2/rclcpp/issues/478>`_)
* Added support for arrays in Parameters. (`#443 <https://github.com/ros2/rclcpp/issues/443>`_)
* Changed how executors use ``AnyExecutable`` objects so that they are a reference instead of a shared pointer, in order to avoid memory allocation in the "common case". (`#463 <https://github.com/ros2/rclcpp/issues/463>`_)
* Added ability to pass command line arguments to the Node constructor. (`#461 <https://github.com/ros2/rclcpp/issues/461>`_)
* Added an argument to specify the number of threads a multithreaded executor should create. (`#442 <https://github.com/ros2/rclcpp/issues/442>`_)
* Changed library export order for static linking. (`#446 <https://github.com/ros2/rclcpp/issues/446>`_)
* Fixed some typos in the time unit tests. (`#453 <https://github.com/ros2/rclcpp/issues/453>`_)
Obviously it mean RCL_SYSTEM_TIME but not RCL_ROS_TIME in some test cases
* Signed-off-by: jwang <jing.j.wang@intel.com>
* Added the scale operation to ``rclcpp::Duration``.
* Signed-off-by: jwang <jing.j.wang@intel.com>
* Changed API of the log location parameter to be ``const``. (`#451 <https://github.com/ros2/rclcpp/issues/451>`_)
* Changed how the subscriber, client, service, and timer handles are stored to resolve shutdown order issues. (`#431 <https://github.com/ros2/rclcpp/issues/431>`_ and `#448 <https://github.com/ros2/rclcpp/issues/448>`_)
* Updated to get the node's logger name from ``rcl``. (`#433 <https://github.com/ros2/rclcpp/issues/433>`_)
* Now depends on ``ament_cmake_ros``. (`#444 <https://github.com/ros2/rclcpp/issues/444>`_)
* Updaed code to use logging macros rather than ``fprintf()``. (`#439 <https://github.com/ros2/rclcpp/issues/439>`_)
* Fixed a bug that was using an invalid iterator when erasing items using an iterator in a loop. (`#436 <https://github.com/ros2/rclcpp/issues/436>`_)
* Changed code to support move of ``rcutils_time_point_value_t`` type from ``uint64_t`` to ``int64_t``. (`#429 <https://github.com/ros2/rclcpp/issues/429>`_)
* Renamed parameter byte type to ``byte_values`` from ``bytes_value``. (`#428 <https://github.com/ros2/rclcpp/issues/428>`_)
* Changed executor code to clear the wait set before resizing and waiting. (`#427 <https://github.com/ros2/rclcpp/issues/427>`_)
* Fixed a potential dereference of nullptr in the topic name validation error string. (`#405 <https://github.com/ros2/rclcpp/issues/405>`_)
* Signed-off-by: Ethan Gao <ethan.gao@linux.intel.com>
* Changed to use ``rcl_count_publishers()`` like API's rather than the lower level ``rmw_count_publishers()`` API. (`#425 <https://github.com/ros2/rclcpp/issues/425>`_)
* Signed-off-by: Sriram Raghunathan <rsriram7@visteon.com>
* Fix potential segmentation fault due to ``get_topic_name()`` or ``rcl_service_get_service_name()`` returning nullptr and that not being checked before access in ``rclcpp``. (`#426 <https://github.com/ros2/rclcpp/issues/426>`_)
* Signed-off-by: Ethan Gao <ethan.gao@linux.intel.com>
* Contributors: Denise Eng, Dirk Thomas, Ernesto Corbellini, Esteve Fernandez, Ethan Gao, Guillaume Autran, Karsten Knese, Matthew, Michael Carroll, Mikael Arguedas, Shane Loretz, Sriram Raghunathan, Tom Moore, William Woodall, dhood, jwang, jwang11, serge-nikulin

View File

@@ -2,12 +2,14 @@ cmake_minimum_required(VERSION 3.5)
project(rclcpp)
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_ros REQUIRED)
find_package(builtin_interfaces REQUIRED)
find_package(rcl REQUIRED)
find_package(rcl_interfaces REQUIRED)
find_package(rcl_yaml_param_parser REQUIRED)
find_package(rmw REQUIRED)
find_package(rmw_implementation REQUIRED)
find_package(rosgraph_msgs REQUIRED)
find_package(rosidl_generator_cpp REQUIRED)
find_package(rosidl_typesupport_c REQUIRED)
find_package(rosidl_typesupport_cpp REQUIRED)
@@ -17,12 +19,7 @@ if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# 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")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
include_directories(include)
@@ -58,8 +55,10 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/node_interfaces/node_timers.cpp
src/rclcpp/node_interfaces/node_topics.cpp
src/rclcpp/parameter.cpp
src/rclcpp/parameter_value.cpp
src/rclcpp/parameter_client.cpp
src/rclcpp/parameter_events_filter.cpp
src/rclcpp/parameter_map.cpp
src/rclcpp/parameter_service.cpp
src/rclcpp/publisher.cpp
src/rclcpp/service.cpp
@@ -93,13 +92,16 @@ list(APPEND ${PROJECT_NAME}_SRCS
include/rclcpp/logging.hpp)
include_directories("${CMAKE_CURRENT_BINARY_DIR}/include")
add_library(${PROJECT_NAME} SHARED
add_library(${PROJECT_NAME}
${${PROJECT_NAME}_SRCS})
# specific order: dependents before dependencies
ament_target_dependencies(${PROJECT_NAME}
"builtin_interfaces"
"rcl"
"rosidl_generator_cpp"
"rosidl_typesupport_cpp")
"rcl_yaml_param_parser"
"builtin_interfaces"
"rosgraph_msgs"
"rosidl_typesupport_cpp"
"rosidl_generator_cpp")
# Causes the visibility macros to use dllexport rather than dllimport,
# which is appropriate when building the dll but not consuming it.
@@ -113,18 +115,21 @@ install(
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)
# specific order: dependents before dependencies
ament_export_include_directories(include)
ament_export_libraries(${PROJECT_NAME})
ament_export_dependencies(ament_cmake)
ament_export_dependencies(rcl)
ament_export_dependencies(builtin_interfaces)
ament_export_dependencies(rosgraph_msgs)
ament_export_dependencies(rosidl_typesupport_cpp)
ament_export_dependencies(rosidl_typesupport_c)
ament_export_dependencies(rosidl_generator_cpp)
ament_export_dependencies(rcl_yaml_param_parser)
if(BUILD_TESTING)
find_package(ament_cmake_gtest REQUIRED)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
@@ -189,6 +194,20 @@ if(BUILD_TESTING)
)
target_link_libraries(test_node ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_global_args test/test_node_global_args.cpp)
if(TARGET test_node_global_args)
target_include_directories(test_node_global_args PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_node_global_args ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_initial_parameters test/test_node_initial_parameters.cpp)
if(TARGET test_node_initial_parameters)
target_link_libraries(test_node_initial_parameters ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter_events_filter test/test_parameter_events_filter.cpp)
if(TARGET test_parameter_events_filter)
target_include_directories(test_parameter_events_filter PUBLIC
@@ -199,6 +218,20 @@ if(BUILD_TESTING)
)
target_link_libraries(test_parameter_events_filter ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter test/test_parameter.cpp)
if(TARGET test_parameter)
target_include_directories(test_parameter PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_parameter ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter_map test/test_parameter_map.cpp)
if(TARGET test_parameter_map)
target_link_libraries(test_parameter_map ${PROJECT_NAME})
endif()
ament_add_gtest(test_publisher test/test_publisher.cpp)
if(TARGET test_publisher)
target_include_directories(test_publisher PUBLIC
@@ -209,8 +242,7 @@ if(BUILD_TESTING)
)
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})
ament_add_gtest(test_rate test/test_rate.cpp)
if(TARGET test_rate)
target_include_directories(test_rate PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
@@ -222,6 +254,16 @@ if(BUILD_TESTING)
${PROJECT_NAME}
)
endif()
ament_add_gtest(test_serialized_message_allocator test/test_serialized_message_allocator.cpp)
if(TARGET test_serialized_message_allocator)
target_include_directories(test_serialized_message_allocator PUBLIC
${test_msgs_INCLUDE_DIRS}
)
target_link_libraries(test_serialized_message_allocator
${PROJECT_NAME}
${test_msgs_LIBRARIES}
)
endif()
ament_add_gtest(test_service test/test_service.cpp)
if(TARGET test_service)
target_include_directories(test_service PUBLIC
@@ -242,6 +284,16 @@ if(BUILD_TESTING)
)
target_link_libraries(test_subscription ${PROJECT_NAME})
endif()
find_package(test_msgs REQUIRED)
ament_add_gtest(test_subscription_traits test/test_subscription_traits.cpp)
if(TARGET test_subscription_traits)
target_include_directories(test_subscription_traits PUBLIC
${rcl_INCLUDE_DIRS}
)
ament_target_dependencies(test_subscription_traits
"test_msgs"
)
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
@@ -320,6 +372,30 @@ if(BUILD_TESTING)
"rcl")
target_link_libraries(test_time_source ${PROJECT_NAME})
endif()
ament_add_gtest(test_utilities test/test_utilities.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_utilities)
ament_target_dependencies(test_utilities
"rcl")
target_link_libraries(test_utilities ${PROJECT_NAME})
endif()
ament_add_gtest(test_init test/test_init.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_init)
ament_target_dependencies(test_init
"rcl")
target_link_libraries(test_init ${PROJECT_NAME})
endif()
ament_add_gtest(test_multi_threaded_executor test/executors/test_multi_threaded_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_multi_threaded_executor)
ament_target_dependencies(test_multi_threaded_executor
"rcl")
target_link_libraries(test_multi_threaded_executor ${PROJECT_NAME})
endif()
endif()
ament_package(

View File

@@ -94,11 +94,11 @@ void set_allocator_for_deleter(AllocatorDeleter<T> * deleter, Alloc * alloc)
template<typename Alloc, typename T>
using Deleter = typename std::conditional<
std::is_same<typename std::allocator_traits<Alloc>::template rebind_alloc<T>,
typename std::allocator<void>::template rebind<T>::other>::value,
std::default_delete<T>,
AllocatorDeleter<Alloc>
>::type;
std::is_same<typename std::allocator_traits<Alloc>::template rebind_alloc<T>,
typename std::allocator<void>::template rebind<T>::other>::value,
std::default_delete<T>,
AllocatorDeleter<Alloc>
>::type;
} // namespace allocator
} // namespace rclcpp

View File

@@ -33,8 +33,6 @@ namespace executor
struct AnyExecutable
{
RCLCPP_SMART_PTR_DEFINITIONS(AnyExecutable)
RCLCPP_PUBLIC
AnyExecutable();

View File

@@ -32,16 +32,16 @@ class AnyServiceCallback
{
private:
using SharedPtrCallback = std::function<
void(
const std::shared_ptr<typename ServiceT::Request>,
std::shared_ptr<typename ServiceT::Response>
)>;
void (
const std::shared_ptr<typename ServiceT::Request>,
std::shared_ptr<typename ServiceT::Response>
)>;
using SharedPtrWithRequestHeaderCallback = std::function<
void(
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<typename ServiceT::Request>,
std::shared_ptr<typename ServiceT::Response>
)>;
void (
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<typename ServiceT::Request>,
std::shared_ptr<typename ServiceT::Response>
)>;
SharedPtrCallback shared_ptr_callback_;
SharedPtrWithRequestHeaderCallback shared_ptr_with_request_header_callback_;

View File

@@ -38,15 +38,15 @@ class AnySubscriptionCallback
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
using SharedPtrCallback = std::function<void(const std::shared_ptr<MessageT>)>;
using SharedPtrCallback = std::function<void (const std::shared_ptr<MessageT>)>;
using SharedPtrWithInfoCallback =
std::function<void(const std::shared_ptr<MessageT>, const rmw_message_info_t &)>;
using ConstSharedPtrCallback = std::function<void(const std::shared_ptr<const MessageT>)>;
std::function<void (const std::shared_ptr<MessageT>, const rmw_message_info_t &)>;
using ConstSharedPtrCallback = std::function<void (const std::shared_ptr<const MessageT>)>;
using ConstSharedPtrWithInfoCallback =
std::function<void(const std::shared_ptr<const MessageT>, const rmw_message_info_t &)>;
using UniquePtrCallback = std::function<void(MessageUniquePtr)>;
std::function<void (const std::shared_ptr<const MessageT>, const rmw_message_info_t &)>;
using UniquePtrCallback = std::function<void (MessageUniquePtr)>;
using UniquePtrWithInfoCallback =
std::function<void(MessageUniquePtr, const rmw_message_info_t &)>;
std::function<void (MessageUniquePtr, const rmw_message_info_t &)>;
SharedPtrCallback shared_ptr_callback_;
SharedPtrWithInfoCallback shared_ptr_with_info_callback_;

View File

@@ -36,6 +36,8 @@
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rcutils/logging_macros.h"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
@@ -55,22 +57,21 @@ public:
RCLCPP_PUBLIC
ClientBase(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
const std::string & service_name);
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph);
RCLCPP_PUBLIC
virtual ~ClientBase();
RCLCPP_PUBLIC
const std::string &
const char *
get_service_name() const;
RCLCPP_PUBLIC
rcl_client_t *
std::shared_ptr<rcl_client_t>
get_client_handle();
RCLCPP_PUBLIC
const rcl_client_t *
std::shared_ptr<const rcl_client_t>
get_client_handle() const;
RCLCPP_PUBLIC
@@ -110,8 +111,7 @@ protected:
rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_;
std::shared_ptr<rcl_node_t> node_handle_;
rcl_client_t client_handle_ = rcl_get_zero_initialized_client();
std::string service_name_;
std::shared_ptr<rcl_client_t> client_handle_;
};
template<typename ServiceT>
@@ -130,8 +130,8 @@ public:
using SharedFuture = std::shared_future<SharedResponse>;
using SharedFutureWithRequest = std::shared_future<std::pair<SharedRequest, SharedResponse>>;
using CallbackType = std::function<void(SharedFuture)>;
using CallbackWithRequestType = std::function<void(SharedFutureWithRequest)>;
using CallbackType = std::function<void (SharedFuture)>;
using CallbackWithRequestType = std::function<void (SharedFutureWithRequest)>;
RCLCPP_SMART_PTR_DEFINITIONS(Client)
@@ -140,13 +140,13 @@ public:
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
const std::string & service_name,
rcl_client_options_t & client_options)
: ClientBase(node_base, node_graph, service_name)
: ClientBase(node_base, node_graph)
{
using rosidl_typesupport_cpp::get_service_type_support_handle;
auto service_type_support_handle =
get_service_type_support_handle<ServiceT>();
rcl_ret_t ret = rcl_client_init(
&client_handle_,
this->get_client_handle().get(),
this->get_rcl_node_handle(),
service_type_support_handle,
service_name.c_str(),
@@ -168,11 +168,6 @@ public:
virtual ~Client()
{
if (rcl_client_fini(&client_handle_, this->get_rcl_node_handle()) != RCL_RET_OK) {
fprintf(stderr,
"Error in destruction of rcl client handle: %s\n", rcl_get_error_string_safe());
rcl_reset_error();
}
}
std::shared_ptr<void>
@@ -199,7 +194,9 @@ public:
int64_t sequence_number = request_header->sequence_number;
// TODO(esteve) this should throw instead since it is not expected to happen in the first place
if (this->pending_requests_.count(sequence_number) == 0) {
fprintf(stderr, "Received invalid sequence number. Ignoring...\n");
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Received invalid sequence number. Ignoring...");
return;
}
auto tuple = this->pending_requests_[sequence_number];
@@ -234,7 +231,7 @@ public:
{
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
int64_t sequence_number;
rcl_ret_t ret = rcl_send_request(get_client_handle(), request.get(), &sequence_number);
rcl_ret_t ret = rcl_send_request(get_client_handle().get(), request.get(), &sequence_number);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send request");
}

View File

@@ -31,53 +31,18 @@ namespace rclcpp
class TimeSource;
/// A struct to represent a timejump.
/**
* It represents the time jump duration and whether it changed clock type.
*/
struct TimeJump
{
typedef enum ClockChange_t
{
ROS_TIME_NO_CHANGE, ///< The time type before and after the jump is ROS_TIME
ROS_TIME_ACTIVATED, ///< The time type switched to ROS_TIME from SYSTEM_TIME
ROS_TIME_DEACTIVATED, ///< The time type switched to SYSTEM_TIME from ROS_TIME
SYSTEM_TIME_NO_CHANGE ///< The time type before and after the jump is SYSTEM_TIME
} ClockChange_t;
ClockChange_t jump_type_; ///< The change in clock_type if there is one.
rcl_duration_t delta_; ///< The change in time value.
};
/// A class to store a threshold for a TimeJump.
/**
* This class can be used to evaluate a time jump's magnitude.
*/
class JumpThreshold
{
public:
uint64_t min_forward_; ///< The minimum jump forward to be considered exceeded..
uint64_t min_backward_; ///< The minimum backwards jump to be considered exceeded.
bool on_clock_change_; ///< Whether to trigger on any clock type change.
// Test if the threshold is exceeded by a TimeJump
RCLCPP_PUBLIC
bool
is_exceeded(const TimeJump & jump);
};
class JumpHandler
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(JumpHandler)
JumpHandler(
std::function<void()> pre_callback,
std::function<void(TimeJump)> post_callback,
JumpThreshold & threshold);
std::function<void(const rcl_time_jump_t &)> post_callback,
const rcl_jump_threshold_t & threshold);
std::function<void()> pre_callback;
std::function<void(const TimeJump &)> post_callback;
JumpThreshold notice_threshold;
std::function<void(const rcl_time_jump_t &)> post_callback;
rcl_jump_threshold_t notice_threshold;
};
class Clock
@@ -99,6 +64,10 @@ public:
bool
ros_time_is_active();
RCLCPP_PUBLIC
rcl_clock_t *
get_clock_handle();
RCLCPP_PUBLIC
rcl_clock_type_t
get_clock_type();
@@ -112,32 +81,22 @@ public:
JumpHandler::SharedPtr
create_jump_callback(
std::function<void()> pre_callback,
std::function<void(const TimeJump &)> post_callback,
JumpThreshold & threshold);
std::function<void(const rcl_time_jump_t &)> post_callback,
const rcl_jump_threshold_t & threshold);
private:
// A method for TimeSource to get a list of callbacks to invoke while updating
// Invoke time jump callback
RCLCPP_PUBLIC
std::vector<JumpHandler::SharedPtr>
get_triggered_callback_handlers(const TimeJump & jump);
// Invoke callbacks that are valid and outside threshold.
RCLCPP_PUBLIC
static void invoke_prejump_callbacks(
const std::vector<JumpHandler::SharedPtr> & callbacks);
RCLCPP_PUBLIC
static void invoke_postjump_callbacks(
const std::vector<JumpHandler::SharedPtr> & callbacks,
const TimeJump & jump);
static void
on_time_jump(
const struct rcl_time_jump_t * time_jump,
bool before_jump,
void * user_data);
/// Internal storage backed by rcl
rcl_clock_t rcl_clock_;
friend TimeSource; /// Allow TimeSource to access the rcl_clock_ datatype.
rcl_allocator_t allocator_;
std::mutex callback_list_mutex_;
std::vector<std::weak_ptr<JumpHandler>> active_jump_handlers_;
};
} // namespace rclcpp

View File

@@ -0,0 +1,58 @@
// Copyright 2018 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__CREATE_SERVICE_HPP_
#define RCLCPP__CREATE_SERVICE_HPP_
#include <memory>
#include <string>
#include <utility>
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/rmw.h"
namespace rclcpp
{
/// Create a service with a given type.
/// \internal
template<typename ServiceT, typename CallbackT>
typename rclcpp::Service<ServiceT>::SharedPtr
create_service(
std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
const std::string & service_name,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
rclcpp::AnyServiceCallback<ServiceT> any_service_callback;
any_service_callback.set(std::forward<CallbackT>(callback));
rcl_service_options_t service_options = rcl_service_get_default_options();
service_options.qos = qos_profile;
auto serv = Service<ServiceT>::make_shared(
node_base->get_shared_rcl_node_handle(),
service_name, any_service_callback, service_options);
auto serv_base_ptr = std::dynamic_pointer_cast<ServiceBase>(serv);
node_services->add_service(serv_base_ptr, group);
return serv;
}
} // namespace rclcpp
#endif // RCLCPP__CREATE_SERVICE_HPP_

View File

@@ -26,8 +26,13 @@
namespace rclcpp
{
template<typename MessageT, typename CallbackT, typename AllocatorT, typename SubscriptionT>
typename rclcpp::Subscription<MessageT, AllocatorT>::SharedPtr
template<
typename MessageT,
typename CallbackT,
typename AllocatorT,
typename CallbackMessageT,
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>>
typename std::shared_ptr<SubscriptionT>
create_subscription(
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
const std::string & topic_name,
@@ -36,7 +41,8 @@ create_subscription(
rclcpp::callback_group::CallbackGroup::SharedPtr group,
bool ignore_local_publications,
bool use_intra_process_comms,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, AllocatorT>::SharedPtr
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT, AllocatorT>::SharedPtr
msg_mem_strat,
typename std::shared_ptr<AllocatorT> allocator)
{
@@ -44,8 +50,8 @@ create_subscription(
subscription_options.qos = qos_profile;
subscription_options.ignore_local_publications = ignore_local_publications;
auto factory =
rclcpp::create_subscription_factory<MessageT, CallbackT, AllocatorT, SubscriptionT>(
auto factory = rclcpp::create_subscription_factory
<MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT>(
std::forward<CallbackT>(callback), msg_mem_strat, allocator);
auto sub = node_topics->create_subscription(

View File

@@ -89,10 +89,25 @@ public:
Duration
operator-(const rclcpp::Duration & rhs) const;
RCLCPP_PUBLIC
static Duration
max();
RCLCPP_PUBLIC
Duration
operator*(double scale) const;
RCLCPP_PUBLIC
rcl_duration_value_t
nanoseconds() const;
/// \return the duration in seconds as a floating point number.
/// \warning Depending on sizeof(double) there could be significant precision loss.
/// When an exact time is required use nanoseconds() instead.
RCLCPP_PUBLIC
double
seconds() const;
private:
rcl_duration_t rcl_duration_;
};

View File

@@ -116,7 +116,7 @@ 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);
void (* reset_error)() = rcl_reset_error);
class RCLErrorBase
{
@@ -181,6 +181,21 @@ public:
: std::runtime_error("event already registered") {}
};
/// Thrown if passed parameters are inconsistent or invalid
class InvalidParametersException : public std::runtime_error
{
public:
// Inherit constructors from runtime_error;
using std::runtime_error::runtime_error;
};
/// Throwing if passed parameter value is invalid.
class InvalidParameterValueException : public std::runtime_error
{
// Inherit constructors from runtime_error;
using std::runtime_error::runtime_error;
};
} // namespace exceptions
} // namespace rclcpp

View File

@@ -190,10 +190,14 @@ public:
* single-threaded model of execution.
* Adding subscriptions, timers, services, etc. with blocking callbacks will cause this function
* to block (which may have unintended consequences).
*
* \param[in] max_duration The maximum amount of time to spend executing work, or 0 for no limit.
* Note that spin_some() may take longer than this time as it only returns once max_duration has
* been exceeded.
*/
RCLCPP_PUBLIC
virtual void
spin_some();
spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0));
RCLCPP_PUBLIC
virtual void
@@ -287,7 +291,7 @@ protected:
*/
RCLCPP_PUBLIC
void
execute_any_executable(AnyExecutable::SharedPtr any_exec);
execute_any_executable(AnyExecutable & any_exec);
RCLCPP_PUBLIC
static void
@@ -325,15 +329,17 @@ protected:
RCLCPP_PUBLIC
void
get_next_timer(AnyExecutable::SharedPtr any_exec);
get_next_timer(AnyExecutable & any_exec);
RCLCPP_PUBLIC
AnyExecutable::SharedPtr
get_next_ready_executable();
bool
get_next_ready_executable(AnyExecutable & any_executable);
RCLCPP_PUBLIC
AnyExecutable::SharedPtr
get_next_executable(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
bool
get_next_executable(
AnyExecutable & any_executable,
std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
/// Spinning state, used to prevent multi threaded calls to spin and to cancel blocking spins.
std::atomic_bool spinning;

View File

@@ -15,7 +15,9 @@
#ifndef RCLCPP__EXECUTORS__MULTI_THREADED_EXECUTOR_HPP_
#define RCLCPP__EXECUTORS__MULTI_THREADED_EXECUTOR_HPP_
#include <memory>
#include <mutex>
#include <set>
#include <thread>
#include <unordered_map>
@@ -34,9 +36,24 @@ class MultiThreadedExecutor : public executor::Executor
public:
RCLCPP_SMART_PTR_DEFINITIONS(MultiThreadedExecutor)
/// Constructor for MultiThreadedExecutor.
/**
* For the yield_before_execute option, when true std::this_thread::yield()
* will be called after acquiring work (as an AnyExecutable) and
* releasing the spinning lock, but before executing the work.
* This is useful for reproducing some bugs related to taking work more than
* once.
*
* \param args common arguments for all executors
* \param number_of_threads number of threads to have in the thread pool,
* the default 0 will use the number of cpu cores found instead
* \param yield_before_execute if true std::this_thread::yield() is called
*/
RCLCPP_PUBLIC
MultiThreadedExecutor(
const executor::ExecutorArgs & args = rclcpp::executor::create_default_executor_arguments());
const executor::ExecutorArgs & args = rclcpp::executor::create_default_executor_arguments(),
size_t number_of_threads = 0,
bool yield_before_execute = false);
RCLCPP_PUBLIC
virtual ~MultiThreadedExecutor();
@@ -59,6 +76,10 @@ private:
std::mutex wait_mutex_;
size_t number_of_threads_;
bool yield_before_execute_;
std::mutex scheduled_timers_mutex_;
std::set<TimerBase::SharedPtr> scheduled_timers_;
};
} // namespace executors

View File

@@ -49,7 +49,7 @@ template<typename FunctionT>
struct function_traits
{
using arguments = typename tuple_tail<
typename function_traits<decltype( & FunctionT::operator())>::arguments>::type;
typename function_traits<decltype( & FunctionT::operator())>::arguments>::type;
static constexpr std::size_t arity = std::tuple_size<arguments>::value;
@@ -83,7 +83,7 @@ template<typename ClassT, typename ReturnTypeT, typename ... Args, typename ...
#if defined _LIBCPP_VERSION // libc++ (Clang)
struct function_traits<std::__1::__bind<ReturnTypeT (ClassT::*)(Args ...), FArgs ...>>
#elif defined _GLIBCXX_RELEASE // glibc++ (GNU C++ >= 7.1)
struct function_traits<std::_Bind<ReturnTypeT(ClassT::*(ClassT *, FArgs ...))(Args ...)>>
struct function_traits<std::_Bind<ReturnTypeT(ClassT::*(FArgs ...))(Args ...)>>
#elif defined __GLIBCXX__ // glibc++ (GNU C++)
struct function_traits<std::_Bind<std::_Mem_fn<ReturnTypeT (ClassT::*)(Args ...)>(FArgs ...)>>
#elif defined _MSC_VER // MS Visual Studio
@@ -130,20 +130,20 @@ struct function_traits<FunctionT &&>: function_traits<FunctionT>
*/
template<std::size_t Arity, typename FunctorT>
struct arity_comparator : std::integral_constant<
bool, (Arity == function_traits<FunctorT>::arity)>{};
bool, (Arity == function_traits<FunctorT>::arity)> {};
template<typename FunctorT, typename ... Args>
struct check_arguments : std::is_same<
typename function_traits<FunctorT>::arguments,
std::tuple<Args ...>
>
>
{};
template<typename FunctorAT, typename FunctorBT>
struct same_arguments : std::is_same<
typename function_traits<FunctorAT>::arguments,
typename function_traits<FunctorBT>::arguments
>
>
{};
} // namespace function_traits

View File

@@ -326,7 +326,7 @@ public:
message_sequence_number,
requesting_subscriptions_intra_process_id,
target_subs_size
);
);
typename TypedMRB::SharedPtr typed_buffer = std::static_pointer_cast<TypedMRB>(buffer);
if (!typed_buffer) {
return;

View File

@@ -45,7 +45,7 @@ public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(IntraProcessManagerImplBase)
IntraProcessManagerImplBase() = default;
~IntraProcessManagerImplBase() = default;
virtual ~IntraProcessManagerImplBase() = default;
virtual void
add_subscription(uint64_t id, SubscriptionBase::SharedPtr subscription) = 0;
@@ -258,11 +258,11 @@ private:
using AllocSet = std::set<uint64_t, std::less<uint64_t>, RebindAlloc<uint64_t>>;
using SubscriptionMap = std::unordered_map<
uint64_t, SubscriptionBase::WeakPtr,
std::hash<uint64_t>, std::equal_to<uint64_t>,
RebindAlloc<std::pair<const uint64_t, SubscriptionBase::WeakPtr>>>;
uint64_t, SubscriptionBase::WeakPtr,
std::hash<uint64_t>, std::equal_to<uint64_t>,
RebindAlloc<std::pair<const uint64_t, SubscriptionBase::WeakPtr>>>;
struct strcmp_wrapper : public std::binary_function<const char *, const char *, bool>
struct strcmp_wrapper
{
bool
operator()(const char * lhs, const char * rhs) const
@@ -271,10 +271,10 @@ private:
}
};
using IDTopicMap = std::map<
const char *,
AllocSet,
strcmp_wrapper,
RebindAlloc<std::pair<const char * const, AllocSet>>>;
const char *,
AllocSet,
strcmp_wrapper,
RebindAlloc<std::pair<const char * const, AllocSet>>>;
SubscriptionMap subscriptions_;
@@ -291,16 +291,16 @@ private:
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer;
using TargetSubscriptionsMap = std::unordered_map<
uint64_t, AllocSet,
std::hash<uint64_t>, std::equal_to<uint64_t>,
RebindAlloc<std::pair<const uint64_t, AllocSet>>>;
uint64_t, AllocSet,
std::hash<uint64_t>, std::equal_to<uint64_t>,
RebindAlloc<std::pair<const uint64_t, AllocSet>>>;
TargetSubscriptionsMap target_subscriptions_by_message_sequence;
};
using PublisherMap = std::unordered_map<
uint64_t, PublisherInfo,
std::hash<uint64_t>, std::equal_to<uint64_t>,
RebindAlloc<std::pair<const uint64_t, PublisherInfo>>>;
uint64_t, PublisherInfo,
std::hash<uint64_t>, std::equal_to<uint64_t>,
RebindAlloc<std::pair<const uint64_t, PublisherInfo>>>;
PublisherMap publishers_;

View File

@@ -43,6 +43,8 @@ public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(MemoryStrategy)
using WeakNodeVector = std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>;
virtual ~MemoryStrategy() = default;
virtual bool collect_entities(const WeakNodeVector & weak_nodes) = 0;
virtual size_t number_of_ready_subscriptions() const = 0;
@@ -55,27 +57,23 @@ public:
virtual void clear_handles() = 0;
virtual void remove_null_handles(rcl_wait_set_t * wait_set) = 0;
/// Provide a newly initialized AnyExecutable object.
// \return Shared pointer to the fresh executable.
virtual rclcpp::executor::AnyExecutable::SharedPtr instantiate_next_executable() = 0;
virtual void add_guard_condition(const rcl_guard_condition_t * guard_condition) = 0;
virtual void remove_guard_condition(const rcl_guard_condition_t * guard_condition) = 0;
virtual void
get_next_subscription(
rclcpp::executor::AnyExecutable::SharedPtr any_exec,
rclcpp::executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes) = 0;
virtual void
get_next_service(
rclcpp::executor::AnyExecutable::SharedPtr any_exec,
rclcpp::executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes) = 0;
virtual void
get_next_client(
rclcpp::executor::AnyExecutable::SharedPtr any_exec,
rclcpp::executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes) = 0;
virtual rcl_allocator_t
@@ -83,14 +81,18 @@ public:
static rclcpp::SubscriptionBase::SharedPtr
get_subscription_by_handle(
const rcl_subscription_t * subscriber_handle,
std::shared_ptr<const rcl_subscription_t> subscriber_handle,
const WeakNodeVector & weak_nodes);
static rclcpp::ServiceBase::SharedPtr
get_service_by_handle(const rcl_service_t * service_handle, const WeakNodeVector & weak_nodes);
get_service_by_handle(
std::shared_ptr<const rcl_service_t> service_handle,
const WeakNodeVector & weak_nodes);
static rclcpp::ClientBase::SharedPtr
get_client_by_handle(const rcl_client_t * client_handle, const WeakNodeVector & weak_nodes);
get_client_by_handle(
std::shared_ptr<const rcl_client_t> client_handle,
const WeakNodeVector & weak_nodes);
static rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_by_group(

View File

@@ -18,10 +18,17 @@
#include <memory>
#include <stdexcept>
#include "rcl/types.h"
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rcutils/logging_macros.h"
#include "rmw/serialized_message.h"
namespace rclcpp
{
namespace message_memory_strategy
@@ -39,16 +46,33 @@ public:
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
using SerializedMessageAllocTraits = allocator::AllocRebind<rcl_serialized_message_t, Alloc>;
using SerializedMessageAlloc = typename SerializedMessageAllocTraits::allocator_type;
using SerializedMessageDeleter =
allocator::Deleter<SerializedMessageAlloc, rcl_serialized_message_t>;
using BufferAllocTraits = allocator::AllocRebind<char, Alloc>;
using BufferAlloc = typename BufferAllocTraits::allocator_type;
using BufferDeleter = allocator::Deleter<BufferAlloc, char>;
MessageMemoryStrategy()
{
message_allocator_ = std::make_shared<MessageAlloc>();
serialized_message_allocator_ = std::make_shared<SerializedMessageAlloc>();
buffer_allocator_ = std::make_shared<BufferAlloc>();
rcutils_allocator_ = allocator::get_rcl_allocator<char, BufferAlloc>(*buffer_allocator_.get());
}
explicit MessageMemoryStrategy(std::shared_ptr<Alloc> allocator)
{
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
serialized_message_allocator_ = std::make_shared<SerializedMessageAlloc>(*allocator.get());
buffer_allocator_ = std::make_shared<BufferAlloc>(*allocator.get());
rcutils_allocator_ = allocator::get_rcl_allocator<char, BufferAlloc>(*buffer_allocator_.get());
}
virtual ~MessageMemoryStrategy() = default;
/// Default factory method
static SharedPtr create_default()
{
@@ -62,6 +86,39 @@ public:
return std::allocate_shared<MessageT, MessageAlloc>(*message_allocator_.get());
}
virtual std::shared_ptr<rcl_serialized_message_t> borrow_serialized_message(size_t capacity)
{
auto msg = new rcl_serialized_message_t;
*msg = rmw_get_zero_initialized_serialized_message();
auto ret = rmw_serialized_message_init(msg, capacity, &rcutils_allocator_);
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
auto serialized_msg = std::shared_ptr<rcl_serialized_message_t>(msg,
[](rmw_serialized_message_t * msg) {
auto ret = rmw_serialized_message_fini(msg);
delete msg;
if (ret != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"failed to destroy serialized message: %s", rcl_get_error_string().str);
}
});
return serialized_msg;
}
virtual std::shared_ptr<rcl_serialized_message_t> borrow_serialized_message()
{
return borrow_serialized_message(default_buffer_capacity_);
}
virtual void set_default_buffer_capacity(size_t capacity)
{
default_buffer_capacity_ = capacity;
}
/// Release ownership of the message, which will deallocate it if it has no more owners.
/** \param[in] msg Shared pointer to the message we are returning. */
virtual void return_message(std::shared_ptr<MessageT> & msg)
@@ -69,8 +126,22 @@ public:
msg.reset();
}
virtual void return_serialized_message(std::shared_ptr<rcl_serialized_message_t> & serialized_msg)
{
serialized_msg.reset();
}
std::shared_ptr<MessageAlloc> message_allocator_;
MessageDeleter message_deleter_;
std::shared_ptr<SerializedMessageAlloc> serialized_message_allocator_;
SerializedMessageDeleter serialized_message_deleter_;
std::shared_ptr<BufferAlloc> buffer_allocator_;
BufferDeleter buffer_deleter_;
size_t default_buffer_capacity_ = 0;
rcutils_allocator_t rcutils_allocator_;
};
} // namespace message_memory_strategy

View File

@@ -53,6 +53,7 @@
#include "rclcpp/publisher.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/subscription_traits.hpp"
#include "rclcpp/time.hpp"
#include "rclcpp/timer.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -84,15 +85,25 @@ public:
* \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] arguments Command line arguments that should apply only to this node.
* \param[in] initial_parameters a list of initial values for parameters on the node.
* This can be used to provide remapping rules that only affect one instance.
* \param[in] use_global_arguments False to prevent node using arguments passed to the 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.
* \param[in] start_parameter_services True to setup ROS interfaces for accessing parameters
* in the node.
*/
RCLCPP_PUBLIC
Node(
const std::string & node_name,
const std::string & namespace_,
rclcpp::Context::SharedPtr context,
bool use_intra_process_comms = false);
const std::vector<std::string> & arguments,
const std::vector<Parameter> & initial_parameters,
bool use_global_arguments = true,
bool use_intra_process_comms = false,
bool start_parameter_services = true);
RCLCPP_PUBLIC
virtual ~Node();
@@ -175,7 +186,8 @@ public:
typename MessageT,
typename CallbackT,
typename Alloc = std::allocator<void>,
typename SubscriptionT = rclcpp::Subscription<MessageT, Alloc>>
typename SubscriptionT = rclcpp::Subscription<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>>
std::shared_ptr<SubscriptionT>
create_subscription(
const std::string & topic_name,
@@ -183,7 +195,8 @@ public:
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_default,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr,
bool ignore_local_publications = false,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>::SharedPtr
msg_mem_strat = nullptr,
std::shared_ptr<Alloc> allocator = nullptr);
@@ -206,15 +219,17 @@ public:
typename MessageT,
typename CallbackT,
typename Alloc = std::allocator<void>,
typename SubscriptionT = rclcpp::Subscription<MessageT, Alloc>>
typename SubscriptionT = rclcpp::Subscription<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>>
std::shared_ptr<SubscriptionT>
create_subscription(
const std::string & topic_name,
size_t qos_history_depth,
CallbackT && callback,
size_t qos_history_depth,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr,
bool ignore_local_publications = false,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>::SharedPtr
msg_mem_strat = nullptr,
std::shared_ptr<Alloc> allocator = nullptr);
@@ -250,11 +265,11 @@ public:
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
set_parameters(const std::vector<rclcpp::Parameter> & parameters);
RCLCPP_PUBLIC
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
set_parameters_atomically(const std::vector<rclcpp::Parameter> & parameters);
template<typename ParameterT>
void
@@ -263,18 +278,18 @@ public:
const ParameterT & value);
RCLCPP_PUBLIC
std::vector<rclcpp::parameter::ParameterVariant>
std::vector<rclcpp::Parameter>
get_parameters(const std::vector<std::string> & names) const;
RCLCPP_PUBLIC
rclcpp::parameter::ParameterVariant
rclcpp::Parameter
get_parameter(const std::string & name) const;
RCLCPP_PUBLIC
bool
get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const;
rclcpp::Parameter & parameter) const;
/// Assign the value of the parameter if set into the parameter argument.
/**
@@ -306,6 +321,24 @@ public:
ParameterT & value,
const ParameterT & alternative_value) const;
/// Get the parameter value; if not set, set the "alternative value" and store it in the node.
/**
* If the parameter is set, then the "value" argument is assigned the value
* in the parameter.
* If the parameter is not set, then the "value" argument is assigned the "alternative_value",
* and the parameter is set to the "alternative_value" on the node.
*
* \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 and parameter if the parameter was not set.
*/
template<typename ParameterT>
void
get_parameter_or_set(
const std::string & name,
ParameterT & value,
const ParameterT & alternative_value);
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::ParameterDescriptor>
describe_parameters(const std::vector<std::string> & names) const;
@@ -328,6 +361,10 @@ public:
void
register_param_change_callback(CallbackT && callback);
RCLCPP_PUBLIC
std::vector<std::string>
get_node_names() const;
RCLCPP_PUBLIC
std::map<std::string, std::vector<std::string>>
get_topic_names_and_types() const;
@@ -346,7 +383,7 @@ public:
/// Return a graph event, which will be set anytime a graph change occurs.
/* The graph Event object is a loan which must be returned.
* The Event object is scoped and therefore to return the load just let it go
* The Event object is scoped and therefore to return the loan just let it go
* out of scope.
*/
RCLCPP_PUBLIC

View File

@@ -39,6 +39,7 @@
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/create_publisher.hpp"
#include "rclcpp/create_service.hpp"
#include "rclcpp/create_subscription.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -81,7 +82,11 @@ Node::create_publisher(
allocator);
}
template<typename MessageT, typename CallbackT, typename Alloc, typename SubscriptionT>
template<
typename MessageT,
typename CallbackT,
typename Alloc,
typename SubscriptionT>
std::shared_ptr<SubscriptionT>
Node::create_subscription(
const std::string & topic_name,
@@ -89,20 +94,23 @@ Node::create_subscription(
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group,
bool ignore_local_publications,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>::SharedPtr
msg_mem_strat,
std::shared_ptr<Alloc> allocator)
{
using CallbackMessageT = typename rclcpp::subscription_traits::has_message_type<CallbackT>::type;
if (!allocator) {
allocator = std::make_shared<Alloc>();
}
if (!msg_mem_strat) {
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
msg_mem_strat = MessageMemoryStrategy<MessageT, Alloc>::create_default();
msg_mem_strat = MessageMemoryStrategy<CallbackMessageT, Alloc>::create_default();
}
return rclcpp::create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
return rclcpp::create_subscription<MessageT, CallbackT, Alloc, CallbackMessageT, SubscriptionT>(
this->node_topics_.get(),
topic_name,
std::forward<CallbackT>(callback),
@@ -114,21 +122,26 @@ Node::create_subscription(
allocator);
}
template<typename MessageT, typename CallbackT, typename Alloc, typename SubscriptionT>
template<
typename MessageT,
typename CallbackT,
typename Alloc,
typename SubscriptionT>
std::shared_ptr<SubscriptionT>
Node::create_subscription(
const std::string & topic_name,
size_t qos_history_depth,
CallbackT && callback,
size_t qos_history_depth,
rclcpp::callback_group::CallbackGroup::SharedPtr group,
bool ignore_local_publications,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>::SharedPtr
msg_mem_strat,
std::shared_ptr<Alloc> allocator)
{
rmw_qos_profile_t qos = rmw_qos_profile_default;
qos.depth = qos_history_depth;
return this->create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
return this->create_subscription<MessageT>(
topic_name,
std::forward<CallbackT>(callback),
qos,
@@ -184,18 +197,9 @@ Node::create_service(
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
rclcpp::AnyServiceCallback<ServiceT> any_service_callback;
any_service_callback.set(std::forward<CallbackT>(callback));
rcl_service_options_t service_options = rcl_service_get_default_options();
service_options.qos = qos_profile;
auto serv = Service<ServiceT>::make_shared(
node_base_->get_shared_rcl_node_handle(),
service_name, any_service_callback, service_options);
auto serv_base_ptr = std::dynamic_pointer_cast<ServiceBase>(serv);
node_services_->add_service(serv_base_ptr, group);
return serv;
return rclcpp::create_service<ServiceT, CallbackT>(
node_base_, node_services_,
service_name, std::forward<CallbackT>(callback), qos_profile, group);
}
template<typename CallbackT>
@@ -211,10 +215,10 @@ Node::set_parameter_if_not_set(
const std::string & name,
const ParameterT & value)
{
rclcpp::parameter::ParameterVariant parameter_variant;
if (!this->get_parameter(name, parameter_variant)) {
rclcpp::Parameter parameter;
if (!this->get_parameter(name, parameter)) {
this->set_parameters({
rclcpp::parameter::ParameterVariant(name, value),
rclcpp::Parameter(name, value),
});
}
}
@@ -223,10 +227,10 @@ 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);
rclcpp::Parameter parameter;
bool result = get_parameter(name, parameter);
if (result) {
value = parameter_variant.get_value<ParameterT>();
value = parameter.get_value<ParameterT>();
}
return result;
@@ -246,6 +250,22 @@ Node::get_parameter_or(
return got_parameter;
}
template<typename ParameterT>
void
Node::get_parameter_or_set(
const std::string & name,
ParameterT & value,
const ParameterT & alternative_value)
{
bool got_parameter = get_parameter(name, value);
if (!got_parameter) {
this->set_parameters({
rclcpp::Parameter(name, alternative_value),
});
value = alternative_value;
}
}
} // namespace rclcpp
#endif // RCLCPP__NODE_IMPL_HPP_

View File

@@ -39,7 +39,9 @@ public:
NodeBase(
const std::string & node_name,
const std::string & namespace_,
rclcpp::Context::SharedPtr context);
rclcpp::Context::SharedPtr context,
const std::vector<std::string> & arguments,
bool use_global_arguments);
RCLCPP_PUBLIC
virtual

View File

@@ -20,6 +20,7 @@
#include <memory>
#include <mutex>
#include <string>
#include <utility>
#include <vector>
#include "rcl/guard_condition.h"
@@ -69,6 +70,11 @@ public:
std::vector<std::string>
get_node_names() const;
RCLCPP_PUBLIC
virtual
std::vector<std::pair<std::string, std::string>>
get_node_names_and_namespaces() const;
RCLCPP_PUBLIC
virtual
size_t

View File

@@ -18,6 +18,7 @@
#include <chrono>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "rcl/guard_condition.h"
@@ -66,6 +67,12 @@ public:
std::vector<std::string>
get_node_names() const = 0;
/// Return a vector of existing node names and namespaces (pair of string).
RCLCPP_PUBLIC
virtual
std::vector<std::pair<std::string, std::string>>
get_node_names_and_namespaces() const = 0;
/// Return the number of publishers that are advertised on a given topic.
RCLCPP_PUBLIC
virtual

View File

@@ -46,6 +46,11 @@ public:
rclcpp::Logger
get_logger() const;
RCLCPP_PUBLIC
virtual
const char *
get_logger_name() const;
private:
RCLCPP_DISABLE_COPY(NodeLogging)

View File

@@ -39,6 +39,13 @@ public:
virtual
rclcpp::Logger
get_logger() const = 0;
/// Return the logger name associated with the node.
/** \return The logger name associated with the node. */
RCLCPP_PUBLIC
virtual
const char *
get_logger_name() const = 0;
};
} // namespace node_interfaces

View File

@@ -16,6 +16,7 @@
#define RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_HPP_
#include <map>
#include <memory>
#include <string>
#include <vector>
@@ -30,6 +31,7 @@
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/parameter_service.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -46,8 +48,12 @@ public:
RCLCPP_PUBLIC
NodeParameters(
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
bool use_intra_process);
const node_interfaces::NodeBaseInterface::SharedPtr node_base,
const node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
const node_interfaces::NodeServicesInterface::SharedPtr node_services,
const std::vector<Parameter> & initial_parameters,
bool use_intra_process,
bool start_parameter_services);
RCLCPP_PUBLIC
virtual
@@ -57,22 +63,22 @@ public:
virtual
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
const std::vector<rclcpp::Parameter> & parameters);
RCLCPP_PUBLIC
virtual
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
const std::vector<rclcpp::Parameter> & parameters);
RCLCPP_PUBLIC
virtual
std::vector<rclcpp::parameter::ParameterVariant>
std::vector<rclcpp::Parameter>
get_parameters(const std::vector<std::string> & names) const;
RCLCPP_PUBLIC
virtual
rclcpp::parameter::ParameterVariant
rclcpp::Parameter
get_parameter(const std::string & name) const;
RCLCPP_PUBLIC
@@ -80,7 +86,7 @@ public:
bool
get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const;
rclcpp::Parameter & parameter) const;
RCLCPP_PUBLIC
virtual
@@ -105,15 +111,15 @@ public:
private:
RCLCPP_DISABLE_COPY(NodeParameters)
rclcpp::node_interfaces::NodeTopicsInterface * node_topics_;
mutable std::mutex mutex_;
ParametersCallbackFunction parameters_callback_ = nullptr;
std::map<std::string, rclcpp::parameter::ParameterVariant> parameters_;
std::map<std::string, rclcpp::Parameter> parameters_;
Publisher<rcl_interfaces::msg::ParameterEvent>::SharedPtr events_publisher_;
std::shared_ptr<ParameterService> parameter_service_;
};
} // namespace node_interfaces

View File

@@ -41,30 +41,49 @@ public:
virtual
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters) = 0;
const std::vector<rclcpp::Parameter> & parameters) = 0;
RCLCPP_PUBLIC
virtual
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters) = 0;
const std::vector<rclcpp::Parameter> & parameters) = 0;
/// Get descriptions of parameters given their names.
/*
* \param[in] names a list of parameter names to check.
* \return the list of parameters that were found.
* Any parameter not found is omitted from the returned list.
*/
RCLCPP_PUBLIC
virtual
std::vector<rclcpp::parameter::ParameterVariant>
std::vector<rclcpp::Parameter>
get_parameters(const std::vector<std::string> & names) const = 0;
/// Get the description of one parameter given a name.
/*
* \param[in] name the name of the parameter to look for.
* \return the parameter if it exists on the node.
* \throws std::out_of_range if the parameter does not exist on the node.
*/
RCLCPP_PUBLIC
virtual
rclcpp::parameter::ParameterVariant
rclcpp::Parameter
get_parameter(const std::string & name) const = 0;
/// Get the description of one parameter given a name.
/*
* \param[in] name the name of the parameter to look for.
* \param[out] parameter the description if parameter exists on the node.
* \return true if the parameter exists on the node, or
* \return false if the parameter does not exist.
*/
RCLCPP_PUBLIC
virtual
bool
get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const = 0;
rclcpp::Parameter & parameter) const = 0;
RCLCPP_PUBLIC
virtual
@@ -82,8 +101,8 @@ public:
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const = 0;
using ParametersCallbackFunction = std::function<
rcl_interfaces::msg::SetParametersResult(
const std::vector<rclcpp::parameter::ParameterVariant> &)>;
rcl_interfaces::msg::SetParametersResult(
const std::vector<rclcpp::Parameter> &)>;
RCLCPP_PUBLIC
virtual

View File

@@ -15,54 +15,34 @@
#ifndef RCLCPP__PARAMETER_HPP_
#define RCLCPP__PARAMETER_HPP_
#include <iostream>
#include <ostream>
#include <sstream>
#include <string>
#include <vector>
#include "rcl_interfaces/msg/parameter.hpp"
#include "rcl_interfaces/msg/parameter_type.hpp"
#include "rcl_interfaces/msg/parameter_value.hpp"
#include "rclcpp/parameter_value.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/rmw.h"
namespace rclcpp
{
namespace parameter
{
enum ParameterType
{
PARAMETER_NOT_SET = rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET,
PARAMETER_BOOL = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL,
PARAMETER_INTEGER = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER,
PARAMETER_DOUBLE = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE,
PARAMETER_STRING = rcl_interfaces::msg::ParameterType::PARAMETER_STRING,
PARAMETER_BYTES = rcl_interfaces::msg::ParameterType::PARAMETER_BYTES,
};
// Structure to store an arbitrary parameter with templated get/set methods
class ParameterVariant
/// Structure to store an arbitrary parameter with templated get/set methods.
class Parameter
{
public:
RCLCPP_PUBLIC
ParameterVariant();
Parameter();
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const bool bool_value);
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const int int_value);
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const int64_t int_value);
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const float double_value);
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const double double_value);
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const std::string & string_value);
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const char * string_value);
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const std::vector<uint8_t> & bytes_value);
Parameter(const std::string & name, const ParameterValue & value);
template<typename ValueTypeT>
explicit Parameter(const std::string & name, ValueTypeT value)
: Parameter(name, ParameterValue(value))
{
}
RCLCPP_PUBLIC
ParameterType
@@ -78,105 +58,27 @@ public:
RCLCPP_PUBLIC
rcl_interfaces::msg::ParameterValue
get_parameter_value() const;
get_value_message() const;
// The following get_value() variants require the use of ParameterType
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_INTEGER, int64_t>::type
/// Get value of parameter using rclcpp::ParameterType as template argument.
template<ParameterType ParamT>
decltype(auto)
get_value() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER) {
// TODO(wjwwood): use custom exception
throw std::runtime_error("Invalid type");
}
return value_.integer_value;
return value_.get<ParamT>();
}
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_DOUBLE, double>::type
/// Get value of parameter using c++ types as template argument.
template<typename T>
decltype(auto)
get_value() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE) {
// TODO(wjwwood): use custom exception
throw std::runtime_error("Invalid type");
}
return value_.double_value;
return value_.get<T>();
}
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_STRING, const std::string &>::type
get_value() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_STRING) {
// TODO(wjwwood): use custom exception
throw std::runtime_error("Invalid type");
}
return value_.string_value;
}
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_BOOL, bool>::type
get_value() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_BOOL) {
// TODO(wjwwood): use custom exception
throw std::runtime_error("Invalid type");
}
return value_.bool_value;
}
template<ParameterType type>
typename std::enable_if<
type == ParameterType::PARAMETER_BYTES, const std::vector<uint8_t> &>::type
get_value() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_BYTES) {
// TODO(wjwwood): use custom exception
throw std::runtime_error("Invalid type");
}
return value_.bytes_value;
}
// The following get_value() variants allow the use of primitive types
template<typename type>
typename std::enable_if<
std::is_integral<type>::value && !std::is_same<type, bool>::value, int64_t>::type
get_value() const
{
return get_value<ParameterType::PARAMETER_INTEGER>();
}
template<typename type>
typename std::enable_if<std::is_floating_point<type>::value, double>::type
get_value() const
{
return get_value<ParameterType::PARAMETER_DOUBLE>();
}
template<typename type>
typename std::enable_if<std::is_convertible<type, std::string>::value, const std::string &>::type
get_value() const
{
return get_value<ParameterType::PARAMETER_STRING>();
}
template<typename type>
typename std::enable_if<std::is_same<type, bool>::value, bool>::type
get_value() const
{
return get_value<ParameterType::PARAMETER_BOOL>();
}
template<typename type>
typename std::enable_if<
std::is_convertible<
type, const std::vector<uint8_t> &>::value, const std::vector<uint8_t> &>::type
get_value() const
{
return get_value<ParameterType::PARAMETER_BYTES>();
}
RCLCPP_PUBLIC
bool
as_bool() const;
RCLCPP_PUBLIC
int64_t
@@ -190,21 +92,33 @@ public:
const std::string &
as_string() const;
RCLCPP_PUBLIC
bool
as_bool() const;
RCLCPP_PUBLIC
const std::vector<uint8_t> &
as_bytes() const;
as_byte_array() const;
RCLCPP_PUBLIC
static ParameterVariant
from_parameter(const rcl_interfaces::msg::Parameter & parameter);
const std::vector<bool> &
as_bool_array() const;
RCLCPP_PUBLIC
const std::vector<int64_t> &
as_integer_array() const;
RCLCPP_PUBLIC
const std::vector<double> &
as_double_array() const;
RCLCPP_PUBLIC
const std::vector<std::string> &
as_string_array() const;
RCLCPP_PUBLIC
static Parameter
from_parameter_msg(const rcl_interfaces::msg::Parameter & parameter);
RCLCPP_PUBLIC
rcl_interfaces::msg::Parameter
to_parameter();
to_parameter_msg() const;
RCLCPP_PUBLIC
std::string
@@ -212,24 +126,22 @@ public:
private:
std::string name_;
rcl_interfaces::msg::ParameterValue value_;
ParameterValue value_;
};
/// Return a json encoded version of the parameter intended for a dict.
RCLCPP_PUBLIC
std::string
_to_json_dict_entry(const ParameterVariant & param);
_to_json_dict_entry(const Parameter & param);
RCLCPP_PUBLIC
std::ostream &
operator<<(std::ostream & os, const rclcpp::parameter::ParameterVariant & pv);
operator<<(std::ostream & os, const rclcpp::Parameter & pv);
RCLCPP_PUBLIC
std::ostream &
operator<<(std::ostream & os, const std::vector<ParameterVariant> & parameters);
operator<<(std::ostream & os, const std::vector<Parameter> & parameters);
} // namespace parameter
} // namespace rclcpp
namespace std
@@ -238,12 +150,12 @@ namespace std
/// Return a json encoded version of the parameter intended for a list.
RCLCPP_PUBLIC
std::string
to_string(const rclcpp::parameter::ParameterVariant & param);
to_string(const rclcpp::Parameter & param);
/// 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);
to_string(const std::vector<rclcpp::Parameter> & parameters);
} // namespace std

View File

@@ -68,25 +68,25 @@ public:
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
RCLCPP_PUBLIC
std::shared_future<std::vector<rclcpp::parameter::ParameterVariant>>
std::shared_future<std::vector<rclcpp::Parameter>>
get_parameters(
const std::vector<std::string> & names,
std::function<
void(std::shared_future<std::vector<rclcpp::parameter::ParameterVariant>>)
void(std::shared_future<std::vector<rclcpp::Parameter>>)
> callback = nullptr);
RCLCPP_PUBLIC
std::shared_future<std::vector<rclcpp::parameter::ParameterType>>
std::shared_future<std::vector<rclcpp::ParameterType>>
get_parameter_types(
const std::vector<std::string> & names,
std::function<
void(std::shared_future<std::vector<rclcpp::parameter::ParameterType>>)
void(std::shared_future<std::vector<rclcpp::ParameterType>>)
> callback = nullptr);
RCLCPP_PUBLIC
std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters,
const std::vector<rclcpp::Parameter> & parameters,
std::function<
void(std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>)
> callback = nullptr);
@@ -94,7 +94,7 @@ public:
RCLCPP_PUBLIC
std::shared_future<rcl_interfaces::msg::SetParametersResult>
set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters,
const std::vector<rclcpp::Parameter> & parameters,
std::function<
void(std::shared_future<rcl_interfaces::msg::SetParametersResult>)
> callback = nullptr);
@@ -120,8 +120,9 @@ public:
auto msg_mem_strat =
MessageMemoryStrategy<rcl_interfaces::msg::ParameterEvent, Alloc>::create_default();
using rcl_interfaces::msg::ParameterEvent;
return rclcpp::create_subscription<
rcl_interfaces::msg::ParameterEvent, CallbackT, Alloc, SubscriptionT>(
ParameterEvent, CallbackT, Alloc, ParameterEvent, SubscriptionT>(
this->node_topics_interface_.get(),
"parameter_events",
std::forward<CallbackT>(callback),
@@ -185,7 +186,7 @@ public:
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
RCLCPP_PUBLIC
std::vector<rclcpp::parameter::ParameterVariant>
std::vector<rclcpp::Parameter>
get_parameters(const std::vector<std::string> & parameter_names);
RCLCPP_PUBLIC
@@ -200,7 +201,7 @@ public:
std::vector<std::string> names;
names.push_back(parameter_name);
auto vars = get_parameters(names);
if ((vars.size() != 1) || (vars[0].get_type() == rclcpp::parameter::PARAMETER_NOT_SET)) {
if ((vars.size() != 1) || (vars[0].get_type() == rclcpp::ParameterType::PARAMETER_NOT_SET)) {
return parameter_not_found_handler();
} else {
return static_cast<T>(vars[0].get_value<T>());
@@ -226,16 +227,16 @@ public:
}
RCLCPP_PUBLIC
std::vector<rclcpp::parameter::ParameterType>
std::vector<rclcpp::ParameterType>
get_parameter_types(const std::vector<std::string> & parameter_names);
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
set_parameters(const std::vector<rclcpp::Parameter> & parameters);
RCLCPP_PUBLIC
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
set_parameters_atomically(const std::vector<rclcpp::Parameter> & parameters);
RCLCPP_PUBLIC
rcl_interfaces::msg::ListParametersResult

View File

@@ -0,0 +1,53 @@
// Copyright 2018 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__PARAMETER_MAP_HPP_
#define RCLCPP__PARAMETER_MAP_HPP_
#include <rcl_yaml_param_parser/types.h>
#include <string>
#include <unordered_map>
#include <vector>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/parameter_value.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// A map of fully qualified node names to a list of parameters
using ParameterMap = std::unordered_map<std::string, std::vector<Parameter>>;
/// Convert parameters from rcl_yaml_param_parser into C++ class instances.
/// \param[in] c_params C structures containing parameters for multiple nodes.
/// \returns a map where the keys are fully qualified node names and values a list of parameters.
/// \throws InvalidParametersException if the `rcl_params_t` is inconsistent or invalid.
RCLCPP_PUBLIC
ParameterMap
parameter_map_from(const rcl_params_t * const c_params);
/// Convert parameter value from rcl_yaml_param_parser into a C++ class instance.
/// \param[in] c_value C structure containing a value of a parameter.
/// \returns an instance of a parameter value
/// \throws InvalidParameterValueException if the `rcl_variant_t` is inconsistent or invalid.
RCLCPP_PUBLIC
ParameterValue
parameter_value_from(const rcl_variant_t * const c_value);
} // namespace rclcpp
#endif // RCLCPP__PARAMETER_MAP_HPP_

View File

@@ -15,6 +15,7 @@
#ifndef RCLCPP__PARAMETER_SERVICE_HPP_
#define RCLCPP__PARAMETER_SERVICE_HPP_
#include <memory>
#include <string>
#include "rcl_interfaces/srv/describe_parameters.hpp"
@@ -40,11 +41,12 @@ public:
RCLCPP_PUBLIC
explicit ParameterService(
const rclcpp::Node::SharedPtr node,
const std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
const std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
node_interfaces::NodeParametersInterface * node_params,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
private:
const rclcpp::Node::SharedPtr node_;
rclcpp::Service<rcl_interfaces::srv::GetParameters>::SharedPtr get_parameters_service_;
rclcpp::Service<rcl_interfaces::srv::GetParameterTypes>::SharedPtr
get_parameter_types_service_;

View File

@@ -0,0 +1,316 @@
// Copyright 2018 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__PARAMETER_VALUE_HPP_
#define RCLCPP__PARAMETER_VALUE_HPP_
#include <exception>
#include <iostream>
#include <ostream>
#include <sstream>
#include <string>
#include <vector>
#include "rcl_interfaces/msg/parameter_type.hpp"
#include "rcl_interfaces/msg/parameter_value.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
enum ParameterType
{
PARAMETER_NOT_SET = rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET,
PARAMETER_BOOL = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL,
PARAMETER_INTEGER = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER,
PARAMETER_DOUBLE = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE,
PARAMETER_STRING = rcl_interfaces::msg::ParameterType::PARAMETER_STRING,
PARAMETER_BYTE_ARRAY = rcl_interfaces::msg::ParameterType::PARAMETER_BYTE_ARRAY,
PARAMETER_BOOL_ARRAY = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL_ARRAY,
PARAMETER_INTEGER_ARRAY = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY,
PARAMETER_DOUBLE_ARRAY = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY,
PARAMETER_STRING_ARRAY = rcl_interfaces::msg::ParameterType::PARAMETER_STRING_ARRAY,
};
/// Return the name of a parameter type
RCLCPP_PUBLIC
std::string
to_string(const ParameterType type);
RCLCPP_PUBLIC
std::ostream &
operator<<(std::ostream & os, const ParameterType type);
/// Indicate the parameter type does not match the expected type.
class ParameterTypeException : public std::runtime_error
{
public:
/// Construct an instance.
/**
* \param[in] expected the expected parameter type.
* \param[in] actual the actual parameter type.
*/
RCLCPP_PUBLIC
ParameterTypeException(ParameterType expected, ParameterType actual)
: std::runtime_error("expected [" + to_string(expected) + "] got [" + to_string(actual) + "]")
{}
};
/// Store the type and value of a parameter.
class ParameterValue
{
public:
/// Construct a parameter value with type PARAMETER_NOT_SET.
RCLCPP_PUBLIC
ParameterValue();
/// Construct a parameter value from a message.
RCLCPP_PUBLIC
explicit ParameterValue(const rcl_interfaces::msg::ParameterValue & value);
/// Construct a parameter value with type PARAMETER_BOOL.
RCLCPP_PUBLIC
explicit ParameterValue(const bool bool_value);
/// Construct a parameter value with type PARAMETER_INTEGER.
RCLCPP_PUBLIC
explicit ParameterValue(const int int_value);
/// Construct a parameter value with type PARAMETER_INTEGER.
RCLCPP_PUBLIC
explicit ParameterValue(const int64_t int_value);
/// Construct a parameter value with type PARAMETER_DOUBLE.
RCLCPP_PUBLIC
explicit ParameterValue(const float double_value);
/// Construct a parameter value with type PARAMETER_DOUBLE.
RCLCPP_PUBLIC
explicit ParameterValue(const double double_value);
/// Construct a parameter value with type PARAMETER_STRING.
RCLCPP_PUBLIC
explicit ParameterValue(const std::string & string_value);
/// Construct a parameter value with type PARAMETER_STRING.
RCLCPP_PUBLIC
explicit ParameterValue(const char * string_value);
/// Construct a parameter value with type PARAMETER_BYTE_ARRAY.
RCLCPP_PUBLIC
explicit ParameterValue(const std::vector<uint8_t> & byte_array_value);
/// Construct a parameter value with type PARAMETER_BOOL_ARRAY.
RCLCPP_PUBLIC
explicit ParameterValue(const std::vector<bool> & bool_array_value);
/// Construct a parameter value with type PARAMETER_INTEGER_ARRAY.
RCLCPP_PUBLIC
explicit ParameterValue(const std::vector<int> & int_array_value);
/// Construct a parameter value with type PARAMETER_INTEGER_ARRAY.
RCLCPP_PUBLIC
explicit ParameterValue(const std::vector<int64_t> & int_array_value);
/// Construct a parameter value with type PARAMETER_DOUBLE_ARRAY.
RCLCPP_PUBLIC
explicit ParameterValue(const std::vector<float> & double_array_value);
/// Construct a parameter value with type PARAMETER_DOUBLE_ARRAY.
RCLCPP_PUBLIC
explicit ParameterValue(const std::vector<double> & double_array_value);
/// Construct a parameter value with type PARAMETER_STRING_ARRAY.
RCLCPP_PUBLIC
explicit ParameterValue(const std::vector<std::string> & string_array_value);
/// Return an enum indicating the type of the set value.
RCLCPP_PUBLIC
ParameterType
get_type() const;
/// Return a message populated with the parameter value
RCLCPP_PUBLIC
rcl_interfaces::msg::ParameterValue
to_value_msg() const;
// The following get() variants require the use of ParameterType
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_BOOL, bool>::type
get() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_BOOL) {
throw ParameterTypeException(ParameterType::PARAMETER_BOOL, get_type());
}
return value_.bool_value;
}
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_INTEGER, int64_t>::type
get() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER) {
throw ParameterTypeException(ParameterType::PARAMETER_INTEGER, get_type());
}
return value_.integer_value;
}
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_DOUBLE, double>::type
get() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE) {
throw ParameterTypeException(ParameterType::PARAMETER_DOUBLE, get_type());
}
return value_.double_value;
}
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_STRING, const std::string &>::type
get() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_STRING) {
throw ParameterTypeException(ParameterType::PARAMETER_STRING, get_type());
}
return value_.string_value;
}
template<ParameterType type>
typename std::enable_if<
type == ParameterType::PARAMETER_BYTE_ARRAY, const std::vector<uint8_t> &>::type
get() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_BYTE_ARRAY) {
throw ParameterTypeException(ParameterType::PARAMETER_BYTE_ARRAY, get_type());
}
return value_.byte_array_value;
}
template<ParameterType type>
typename std::enable_if<
type == ParameterType::PARAMETER_BOOL_ARRAY, const std::vector<bool> &>::type
get() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_BOOL_ARRAY) {
throw ParameterTypeException(ParameterType::PARAMETER_BOOL_ARRAY, get_type());
}
return value_.bool_array_value;
}
template<ParameterType type>
typename std::enable_if<
type == ParameterType::PARAMETER_INTEGER_ARRAY, const std::vector<int64_t> &>::type
get() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY) {
throw ParameterTypeException(ParameterType::PARAMETER_INTEGER_ARRAY, get_type());
}
return value_.integer_array_value;
}
template<ParameterType type>
typename std::enable_if<
type == ParameterType::PARAMETER_DOUBLE_ARRAY, const std::vector<double> &>::type
get() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY) {
throw ParameterTypeException(ParameterType::PARAMETER_DOUBLE_ARRAY, get_type());
}
return value_.double_array_value;
}
template<ParameterType type>
typename std::enable_if<
type == ParameterType::PARAMETER_STRING_ARRAY, const std::vector<std::string> &>::type
get() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_STRING_ARRAY) {
throw ParameterTypeException(ParameterType::PARAMETER_STRING_ARRAY, get_type());
}
return value_.string_array_value;
}
// The following get() variants allow the use of primitive types
template<typename type>
typename std::enable_if<std::is_same<type, bool>::value, bool>::type
get() const
{
return get<ParameterType::PARAMETER_BOOL>();
}
template<typename type>
typename std::enable_if<
std::is_integral<type>::value && !std::is_same<type, bool>::value, int64_t>::type
get() const
{
return get<ParameterType::PARAMETER_INTEGER>();
}
template<typename type>
typename std::enable_if<std::is_floating_point<type>::value, double>::type
get() const
{
return get<ParameterType::PARAMETER_DOUBLE>();
}
template<typename type>
typename std::enable_if<std::is_convertible<type, std::string>::value, const std::string &>::type
get() const
{
return get<ParameterType::PARAMETER_STRING>();
}
template<typename type>
typename std::enable_if<
std::is_convertible<
type, const std::vector<uint8_t> &>::value, const std::vector<uint8_t> &>::type
get() const
{
return get<ParameterType::PARAMETER_BYTE_ARRAY>();
}
template<typename type>
typename std::enable_if<
std::is_convertible<
type, const std::vector<bool> &>::value, const std::vector<bool> &>::type
get() const
{
return get<ParameterType::PARAMETER_BOOL_ARRAY>();
}
template<typename type>
typename std::enable_if<
std::is_convertible<
type, const std::vector<int64_t> &>::value, const std::vector<int64_t> &>::type
get() const
{
return get<ParameterType::PARAMETER_INTEGER_ARRAY>();
}
template<typename type>
typename std::enable_if<
std::is_convertible<
type, const std::vector<double> &>::value, const std::vector<double> &>::type
get() const
{
return get<ParameterType::PARAMETER_DOUBLE_ARRAY>();
}
template<typename type>
typename std::enable_if<
std::is_convertible<
type, const std::vector<std::string> &>::value, const std::vector<std::string> &>::type
get() const
{
return get<ParameterType::PARAMETER_STRING_ARRAY>();
}
private:
rcl_interfaces::msg::ParameterValue value_;
};
/// Return the value of a parameter as a string
RCLCPP_PUBLIC
std::string
to_string(const ParameterValue & type);
} // namespace rclcpp
#endif // RCLCPP__PARAMETER_VALUE_HPP_

View File

@@ -205,11 +205,8 @@ public:
ipm.publisher_id = intra_process_publisher_id_;
ipm.message_sequence = message_seq;
auto status = rcl_publish(&intra_process_publisher_handle_, &ipm);
if (status != RCL_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("failed to publish intra process message: ") + rcl_get_error_string_safe());
// *INDENT-ON*
if (RCL_RET_OK != status) {
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish intra process message");
}
} else {
// Always destroy the message, even if we don't consume it, for consistency.
@@ -279,6 +276,25 @@ public:
return this->publish(*msg);
}
void
publish(const rcl_serialized_message_t * serialized_msg)
{
if (store_intra_process_message_) {
// TODO(Karsten1987): support serialized message passed by intraprocess
throw std::runtime_error("storing serialized messages in intra process is not supported yet");
}
auto status = rcl_publish_serialized_message(&publisher_handle_, serialized_msg);
if (RCL_RET_OK != status) {
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish serialized message");
}
}
void
publish(std::shared_ptr<const rcl_serialized_message_t> serialized_msg)
{
return this->publish(serialized_msg.get());
}
std::shared_ptr<MessageAlloc> get_allocator() const
{
return message_allocator_;
@@ -289,11 +305,8 @@ protected:
do_inter_process_publish(const MessageT * msg)
{
auto status = rcl_publish(&publisher_handle_, msg);
if (status != RCL_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("failed to publish message: ") + rcl_get_error_string_safe());
// *INDENT-ON*
if (RCL_RET_OK != status) {
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish message");
}
}

View File

@@ -46,19 +46,19 @@ struct PublisherFactory
{
// Creates a PublisherT<MessageT, ...> publisher object and returns it as a PublisherBase.
using PublisherFactoryFunction = std::function<
rclcpp::PublisherBase::SharedPtr(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_publisher_options_t & publisher_options)>;
rclcpp::PublisherBase::SharedPtr(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_publisher_options_t & publisher_options)>;
PublisherFactoryFunction create_typed_publisher;
// Adds the PublisherBase to the intraprocess manager with the correctly
// templated call to IntraProcessManager::store_intra_process_message.
using AddPublisherToIntraProcessManagerFunction = std::function<
uint64_t(
rclcpp::intra_process_manager::IntraProcessManager * ipm,
rclcpp::PublisherBase::SharedPtr publisher)>;
uint64_t(
rclcpp::intra_process_manager::IntraProcessManager * ipm,
rclcpp::PublisherBase::SharedPtr publisher)>;
AddPublisherToIntraProcessManagerFunction add_publisher_to_intra_process_manager;
@@ -66,8 +66,8 @@ struct PublisherFactory
// PublisherT::publish() and which handles the intra process transmission of
// the message being published.
using SharedPublishCallbackFactoryFunction = std::function<
rclcpp::PublisherBase::StoreMessageCallbackT(
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm)>;
rclcpp::PublisherBase::StoreMessageCallbackT(
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm)>;
SharedPublishCallbackFactoryFunction create_shared_publish_callback;
};

View File

@@ -49,10 +49,12 @@
* - rclcpp::Node::describe_parameters()
* - rclcpp::Node::list_parameters()
* - rclcpp::Node::register_param_change_callback()
* - rclcpp::parameter::ParameterVariant
* - rclcpp::Parameter
* - rclcpp::ParameterValue
* - rclcpp::AsyncParametersClient
* - rclcpp::SyncParametersClient
* - rclcpp/parameter.hpp
* - rclcpp/parameter_value.hpp
* - rclcpp/parameter_client.hpp
* - rclcpp/parameter_service.hpp
* - Rate:

View File

@@ -30,6 +30,7 @@
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/logging.hpp"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
@@ -41,11 +42,6 @@ class ServiceBase
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ServiceBase)
RCLCPP_PUBLIC
ServiceBase(
std::shared_ptr<rcl_node_t> node_handle,
const std::string & service_name);
RCLCPP_PUBLIC
explicit ServiceBase(
std::shared_ptr<rcl_node_t> node_handle);
@@ -54,15 +50,15 @@ public:
virtual ~ServiceBase();
RCLCPP_PUBLIC
std::string
const char *
get_service_name();
RCLCPP_PUBLIC
rcl_service_t *
std::shared_ptr<rcl_service_t>
get_service_handle();
RCLCPP_PUBLIC
const rcl_service_t *
std::shared_ptr<const rcl_service_t>
get_service_handle() const;
virtual std::shared_ptr<void> create_request() = 0;
@@ -84,8 +80,7 @@ protected:
std::shared_ptr<rcl_node_t> node_handle_;
rcl_service_t * service_handle_ = nullptr;
std::string service_name_;
std::shared_ptr<rcl_service_t> service_handle_;
bool owns_rcl_handle_ = true;
};
@@ -94,15 +89,15 @@ class Service : public ServiceBase
{
public:
using CallbackType = std::function<
void(
const std::shared_ptr<typename ServiceT::Request>,
std::shared_ptr<typename ServiceT::Response>)>;
void (
const std::shared_ptr<typename ServiceT::Request>,
std::shared_ptr<typename ServiceT::Response>)>;
using CallbackWithHeaderType = std::function<
void(
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<typename ServiceT::Request>,
std::shared_ptr<typename ServiceT::Response>)>;
void (
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<typename ServiceT::Request>,
std::shared_ptr<typename ServiceT::Response>)>;
RCLCPP_SMART_PTR_DEFINITIONS(Service)
Service(
@@ -110,17 +105,37 @@ public:
const std::string & service_name,
AnyServiceCallback<ServiceT> any_callback,
rcl_service_options_t & service_options)
: ServiceBase(node_handle, service_name), any_callback_(any_callback)
: ServiceBase(node_handle), any_callback_(any_callback)
{
using rosidl_typesupport_cpp::get_service_type_support_handle;
auto service_type_support_handle = get_service_type_support_handle<ServiceT>();
std::weak_ptr<rcl_node_t> weak_node_handle(node_handle_);
// rcl does the static memory allocation here
service_handle_ = new rcl_service_t;
*service_handle_ = rcl_get_zero_initialized_service();
service_handle_ = std::shared_ptr<rcl_service_t>(
new rcl_service_t, [weak_node_handle](rcl_service_t * service)
{
auto handle = weak_node_handle.lock();
if (handle) {
if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
RCLCPP_ERROR(
rclcpp::get_logger(rcl_node_get_logger_name(handle.get())).get_child("rclcpp"),
"Error in destruction of rcl service handle: %s",
rcl_get_error_string().str);
rcl_reset_error();
}
} else {
RCLCPP_ERROR(
rclcpp::get_logger("rclcpp"),
"Error in destruction of rcl service handle: "
"the Node Handle was destructed too early. You will leak memory");
}
delete service;
});
*service_handle_.get() = rcl_get_zero_initialized_service();
rcl_ret_t ret = rcl_service_init(
service_handle_,
service_handle_.get(),
node_handle.get(),
service_type_support_handle,
service_name.c_str(),
@@ -141,6 +156,24 @@ public:
}
}
Service(
std::shared_ptr<rcl_node_t> node_handle,
std::shared_ptr<rcl_service_t> service_handle,
AnyServiceCallback<ServiceT> any_callback)
: ServiceBase(node_handle),
any_callback_(any_callback)
{
// check if service handle was initialized
if (!rcl_service_is_valid(service_handle.get())) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("rcl_service_t in constructor argument must be initialized beforehand."));
// *INDENT-ON*
}
service_handle_ = service_handle;
}
Service(
std::shared_ptr<rcl_node_t> node_handle,
rcl_service_t * service_handle,
@@ -149,35 +182,22 @@ public:
any_callback_(any_callback)
{
// check if service handle was initialized
// TODO(karsten1987): Take this verification
// directly in rcl_*_t
// see: https://github.com/ros2/rcl/issues/81
if (!service_handle->impl) {
if (!rcl_service_is_valid(service_handle)) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("rcl_service_t in constructor argument must be initialized beforehand."));
// *INDENT-ON*
}
service_handle_ = service_handle;
service_name_ = std::string(rcl_service_get_service_name(service_handle));
owns_rcl_handle_ = false;
// In this case, rcl owns the service handle memory
service_handle_ = std::shared_ptr<rcl_service_t>(new rcl_service_t);
service_handle_->impl = service_handle->impl;
}
Service() = delete;
virtual ~Service()
{
// check if you have ownership of the handle
if (owns_rcl_handle_) {
if (rcl_service_fini(service_handle_, node_handle_.get()) != RCL_RET_OK) {
std::stringstream ss;
ss << "Error in destruction of rcl service_handle_ handle: " <<
rcl_get_error_string_safe() << '\n';
(std::cerr << ss.str()).flush();
rcl_reset_error();
}
delete service_handle_;
}
}
std::shared_ptr<void> create_request()
@@ -206,7 +226,7 @@ public:
std::shared_ptr<rmw_request_id_t> req_id,
std::shared_ptr<typename ServiceT::Response> response)
{
rcl_ret_t status = rcl_send_response(get_service_handle(), req_id.get(), response.get());
rcl_ret_t status = rcl_send_response(get_service_handle().get(), req_id.get(), response.get());
if (status != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(status, "failed to send response");

View File

@@ -25,6 +25,8 @@
#include "rclcpp/node.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rcutils/logging_macros.h"
#include "rmw/types.h"
namespace rclcpp
@@ -46,21 +48,16 @@ class AllocatorMemoryStrategy : public memory_strategy::MemoryStrategy
public:
RCLCPP_SMART_PTR_DEFINITIONS(AllocatorMemoryStrategy<Alloc>)
using ExecAllocTraits = allocator::AllocRebind<executor::AnyExecutable, Alloc>;
using ExecAlloc = typename ExecAllocTraits::allocator_type;
using ExecDeleter = allocator::Deleter<ExecAlloc, executor::AnyExecutable>;
using VoidAllocTraits = typename allocator::AllocRebind<void *, Alloc>;
using VoidAlloc = typename VoidAllocTraits::allocator_type;
explicit AllocatorMemoryStrategy(std::shared_ptr<Alloc> allocator)
{
executable_allocator_ = std::make_shared<ExecAlloc>(*allocator.get());
allocator_ = std::make_shared<VoidAlloc>(*allocator.get());
}
AllocatorMemoryStrategy()
{
executable_allocator_ = std::make_shared<ExecAlloc>();
allocator_ = std::make_shared<VoidAlloc>();
}
@@ -96,22 +93,22 @@ public:
{
for (size_t i = 0; i < wait_set->size_of_subscriptions; ++i) {
if (!wait_set->subscriptions[i]) {
subscription_handles_[i] = nullptr;
subscription_handles_[i].reset();
}
}
for (size_t i = 0; i < wait_set->size_of_services; ++i) {
if (!wait_set->services[i]) {
service_handles_[i] = nullptr;
service_handles_[i].reset();
}
}
for (size_t i = 0; i < wait_set->size_of_clients; ++i) {
if (!wait_set->clients[i]) {
client_handles_[i] = nullptr;
client_handles_[i].reset();
}
}
for (size_t i = 0; i < wait_set->size_of_timers; ++i) {
if (!wait_set->timers[i]) {
timer_handles_[i] = nullptr;
timer_handles_[i].reset();
}
}
@@ -186,53 +183,56 @@ public:
bool add_handles_to_wait_set(rcl_wait_set_t * wait_set)
{
for (auto subscription : subscription_handles_) {
if (rcl_wait_set_add_subscription(wait_set, subscription) != RCL_RET_OK) {
fprintf(stderr, "Couldn't add subscription to wait set: %s\n", rcl_get_error_string_safe());
if (rcl_wait_set_add_subscription(wait_set, subscription.get()) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't add subscription to wait set: %s", rcl_get_error_string().str);
return false;
}
}
for (auto client : client_handles_) {
if (rcl_wait_set_add_client(wait_set, client) != RCL_RET_OK) {
fprintf(stderr, "Couldn't add client to wait set: %s\n", rcl_get_error_string_safe());
if (rcl_wait_set_add_client(wait_set, client.get()) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't add client to wait set: %s", rcl_get_error_string().str);
return false;
}
}
for (auto service : service_handles_) {
if (rcl_wait_set_add_service(wait_set, service) != RCL_RET_OK) {
fprintf(stderr, "Couldn't add service to wait set: %s\n", rcl_get_error_string_safe());
if (rcl_wait_set_add_service(wait_set, service.get()) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't add service to wait set: %s", rcl_get_error_string().str);
return false;
}
}
for (auto timer : timer_handles_) {
if (rcl_wait_set_add_timer(wait_set, timer) != RCL_RET_OK) {
fprintf(stderr, "Couldn't add timer to wait set: %s\n", rcl_get_error_string_safe());
if (rcl_wait_set_add_timer(wait_set, timer.get()) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't add timer to wait set: %s", rcl_get_error_string().str);
return false;
}
}
for (auto guard_condition : guard_conditions_) {
if (rcl_wait_set_add_guard_condition(wait_set, guard_condition) != RCL_RET_OK) {
fprintf(stderr, "Couldn't add guard_condition to wait set: %s\n",
rcl_get_error_string_safe());
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't add guard_condition to wait set: %s",
rcl_get_error_string().str);
return false;
}
}
return true;
}
/// Provide a newly initialized AnyExecutable object.
// \return Shared pointer to the fresh executable.
executor::AnyExecutable::SharedPtr instantiate_next_executable()
{
return std::allocate_shared<executor::AnyExecutable>(*executable_allocator_.get());
}
virtual void
get_next_subscription(
executor::AnyExecutable::SharedPtr any_exec,
executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes)
{
auto it = subscription_handles_.begin();
@@ -249,7 +249,7 @@ public:
if (!group) {
// Group was not found, meaning the subscription is not valid...
// Remove it from the ready list and continue looking
subscription_handles_.erase(it);
it = subscription_handles_.erase(it);
continue;
}
if (!group->can_be_taken_from().load()) {
@@ -260,23 +260,23 @@ public:
}
// Otherwise it is safe to set and return the any_exec
if (is_intra_process) {
any_exec->subscription_intra_process = subscription;
any_exec.subscription_intra_process = subscription;
} else {
any_exec->subscription = subscription;
any_exec.subscription = subscription;
}
any_exec->callback_group = group;
any_exec->node_base = get_node_by_group(group, weak_nodes);
any_exec.callback_group = group;
any_exec.node_base = get_node_by_group(group, weak_nodes);
subscription_handles_.erase(it);
return;
}
// Else, the subscription is no longer valid, remove it and continue
subscription_handles_.erase(it);
it = subscription_handles_.erase(it);
}
}
virtual void
get_next_service(
executor::AnyExecutable::SharedPtr any_exec,
executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes)
{
auto it = service_handles_.begin();
@@ -288,7 +288,7 @@ public:
if (!group) {
// Group was not found, meaning the service is not valid...
// Remove it from the ready list and continue looking
service_handles_.erase(it);
it = service_handles_.erase(it);
continue;
}
if (!group->can_be_taken_from().load()) {
@@ -298,19 +298,19 @@ public:
continue;
}
// Otherwise it is safe to set and return the any_exec
any_exec->service = service;
any_exec->callback_group = group;
any_exec->node_base = get_node_by_group(group, weak_nodes);
any_exec.service = service;
any_exec.callback_group = group;
any_exec.node_base = get_node_by_group(group, weak_nodes);
service_handles_.erase(it);
return;
}
// Else, the service is no longer valid, remove it and continue
service_handles_.erase(it);
it = service_handles_.erase(it);
}
}
virtual void
get_next_client(executor::AnyExecutable::SharedPtr any_exec, const WeakNodeVector & weak_nodes)
get_next_client(executor::AnyExecutable & any_exec, const WeakNodeVector & weak_nodes)
{
auto it = client_handles_.begin();
while (it != client_handles_.end()) {
@@ -321,7 +321,7 @@ public:
if (!group) {
// Group was not found, meaning the service is not valid...
// Remove it from the ready list and continue looking
client_handles_.erase(it);
it = client_handles_.erase(it);
continue;
}
if (!group->can_be_taken_from().load()) {
@@ -331,14 +331,14 @@ public:
continue;
}
// Otherwise it is safe to set and return the any_exec
any_exec->client = client;
any_exec->callback_group = group;
any_exec->node_base = get_node_by_group(group, weak_nodes);
any_exec.client = client;
any_exec.callback_group = group;
any_exec.node_base = get_node_by_group(group, weak_nodes);
client_handles_.erase(it);
return;
}
// Else, the service is no longer valid, remove it and continue
client_handles_.erase(it);
it = client_handles_.erase(it);
}
}
@@ -375,16 +375,15 @@ public:
private:
template<typename T>
using VectorRebind =
std::vector<T, typename std::allocator_traits<Alloc>::template rebind_alloc<T>>;
std::vector<T, typename std::allocator_traits<Alloc>::template rebind_alloc<T>>;
VectorRebind<const rcl_guard_condition_t *> guard_conditions_;
VectorRebind<const rcl_subscription_t *> subscription_handles_;
VectorRebind<const rcl_service_t *> service_handles_;
VectorRebind<const rcl_client_t *> client_handles_;
VectorRebind<const rcl_timer_t *> timer_handles_;
VectorRebind<std::shared_ptr<const rcl_subscription_t>> subscription_handles_;
VectorRebind<std::shared_ptr<const rcl_service_t>> service_handles_;
VectorRebind<std::shared_ptr<const rcl_client_t>> client_handles_;
VectorRebind<std::shared_ptr<const rcl_timer_t>> timer_handles_;
std::shared_ptr<ExecAlloc> executable_allocator_;
std::shared_ptr<VoidAlloc> allocator_;
};

View File

@@ -29,12 +29,13 @@
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/message_memory_strategy.hpp"
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/subscription_traits.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
@@ -58,13 +59,15 @@ public:
* \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
* \param[in] topic_name Name of the topic to subscribe to.
* \param[in] subscription_options options for the subscription.
* \param[in] is_serialized is true if the message will be delivered still serialized
*/
RCLCPP_PUBLIC
SubscriptionBase(
std::shared_ptr<rcl_node_t> node_handle,
const rosidl_message_type_support_t & type_support_handle,
const std::string & topic_name,
const rcl_subscription_options_t & subscription_options);
const rcl_subscription_options_t & subscription_options,
bool is_serialized = false);
/// Default destructor.
RCLCPP_PUBLIC
@@ -76,21 +79,27 @@ public:
get_topic_name() const;
RCLCPP_PUBLIC
rcl_subscription_t *
std::shared_ptr<rcl_subscription_t>
get_subscription_handle();
RCLCPP_PUBLIC
const rcl_subscription_t *
const std::shared_ptr<rcl_subscription_t>
get_subscription_handle() const;
RCLCPP_PUBLIC
virtual const rcl_subscription_t *
virtual const std::shared_ptr<rcl_subscription_t>
get_intra_process_subscription_handle() const;
/// Borrow a new message.
/** \return Shared pointer to the fresh message. */
virtual std::shared_ptr<void>
create_message() = 0;
/// Borrow a new serialized message
/** \return Shared pointer to a rcl_message_serialized_t. */
virtual std::shared_ptr<rcl_serialized_message_t>
create_serialized_message() = 0;
/// Check if we need to handle the message, and execute the callback if we do.
/**
* \param[in] message Shared pointer to the message to handle.
@@ -104,31 +113,47 @@ public:
virtual void
return_message(std::shared_ptr<void> & message) = 0;
/// Return the message borrowed in create_serialized_message.
/** \param[in] message Shared pointer to the returned message. */
virtual void
return_serialized_message(std::shared_ptr<rcl_serialized_message_t> & message) = 0;
virtual void
handle_intra_process_message(
rcl_interfaces::msg::IntraProcessMessage & ipm,
const rmw_message_info_t & message_info) = 0;
const rosidl_message_type_support_t &
get_message_type_support_handle() const;
bool
is_serialized() const;
protected:
rcl_subscription_t intra_process_subscription_handle_ = rcl_get_zero_initialized_subscription();
rcl_subscription_t subscription_handle_ = rcl_get_zero_initialized_subscription();
std::shared_ptr<rcl_subscription_t> intra_process_subscription_handle_;
std::shared_ptr<rcl_subscription_t> subscription_handle_;
std::shared_ptr<rcl_node_t> node_handle_;
private:
RCLCPP_DISABLE_COPY(SubscriptionBase)
rosidl_message_type_support_t type_support_;
bool is_serialized_;
};
/// Subscription implementation, templated on the type of message this subscription receives.
template<typename MessageT, typename Alloc = std::allocator<void>>
template<
typename CallbackMessageT,
typename Alloc = std::allocator<void>>
class Subscription : public SubscriptionBase
{
friend class rclcpp::node_interfaces::NodeTopicsInterface;
public:
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAllocTraits = allocator::AllocRebind<CallbackMessageT, Alloc>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
using MessageDeleter = allocator::Deleter<MessageAlloc, CallbackMessageT>;
using MessageUniquePtr = std::unique_ptr<CallbackMessageT, MessageDeleter>;
RCLCPP_SMART_PTR_DEFINITIONS(Subscription)
@@ -137,6 +162,7 @@ public:
* The constructor for a subscription is almost never called directly. Instead, subscriptions
* should be instantiated through Node::create_subscription.
* \param[in] node_handle rcl representation of the node that owns this subscription.
* \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
* \param[in] topic_name Name of the topic to subscribe to.
* \param[in] subscription_options options for the subscription.
* \param[in] callback User defined callback to call when a message is received.
@@ -144,17 +170,19 @@ public:
*/
Subscription(
std::shared_ptr<rcl_node_t> node_handle,
const rosidl_message_type_support_t & type_support_handle,
const std::string & topic_name,
const rcl_subscription_options_t & subscription_options,
AnySubscriptionCallback<MessageT, Alloc> callback,
typename message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
memory_strategy = message_memory_strategy::MessageMemoryStrategy<MessageT,
AnySubscriptionCallback<CallbackMessageT, Alloc> callback,
typename message_memory_strategy::MessageMemoryStrategy<CallbackMessageT, Alloc>::SharedPtr
memory_strategy = message_memory_strategy::MessageMemoryStrategy<CallbackMessageT,
Alloc>::create_default())
: SubscriptionBase(
node_handle,
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
type_support_handle,
topic_name,
subscription_options),
subscription_options,
rclcpp::subscription_traits::is_serialized_subscription_argument<CallbackMessageT>::value),
any_callback_(callback),
message_memory_strategy_(memory_strategy),
get_intra_process_message_callback_(nullptr),
@@ -167,11 +195,12 @@ public:
* \param[in] message_memory_strategy Shared pointer to the memory strategy to set.
*/
void set_message_memory_strategy(
typename message_memory_strategy::MessageMemoryStrategy<MessageT,
typename message_memory_strategy::MessageMemoryStrategy<CallbackMessageT,
Alloc>::SharedPtr message_memory_strategy)
{
message_memory_strategy_ = message_memory_strategy;
}
std::shared_ptr<void> create_message()
{
/* The default message memory strategy provides a dynamically allocated message on each call to
@@ -181,6 +210,11 @@ public:
return message_memory_strategy_->borrow_message();
}
std::shared_ptr<rcl_serialized_message_t> create_serialized_message()
{
return message_memory_strategy_->borrow_serialized_message();
}
void handle_message(std::shared_ptr<void> & message, const rmw_message_info_t & message_info)
{
if (matches_any_intra_process_publishers_) {
@@ -190,7 +224,7 @@ public:
return;
}
}
auto typed_message = std::static_pointer_cast<MessageT>(message);
auto typed_message = std::static_pointer_cast<CallbackMessageT>(message);
any_callback_.dispatch(typed_message, message_info);
}
@@ -198,10 +232,15 @@ public:
/** \param message message to be returned */
void return_message(std::shared_ptr<void> & message)
{
auto typed_message = std::static_pointer_cast<MessageT>(message);
auto typed_message = std::static_pointer_cast<CallbackMessageT>(message);
message_memory_strategy_->return_message(typed_message);
}
void return_serialized_message(std::shared_ptr<rcl_serialized_message_t> & message)
{
message_memory_strategy_->return_serialized_message(message);
}
void handle_intra_process_message(
rcl_interfaces::msg::IntraProcessMessage & ipm,
const rmw_message_info_t & message_info)
@@ -229,8 +268,8 @@ public:
}
using GetMessageCallbackType =
std::function<void(uint64_t, uint64_t, uint64_t, MessageUniquePtr &)>;
using MatchesAnyPublishersCallbackType = std::function<bool(const rmw_gid_t *)>;
std::function<void (uint64_t, uint64_t, uint64_t, MessageUniquePtr &)>;
using MatchesAnyPublishersCallbackType = std::function<bool (const rmw_gid_t *)>;
/// Implemenation detail.
void setup_intra_process(
@@ -241,7 +280,7 @@ public:
{
std::string intra_process_topic_name = std::string(get_topic_name()) + "/_intra";
rcl_ret_t ret = rcl_subscription_init(
&intra_process_subscription_handle_,
intra_process_subscription_handle_.get(),
node_handle_.get(),
rclcpp::type_support::get_intra_process_message_msg_type_support(),
intra_process_topic_name.c_str(),
@@ -266,20 +305,20 @@ public:
}
/// Implemenation detail.
const rcl_subscription_t *
const std::shared_ptr<rcl_subscription_t>
get_intra_process_subscription_handle() const
{
if (!get_intra_process_message_callback_) {
return nullptr;
}
return &intra_process_subscription_handle_;
return intra_process_subscription_handle_;
}
private:
RCLCPP_DISABLE_COPY(Subscription)
AnySubscriptionCallback<MessageT, Alloc> any_callback_;
typename message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
AnySubscriptionCallback<CallbackMessageT, Alloc> any_callback_;
typename message_memory_strategy::MessageMemoryStrategy<CallbackMessageT, Alloc>::SharedPtr
message_memory_strategy_;
GetMessageCallbackType get_intra_process_message_callback_;

View File

@@ -25,6 +25,7 @@
#include "rosidl_typesupport_cpp/message_type_support.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/subscription_traits.hpp"
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -47,40 +48,46 @@ struct SubscriptionFactory
{
// Creates a Subscription<MessageT> object and returns it as a SubscriptionBase.
using SubscriptionFactoryFunction = std::function<
rclcpp::SubscriptionBase::SharedPtr(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_subscription_options_t & subscription_options)>;
rclcpp::SubscriptionBase::SharedPtr(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_subscription_options_t & subscription_options)>;
SubscriptionFactoryFunction create_typed_subscription;
// Function that takes a MessageT from the intra process manager
using SetupIntraProcessFunction = std::function<
void(
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm,
rclcpp::SubscriptionBase::SharedPtr subscription,
const rcl_subscription_options_t & subscription_options)>;
void (
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm,
rclcpp::SubscriptionBase::SharedPtr subscription,
const rcl_subscription_options_t & subscription_options)>;
SetupIntraProcessFunction setup_intra_process;
};
/// Return a SubscriptionFactory with functions for creating a SubscriptionT<MessageT, Alloc>.
template<typename MessageT, typename CallbackT, typename Alloc, typename SubscriptionT>
template<
typename MessageT,
typename CallbackT,
typename Alloc,
typename CallbackMessageT,
typename SubscriptionT>
SubscriptionFactory
create_subscription_factory(
CallbackT && callback,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT, Alloc>::SharedPtr
msg_mem_strat,
std::shared_ptr<Alloc> allocator)
{
SubscriptionFactory factory;
using rclcpp::AnySubscriptionCallback;
AnySubscriptionCallback<MessageT, Alloc> any_subscription_callback(allocator);
AnySubscriptionCallback<CallbackMessageT, Alloc> any_subscription_callback(allocator);
any_subscription_callback.set(std::forward<CallbackT>(callback));
auto message_alloc =
std::make_shared<typename Subscription<MessageT, Alloc>::MessageAlloc>();
std::make_shared<typename Subscription<CallbackMessageT, Alloc>::MessageAlloc>();
// factory function that creates a MessageT specific SubscriptionT
factory.create_typed_subscription =
@@ -91,13 +98,14 @@ create_subscription_factory(
) -> rclcpp::SubscriptionBase::SharedPtr
{
subscription_options.allocator =
rclcpp::allocator::get_rcl_allocator<MessageT>(*message_alloc.get());
rclcpp::allocator::get_rcl_allocator<CallbackMessageT>(*message_alloc.get());
using rclcpp::Subscription;
using rclcpp::SubscriptionBase;
auto sub = Subscription<MessageT, Alloc>::make_shared(
auto sub = Subscription<CallbackMessageT, Alloc>::make_shared(
node_base->get_shared_rcl_node_handle(),
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
topic_name,
subscription_options,
any_subscription_callback,
@@ -117,7 +125,7 @@ create_subscription_factory(
uint64_t intra_process_subscription_id = ipm->add_subscription(subscription);
auto intra_process_options = rcl_subscription_get_default_options();
intra_process_options.allocator = rclcpp::allocator::get_rcl_allocator<MessageT>(
intra_process_options.allocator = rclcpp::allocator::get_rcl_allocator<CallbackMessageT>(
*message_alloc.get());
intra_process_options.qos = subscription_options.qos;
intra_process_options.ignore_local_publications = false;
@@ -128,7 +136,7 @@ create_subscription_factory(
uint64_t publisher_id,
uint64_t message_sequence,
uint64_t subscription_id,
typename rclcpp::Subscription<MessageT, Alloc>::MessageUniquePtr & message)
typename rclcpp::Subscription<CallbackMessageT, Alloc>::MessageUniquePtr & message)
{
auto ipm = weak_ipm.lock();
if (!ipm) {
@@ -136,7 +144,7 @@ create_subscription_factory(
throw std::runtime_error(
"intra process take called after destruction of intra process manager");
}
ipm->take_intra_process_message<MessageT, Alloc>(
ipm->take_intra_process_message<CallbackMessageT, Alloc>(
publisher_id, message_sequence, subscription_id, message);
};

View File

@@ -0,0 +1,80 @@
// 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__SUBSCRIPTION_TRAITS_HPP_
#define RCLCPP__SUBSCRIPTION_TRAITS_HPP_
#include <memory>
#include "rclcpp/function_traits.hpp"
namespace rclcpp
{
namespace subscription_traits
{
/*
* The current version of uncrustify has a misinterpretion here
* between `:` used for inheritance vs for initializer list
* The result is that whenever a templated struct is used,
* the colon has to be without any whitespace next to it whereas
* when no template is used, the colon has to be separated by a space.
* Cheers!
*/
template<typename T>
struct is_serialized_subscription_argument : std::false_type
{};
template<>
struct is_serialized_subscription_argument<rcl_serialized_message_t>: std::true_type
{};
template<>
struct is_serialized_subscription_argument<std::shared_ptr<rcl_serialized_message_t>>
: std::true_type
{};
template<typename T>
struct is_serialized_subscription : is_serialized_subscription_argument<T>
{};
template<typename CallbackT>
struct is_serialized_callback
: is_serialized_subscription_argument<
typename rclcpp::function_traits::function_traits<CallbackT>::template argument_type<0>>
{};
template<typename MessageT>
struct extract_message_type
{
using type = typename std::remove_cv<MessageT>::type;
};
template<typename MessageT>
struct extract_message_type<std::shared_ptr<MessageT>>: extract_message_type<MessageT>
{};
template<typename MessageT, typename Deleter>
struct extract_message_type<std::unique_ptr<MessageT, Deleter>>: extract_message_type<MessageT>
{};
template<typename CallbackT>
struct has_message_type : extract_message_type<
typename rclcpp::function_traits::function_traits<CallbackT>::template argument_type<0>>
{};
} // namespace subscription_traits
} // namespace rclcpp
#endif // RCLCPP__SUBSCRIPTION_TRAITS_HPP_

View File

@@ -35,7 +35,7 @@ public:
Time(int32_t seconds, uint32_t nanoseconds, rcl_clock_type_t clock_type = RCL_SYSTEM_TIME);
RCLCPP_PUBLIC
explicit Time(uint64_t nanoseconds = 0, rcl_clock_type_t clock = RCL_SYSTEM_TIME);
explicit Time(int64_t nanoseconds = 0, rcl_clock_type_t clock = RCL_SYSTEM_TIME);
RCLCPP_PUBLIC
Time(const Time & rhs);
@@ -102,6 +102,17 @@ public:
rcl_time_point_value_t
nanoseconds() const;
RCLCPP_PUBLIC
static Time
max();
/// \return the seconds since epoch as a floating point number.
/// \warning Depending on sizeof(double) there could be significant precision loss.
/// When an exact time is required use nanoseconds() instead.
RCLCPP_PUBLIC
double
seconds() const;
RCLCPP_PUBLIC
rcl_clock_type_t
get_clock_type() const;

View File

@@ -21,6 +21,7 @@
#include "rcl/time.h"
#include "builtin_interfaces/msg/time.hpp"
#include "rosgraph_msgs/msg/clock.hpp"
#include "rcl_interfaces/msg/parameter_event.hpp"
#include "rclcpp/node.hpp"
@@ -75,13 +76,20 @@ private:
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
// The subscription for the clock callback
using MessageT = builtin_interfaces::msg::Time;
using MessageT = rosgraph_msgs::msg::Clock;
using Alloc = std::allocator<void>;
using SubscriptionT = rclcpp::Subscription<MessageT, Alloc>;
std::shared_ptr<SubscriptionT> clock_subscription_;
std::mutex clock_sub_lock_;
// The clock callback itself
void clock_cb(const builtin_interfaces::msg::Time::SharedPtr msg);
void clock_cb(const rosgraph_msgs::msg::Clock::SharedPtr msg);
// Create the subscription for the clock topic
void create_clock_sub();
// Destroy the subscription for the clock topic
void destroy_clock_sub();
// Parameter Client pointer
std::shared_ptr<rclcpp::AsyncParametersClient> parameter_client_;
@@ -115,7 +123,7 @@ private:
// This is needed when new clocks are added.
bool ros_time_active_;
// Last set message to be passed to newly registered clocks
builtin_interfaces::msg::Time::SharedPtr last_msg_set_;
rosgraph_msgs::msg::Clock::SharedPtr last_msg_set_;
// A lock to protect iterating the associated_clocks_ field.
std::mutex clock_list_lock_;

View File

@@ -23,6 +23,7 @@
#include <type_traits>
#include <utility>
#include "rclcpp/clock.hpp"
#include "rclcpp/function_traits.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/rate.hpp"
@@ -44,7 +45,7 @@ public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(TimerBase)
RCLCPP_PUBLIC
explicit TimerBase(std::chrono::nanoseconds period);
explicit TimerBase(Clock::SharedPtr clock, std::chrono::nanoseconds period);
RCLCPP_PUBLIC
~TimerBase();
@@ -62,7 +63,7 @@ public:
execute_callback() = 0;
RCLCPP_PUBLIC
const rcl_timer_t *
std::shared_ptr<const rcl_timer_t>
get_timer_handle();
/// Check how long the timer has until its next scheduled callback.
@@ -85,21 +86,20 @@ public:
bool is_ready();
protected:
rcl_timer_t timer_handle_ = rcl_get_zero_initialized_timer();
Clock::SharedPtr clock_;
std::shared_ptr<rcl_timer_t> timer_handle_;
};
using VoidCallbackType = std::function<void()>;
using TimerCallbackType = std::function<void(TimerBase &)>;
using VoidCallbackType = std::function<void ()>;
using TimerCallbackType = std::function<void (TimerBase &)>;
/// Generic timer templated on the clock type. Periodically executes a user-specified callback.
/// Generic timer. Periodically executes a user-specified callback.
template<
typename FunctorT,
class Clock,
typename std::enable_if<
(rclcpp::function_traits::same_arguments<FunctorT, VoidCallbackType>::value ||
rclcpp::function_traits::same_arguments<FunctorT, TimerCallbackType>::value) &&
Clock::is_steady
rclcpp::function_traits::same_arguments<FunctorT, VoidCallbackType>::value ||
rclcpp::function_traits::same_arguments<FunctorT, TimerCallbackType>::value
>::type * = nullptr
>
class GenericTimer : public TimerBase
@@ -109,11 +109,14 @@ public:
/// Default constructor.
/**
* \param[in] clock The clock providing the current time.
* \param[in] period The interval at which the timer fires.
* \param[in] callback User-specified callback function.
*/
GenericTimer(std::chrono::nanoseconds period, FunctorT && callback)
: TimerBase(period), callback_(std::forward<FunctorT>(callback))
explicit GenericTimer(
Clock::SharedPtr clock, std::chrono::nanoseconds period, FunctorT && callback
)
: TimerBase(clock, period), callback_(std::forward<FunctorT>(callback))
{
}
@@ -122,15 +125,12 @@ public:
{
// Stop the timer from running.
cancel();
if (rcl_timer_fini(&timer_handle_) != RCL_RET_OK) {
fprintf(stderr, "Failed to clean up rcl timer handle: %s\n", rcl_get_error_string_safe());
}
}
void
execute_callback()
{
rcl_ret_t ret = rcl_timer_call(&timer_handle_);
rcl_ret_t ret = rcl_timer_call(timer_handle_.get());
if (ret == RCL_RET_TIMER_CANCELED) {
return;
}
@@ -168,7 +168,7 @@ public:
virtual bool
is_steady()
{
return Clock::is_steady;
return clock_->get_clock_type() == RCL_STEADY_TIME;
}
protected:
@@ -177,8 +177,26 @@ protected:
FunctorT callback_;
};
template<typename CallbackType>
using WallTimer = GenericTimer<CallbackType, std::chrono::steady_clock>;
template<
typename FunctorT,
typename std::enable_if<
rclcpp::function_traits::same_arguments<FunctorT, VoidCallbackType>::value ||
rclcpp::function_traits::same_arguments<FunctorT, TimerCallbackType>::value
>::type * = nullptr
>
class WallTimer : public GenericTimer<FunctorT>
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(WallTimer)
explicit WallTimer(std::chrono::nanoseconds period, FunctorT && callback)
: GenericTimer<FunctorT>(
std::make_shared<Clock>(RCL_STEADY_TIME), period, std::move(callback))
{}
protected:
RCLCPP_DISABLE_COPY(WallTimer)
};
} // namespace rclcpp

View File

@@ -17,6 +17,8 @@
#include <chrono>
#include <functional>
#include <limits>
#include <vector>
#include "rclcpp/visibility_control.hpp"
@@ -44,7 +46,6 @@ std::string to_string(T value)
namespace rclcpp
{
/// Initialize communications via the rmw implementation and set up a global signal handler.
/**
* \param[in] argc Number of arguments.
@@ -52,7 +53,33 @@ namespace rclcpp
*/
RCLCPP_PUBLIC
void
init(int argc, char * argv[]);
init(int argc, char const * const argv[]);
/// Initialize communications via the rmw implementation and set up a global signal handler.
/**
* Additionally removes ROS-specific arguments from the argument vector.
* \param[in] argc Number of arguments.
* \param[in] argv Argument vector.
* \returns Members of the argument vector that are not ROS arguments.
*/
RCLCPP_PUBLIC
std::vector<std::string>
init_and_remove_ros_arguments(int argc, char const * const argv[]);
/// Remove ROS-specific arguments from argument vector.
/**
* Some arguments may not have been intended as ROS arguments.
* This function populates a the aruments in a vector.
* Since the first argument is always assumed to be a process name, the vector
* will always contain the process name.
*
* \param[in] argc Number of arguments.
* \param[in] argv Argument vector.
* \returns Members of the argument vector that are not ROS arguments.
*/
RCLCPP_PUBLIC
std::vector<std::string>
remove_ros_arguments(int argc, char const * const argv[]);
/// Check rclcpp's status.
/** \return True if SIGINT hasn't fired yet, false otherwise. */
@@ -60,6 +87,12 @@ RCLCPP_PUBLIC
bool
ok();
/// Returns true if init() has already been called.
/** \return True if init() has been called, false otherwise. */
RCLCPP_PUBLIC
bool
is_initialized();
/// Notify the signal handler and rmw that rclcpp is shutting down.
RCLCPP_PUBLIC
void
@@ -109,6 +142,94 @@ RCLCPP_PUBLIC
bool
sleep_for(const std::chrono::nanoseconds & nanoseconds);
/// Safely check if addition will overflow.
/**
* The type of the operands, T, should have defined
* std::numeric_limits<T>::max(), `>`, `<` and `-` operators.
*
* \param[in] x is the first addend.
* \param[in] y is the second addend.
* \tparam T is type of the operands.
* \return True if the x + y sum is greater than T::max value.
*/
template<typename T>
bool
add_will_overflow(const T x, const T y)
{
return (y > 0) && (x > (std::numeric_limits<T>::max() - y));
}
/// Safely check if addition will underflow.
/**
* The type of the operands, T, should have defined
* std::numeric_limits<T>::min(), `>`, `<` and `-` operators.
*
* \param[in] x is the first addend.
* \param[in] y is the second addend.
* \tparam T is type of the operands.
* \return True if the x + y sum is less than T::min value.
*/
template<typename T>
bool
add_will_underflow(const T x, const T y)
{
return (y < 0) && (x < (std::numeric_limits<T>::min() - y));
}
/// Safely check if subtraction will overflow.
/**
* The type of the operands, T, should have defined
* std::numeric_limits<T>::max(), `>`, `<` and `+` operators.
*
* \param[in] x is the minuend.
* \param[in] y is the subtrahend.
* \tparam T is type of the operands.
* \return True if the difference `x - y` sum is grater than T::max value.
*/
template<typename T>
bool
sub_will_overflow(const T x, const T y)
{
return (y < 0) && (x > (std::numeric_limits<T>::max() + y));
}
/// Safely check if subtraction will underflow.
/**
* The type of the operands, T, should have defined
* std::numeric_limits<T>::min(), `>`, `<` and `+` operators.
*
* \param[in] x is the minuend.
* \param[in] y is the subtrahend.
* \tparam T is type of the operands.
* \return True if the difference `x - y` sum is less than T::min value.
*/
template<typename T>
bool
sub_will_underflow(const T x, const T y)
{
return (y > 0) && (x < (std::numeric_limits<T>::min() + y));
}
/// Return the given string.
/**
* This function is overloaded to transform any string to C-style string.
*
* \param[in] string_in is the string to be returned
* \return the given string
*/
RCLCPP_PUBLIC
const char *
get_c_string(const char * string_in);
/// Return the C string from the given std::string.
/**
* \param[in] string_in is a std::string
* \return the C string from the std::string
*/
RCLCPP_PUBLIC
const char *
get_c_string(const std::string & string_in);
} // namespace rclcpp
#endif // RCLCPP__UTILITIES_HPP_

View File

@@ -2,27 +2,30 @@
<?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.4.0</version>
<version>0.6.0</version>
<description>The ROS client library in C++.</description>
<maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
<license>Apache License 2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<buildtool_depend>ament_cmake_ros</buildtool_depend>
<build_export_depend>rmw</build_export_depend>
<build_depend>builtin_interfaces</build_depend>
<build_depend>rcl_interfaces</build_depend>
<build_depend>rosgraph_msgs</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>rosgraph_msgs</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>
<depend>rcl_yaml_param_parser</depend>
<depend>rmw_implementation</depend>
<exec_depend>ament_cmake</exec_depend>
@@ -33,6 +36,7 @@
<test_depend>ament_lint_common</test_depend>
<test_depend>rmw</test_depend>
<test_depend>rmw_implementation_cmake</test_depend>
<test_depend>test_msgs</test_depend>
<export>
<build_type>ament_cmake</build_type>

View File

@@ -21,6 +21,7 @@
#include "rclcpp/logger.hpp"
#include "rcutils/logging_macros.h"
#include "rclcpp/utilities.hpp"
// These are used for compiling out logging macros lower than a minimum severity.
#define RCLCPP_LOG_MIN_SEVERITY_DEBUG 0
@@ -30,6 +31,9 @@
#define RCLCPP_LOG_MIN_SEVERITY_FATAL 4
#define RCLCPP_LOG_MIN_SEVERITY_NONE 5
#define RCLCPP_FIRST_ARG(N, ...) N
#define RCLCPP_ALL_BUT_FIRST_ARGS(N, ...) __VA_ARGS__
/**
* \def RCLCPP_LOG_MIN_SEVERITY
* Define RCLCPP_LOG_MIN_SEVERITY=RCLCPP_LOG_MIN_SEVERITY_[DEBUG|INFO|WARN|ERROR|FATAL]
@@ -82,11 +86,13 @@ def is_supported_feature_combination(feature_combination):
@[ for param_name, doc_line in feature_combinations[feature_combination].params.items()]@
* \param @(param_name) @(doc_line)
@[ end for]@
* \param ... The format string, followed by the variable arguments for the format string
* \param ... The format string, followed by the variable arguments for the format string.
* It also accepts a single argument of type std::string.
*/
#define RCLCPP_@(severity)@(suffix)(logger, @(''.join([p + ', ' for p in get_macro_parameters(feature_combination).keys()]))...) \
static_assert( \
::std::is_same<std::remove_reference<decltype(logger)>::type, ::rclcpp::Logger>::value, \
::std::is_same<typename std::remove_reference<decltype(logger)>::type, \
typename ::rclcpp::Logger>::value, \
"First argument to logging macros must be an rclcpp::Logger"); \
RCUTILS_LOG_@(severity)@(suffix)_NAMED( \
@{params = get_macro_parameters(feature_combination).keys()}@
@@ -94,7 +100,8 @@ def is_supported_feature_combination(feature_combination):
@(''.join([' ' + p + ', \\\n' for p in params]))@
@[ end if]@
logger.get_name(), \
__VA_ARGS__)
rclcpp::get_c_string(RCLCPP_FIRST_ARG(__VA_ARGS__, "")), \
RCLCPP_ALL_BUT_FIRST_ARGS(__VA_ARGS__,""))
@[ end for]@
#endif

View File

@@ -14,6 +14,7 @@
#include "rclcpp/client.hpp"
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <memory>
@@ -26,6 +27,7 @@
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/logging.hpp"
using rclcpp::ClientBase;
using rclcpp::exceptions::InvalidNodeError;
@@ -33,39 +35,65 @@ using rclcpp::exceptions::throw_from_rcl_error;
ClientBase::ClientBase(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
const std::string & service_name)
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph)
: node_graph_(node_graph),
node_handle_(node_base->get_shared_rcl_node_handle()),
service_name_(service_name)
{}
node_handle_(node_base->get_shared_rcl_node_handle())
{
std::weak_ptr<rcl_node_t> weak_node_handle(node_handle_);
client_handle_ = std::shared_ptr<rcl_client_t>(
new rcl_client_t, [weak_node_handle](rcl_client_t * client)
{
auto handle = weak_node_handle.lock();
if (handle) {
if (rcl_client_fini(client, handle.get()) != RCL_RET_OK) {
RCLCPP_ERROR(
rclcpp::get_logger(rcl_node_get_logger_name(handle.get())).get_child("rclcpp"),
"Error in destruction of rcl client handle: %s", rcl_get_error_string().str);
rcl_reset_error();
}
} else {
RCLCPP_ERROR(
rclcpp::get_logger("rclcpp"),
"Error in destruction of rcl client handle: "
"the Node Handle was destructed too early. You will leak memory");
}
delete client;
});
*client_handle_.get() = rcl_get_zero_initialized_client();
}
ClientBase::~ClientBase() {}
ClientBase::~ClientBase()
{
// Make sure the client handle is destructed as early as possible and before the node handle
client_handle_.reset();
}
const std::string &
const char *
ClientBase::get_service_name() const
{
return this->service_name_;
return rcl_client_get_service_name(this->get_client_handle().get());
}
rcl_client_t *
std::shared_ptr<rcl_client_t>
ClientBase::get_client_handle()
{
return &client_handle_;
return client_handle_;
}
const rcl_client_t *
std::shared_ptr<const rcl_client_t>
ClientBase::get_client_handle() const
{
return &client_handle_;
return client_handle_;
}
bool
ClientBase::service_is_ready() const
{
bool is_ready;
rcl_ret_t ret =
rcl_service_server_is_available(this->get_rcl_node_handle(), &client_handle_, &is_ready);
rcl_ret_t ret = rcl_service_server_is_available(
this->get_rcl_node_handle(),
this->get_client_handle().get(),
&is_ready);
if (ret != RCL_RET_OK) {
throw_from_rcl_error(ret, "rcl_service_server_is_available failed");
}
@@ -103,12 +131,18 @@ ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
if (!rclcpp::ok()) {
return false;
}
node_ptr->wait_for_graph_change(event, time_to_wait);
event->check_and_clear(); // reset the event
// always check if the service is ready, even if the graph event wasn't triggered
// this is needed to avoid a race condition that is specific to the Connext RMW implementation
// Limit each wait to 100ms to workaround an issue specific to the Connext RMW implementation.
// A race condition means that graph changes for services becoming available may trigger the
// wait set to wake up, but then not be reported as ready immediately after the wake up
// (see https://github.com/ros2/rmw_connext/issues/201)
// If no other graph events occur, the wait set will not be triggered again until the timeout
// has been reached, despite the service being available, so we artificially limit the wait
// time to limit the delay.
node_ptr->wait_for_graph_change(
event, std::min(time_to_wait, std::chrono::nanoseconds(RCL_MS_TO_NS(100))));
// Because of the aforementioned race condition, we check if the service is ready even if the
// graph event wasn't triggered.
event->check_and_clear();
if (this->service_is_ready()) {
return true;
}

View File

@@ -28,27 +28,11 @@
namespace rclcpp
{
bool
JumpThreshold::is_exceeded(const TimeJump & jump)
{
if (on_clock_change_ &&
(jump.jump_type_ == TimeJump::ClockChange_t::ROS_TIME_ACTIVATED ||
jump.jump_type_ == TimeJump::ClockChange_t::ROS_TIME_DEACTIVATED))
{
return true;
}
if ((uint64_t)jump.delta_.nanoseconds > min_forward_ ||
(uint64_t)jump.delta_.nanoseconds < min_backward_)
{
return true;
}
return false;
}
JumpHandler::JumpHandler(
std::function<void()> pre_callback,
std::function<void(TimeJump)> post_callback,
JumpThreshold & threshold)
std::function<void(const rcl_time_jump_t &)> post_callback,
const rcl_jump_threshold_t & threshold)
: pre_callback(pre_callback),
post_callback(post_callback),
notice_threshold(threshold)
@@ -77,7 +61,7 @@ Clock::now()
{
Time now(0, 0, rcl_clock_.type);
auto ret = rcl_clock_get_now(&rcl_clock_, &now.rcl_time_);
auto ret = rcl_clock_get_now(&rcl_clock_, &now.rcl_time_.nanoseconds);
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(
ret, "could not get current time stamp");
@@ -103,68 +87,65 @@ Clock::ros_time_is_active()
return is_enabled;
}
rcl_clock_t *
Clock::get_clock_handle()
{
return &rcl_clock_;
}
rcl_clock_type_t
Clock::get_clock_type()
{
return rcl_clock_.type;
}
void
Clock::on_time_jump(
const struct rcl_time_jump_t * time_jump,
bool before_jump,
void * user_data)
{
rclcpp::JumpHandler * handler = static_cast<rclcpp::JumpHandler *>(user_data);
if (before_jump && handler->pre_callback) {
handler->pre_callback();
} else if (!before_jump && handler->post_callback) {
handler->post_callback(*time_jump);
}
}
rclcpp::JumpHandler::SharedPtr
Clock::create_jump_callback(
std::function<void()> pre_callback,
std::function<void(const TimeJump &)> post_callback,
JumpThreshold & threshold)
std::function<void(const rcl_time_jump_t &)> post_callback,
const rcl_jump_threshold_t & threshold)
{
// JumpHandler jump_callback;
auto jump_callback =
std::make_shared<rclcpp::JumpHandler>(pre_callback, post_callback, threshold);
{
std::lock_guard<std::mutex> guard(callback_list_mutex_);
active_jump_handlers_.push_back(jump_callback);
// Allocate a new jump handler
auto handler = new rclcpp::JumpHandler(pre_callback, post_callback, threshold);
if (nullptr == handler) {
rclcpp::exceptions::throw_from_rcl_error(RCL_RET_BAD_ALLOC, "Failed to allocate jump handler");
}
return jump_callback;
}
std::vector<JumpHandler::SharedPtr>
Clock::get_triggered_callback_handlers(const TimeJump & jump)
{
std::vector<JumpHandler::SharedPtr> callbacks;
std::lock_guard<std::mutex> guard(callback_list_mutex_);
active_jump_handlers_.erase(
std::remove_if(
active_jump_handlers_.begin(),
active_jump_handlers_.end(),
[&callbacks, &jump](const std::weak_ptr<JumpHandler> & wjcb) {
if (auto jcb = wjcb.lock()) {
if (jcb->notice_threshold.is_exceeded(jump)) {
callbacks.push_back(jcb);
}
return false;
}
// Lock failed so clear the weak pointer.
return true;
}),
active_jump_handlers_.end());
return callbacks;
}
void
Clock::invoke_prejump_callbacks(
const std::vector<JumpHandler::SharedPtr> & callbacks)
{
for (const auto cb : callbacks) {
cb->pre_callback();
// Try to add the jump callback to the clock
rcl_ret_t ret = rcl_clock_add_jump_callback(&rcl_clock_, threshold,
rclcpp::Clock::on_time_jump, handler);
if (RCL_RET_OK != ret) {
delete handler;
handler = NULL;
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to add time jump callback");
}
}
void
Clock::invoke_postjump_callbacks(
const std::vector<JumpHandler::SharedPtr> & callbacks,
const TimeJump & jump)
{
for (auto cb : callbacks) {
cb->post_callback(jump);
}
// *INDENT-OFF*
// create shared_ptr that removes the callback automatically when all copies are destructed
return rclcpp::JumpHandler::SharedPtr(handler, [this](rclcpp::JumpHandler * handler) noexcept {
rcl_ret_t ret = rcl_clock_remove_jump_callback(&rcl_clock_, rclcpp::Clock::on_time_jump,
handler);
delete handler;
handler = NULL;
if (RCL_RET_OK != ret) {
RCUTILS_LOG_ERROR("Failed to remove time jump callback");
}
});
// *INDENT-ON*
}
} // namespace rclcpp

View File

@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cmath>
#include <cstdlib>
#include <limits>
#include <utility>
@@ -87,9 +88,6 @@ Duration::operator=(const Duration & rhs)
Duration &
Duration::operator=(const builtin_interfaces::msg::Duration & duration_msg)
{
if (duration_msg.sec < 0) {
throw std::runtime_error("cannot store a negative duration point in rclcpp::Duration");
}
rcl_duration_.nanoseconds = RCL_S_TO_NS(static_cast<int64_t>(duration_msg.sec));
rcl_duration_.nanoseconds += duration_msg.nanosec;
return *this;
@@ -182,10 +180,50 @@ Duration::operator-(const rclcpp::Duration & rhs) const
rcl_duration_.nanoseconds - rhs.rcl_duration_.nanoseconds);
}
void
bounds_check_duration_scale(int64_t dns, double scale, uint64_t max)
{
auto abs_dns = static_cast<uint64_t>(std::abs(dns));
auto abs_scale = std::abs(scale);
if (abs_scale > 1.0 && abs_dns > static_cast<uint64_t>(max / abs_scale)) {
if ((dns > 0 && scale > 0) || (dns < 0 && scale < 0)) {
throw std::overflow_error("duration scaling leads to int64_t overflow");
} else {
throw std::underflow_error("duration scaling leads to int64_t underflow");
}
}
}
Duration
Duration::operator*(double scale) const
{
if (!std::isfinite(scale)) {
throw std::runtime_error("abnormal scale in rclcpp::Duration");
}
bounds_check_duration_scale(
this->rcl_duration_.nanoseconds,
scale,
std::numeric_limits<rcl_duration_value_t>::max());
return Duration(static_cast<rcl_duration_value_t>(rcl_duration_.nanoseconds * scale));
}
rcl_duration_value_t
Duration::nanoseconds() const
{
return rcl_duration_.nanoseconds;
}
Duration
Duration::max()
{
return Duration(std::numeric_limits<int32_t>::max(), 999999999);
}
double
Duration::seconds() const
{
return std::chrono::duration<double>(std::chrono::nanoseconds(rcl_duration_.nanoseconds)).count();
}
} // namespace rclcpp

View File

@@ -44,7 +44,7 @@ throw_from_rcl_error(
rcl_ret_t ret,
const std::string & prefix,
const rcl_error_state_t * error_state,
void (*reset_error)())
void (* reset_error)())
{
if (RCL_RET_OK == ret) {
throw std::invalid_argument("ret is RCL_RET_OK");
@@ -75,7 +75,7 @@ throw_from_rcl_error(
RCLErrorBase::RCLErrorBase(rcl_ret_t ret, const rcl_error_state_t * error_state)
: ret(ret), message(error_state->message), file(error_state->file), line(error_state->line_number),
formatted_message(rcl_get_error_string_safe())
formatted_message(rcl_get_error_string().str)
{}
RCLError::RCLError(

View File

@@ -28,6 +28,8 @@
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rcutils/logging_macros.h"
using rclcpp::executor::AnyExecutable;
using rclcpp::executor::Executor;
using rclcpp::executor::ExecutorArgs;
@@ -43,7 +45,7 @@ Executor::Executor(const ExecutorArgs & args)
{
throw std::runtime_error(
std::string("Failed to create interrupt guard condition in Executor constructor: ") +
rcl_get_error_string_safe());
rcl_get_error_string().str);
}
// The number of guard conditions is always at least 2: 1 for the ctrl-c guard cond,
@@ -59,12 +61,14 @@ Executor::Executor(const ExecutorArgs & args)
if (rcl_wait_set_init(
&wait_set_, 0, 2, 0, 0, 0, allocator) != RCL_RET_OK)
{
fprintf(stderr,
"[rclcpp::error] failed to create wait set: %s\n", rcl_get_error_string_safe());
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"failed to create wait set: %s", rcl_get_error_string().str);
rcl_reset_error();
if (rcl_guard_condition_fini(&interrupt_guard_condition_) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy guard condition: %s\n", rcl_get_error_string_safe());
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"failed to destroy guard condition: %s", rcl_get_error_string().str);
rcl_reset_error();
}
throw std::runtime_error("Failed to create wait set in Executor constructor");
@@ -85,14 +89,16 @@ Executor::~Executor()
// Finalize the wait set.
if (rcl_wait_set_fini(&wait_set_) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy wait set: %s\n", rcl_get_error_string_safe());
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"failed to destroy wait set: %s", rcl_get_error_string().str);
rcl_reset_error();
}
// Finalize the interrupt guard condition.
if (rcl_guard_condition_fini(&interrupt_guard_condition_) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy guard condition: %s\n", rcl_get_error_string_safe());
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"failed to destroy guard condition: %s", rcl_get_error_string().str);
rcl_reset_error();
}
// Remove and release the sigint guard condition
@@ -121,7 +127,7 @@ Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_pt
if (notify) {
// Interrupt waiting to handle new node
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(rcl_get_error_string_safe());
throw std::runtime_error(rcl_get_error_string().str);
}
}
// Add the node's notify condition to the guard condition handles
@@ -155,7 +161,7 @@ Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node
// If the node was matched and removed, interrupt waiting
if (node_removed) {
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(rcl_get_error_string_safe());
throw std::runtime_error(rcl_get_error_string().str);
}
}
}
@@ -194,15 +200,32 @@ Executor::spin_node_some(std::shared_ptr<rclcpp::Node> node)
}
void
Executor::spin_some()
Executor::spin_some(std::chrono::nanoseconds max_duration)
{
auto start = std::chrono::steady_clock::now();
auto max_duration_not_elapsed = [max_duration, start]() {
if (std::chrono::nanoseconds(0) == max_duration) {
// told to spin forever if need be
return true;
} else if (std::chrono::steady_clock::now() - start < max_duration) {
// told to spin only for some maximum amount of time
return true;
}
// spun too long
return false;
};
if (spinning.exchange(true)) {
throw std::runtime_error("spin_some() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
AnyExecutable::SharedPtr any_exec;
while ((any_exec = get_next_executable(std::chrono::milliseconds::zero())) && spinning.load()) {
execute_any_executable(any_exec);
while (spinning.load() && max_duration_not_elapsed()) {
AnyExecutable any_exec;
if (get_next_executable(any_exec, std::chrono::milliseconds::zero())) {
execute_any_executable(any_exec);
} else {
break;
}
}
}
@@ -213,8 +236,8 @@ Executor::spin_once(std::chrono::nanoseconds timeout)
throw std::runtime_error("spin_once() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
auto any_exec = get_next_executable(timeout);
if (any_exec) {
AnyExecutable any_exec;
if (get_next_executable(any_exec, timeout)) {
execute_any_executable(any_exec);
}
}
@@ -224,7 +247,7 @@ Executor::cancel()
{
spinning.store(false);
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(rcl_get_error_string_safe());
throw std::runtime_error(rcl_get_error_string().str);
}
}
@@ -238,32 +261,32 @@ Executor::set_memory_strategy(rclcpp::memory_strategy::MemoryStrategy::SharedPtr
}
void
Executor::execute_any_executable(AnyExecutable::SharedPtr any_exec)
Executor::execute_any_executable(AnyExecutable & any_exec)
{
if (!any_exec || !spinning.load()) {
if (!spinning.load()) {
return;
}
if (any_exec->timer) {
execute_timer(any_exec->timer);
if (any_exec.timer) {
execute_timer(any_exec.timer);
}
if (any_exec->subscription) {
execute_subscription(any_exec->subscription);
if (any_exec.subscription) {
execute_subscription(any_exec.subscription);
}
if (any_exec->subscription_intra_process) {
execute_intra_process_subscription(any_exec->subscription_intra_process);
if (any_exec.subscription_intra_process) {
execute_intra_process_subscription(any_exec.subscription_intra_process);
}
if (any_exec->service) {
execute_service(any_exec->service);
if (any_exec.service) {
execute_service(any_exec.service);
}
if (any_exec->client) {
execute_client(any_exec->client);
if (any_exec.client) {
execute_client(any_exec.client);
}
// Reset the callback_group, regardless of type
any_exec->callback_group->can_be_taken_from().store(true);
any_exec.callback_group->can_be_taken_from().store(true);
// Wake the wait, because it may need to be recalculated or work that
// was previously blocked is now available.
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(rcl_get_error_string_safe());
throw std::runtime_error(rcl_get_error_string().str);
}
}
@@ -271,21 +294,41 @@ void
Executor::execute_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription)
{
std::shared_ptr<void> message = subscription->create_message();
rmw_message_info_t message_info;
message_info.from_intra_process = false;
auto ret = rcl_take(subscription->get_subscription_handle(),
if (subscription->is_serialized()) {
auto serialized_msg = subscription->create_serialized_message();
auto ret = rcl_take_serialized_message(
subscription->get_subscription_handle().get(),
serialized_msg.get(), &message_info);
if (RCL_RET_OK == ret) {
auto void_serialized_msg = std::static_pointer_cast<void>(serialized_msg);
subscription->handle_message(void_serialized_msg, message_info);
} else if (RCL_RET_SUBSCRIPTION_TAKE_FAILED != ret) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"take_serialized failed for subscription on topic '%s': %s",
subscription->get_topic_name(), rcl_get_error_string().str);
rcl_reset_error();
}
subscription->return_serialized_message(serialized_msg);
} else {
std::shared_ptr<void> message = subscription->create_message();
auto ret = rcl_take(
subscription->get_subscription_handle().get(),
message.get(), &message_info);
if (ret == RCL_RET_OK) {
message_info.from_intra_process = false;
subscription->handle_message(message, message_info);
} else if (ret != RCL_RET_SUBSCRIPTION_TAKE_FAILED) {
fprintf(stderr,
"[rclcpp::error] take failed for subscription on topic '%s': %s\n",
subscription->get_topic_name(), rcl_get_error_string_safe());
rcl_reset_error();
if (RCL_RET_OK == ret) {
subscription->handle_message(message, message_info);
} else if (RCL_RET_SUBSCRIPTION_TAKE_FAILED != ret) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"could not deserialize serialized message on topic '%s': %s",
subscription->get_topic_name(), rcl_get_error_string().str);
rcl_reset_error();
}
subscription->return_message(message);
}
subscription->return_message(message);
}
void
@@ -295,7 +338,7 @@ Executor::execute_intra_process_subscription(
rcl_interfaces::msg::IntraProcessMessage ipm;
rmw_message_info_t message_info;
rcl_ret_t status = rcl_take(
subscription->get_intra_process_subscription_handle(),
subscription->get_intra_process_subscription_handle().get(),
&ipm,
&message_info);
@@ -303,9 +346,10 @@ Executor::execute_intra_process_subscription(
message_info.from_intra_process = true;
subscription->handle_intra_process_message(ipm, message_info);
} else if (status != RCL_RET_SUBSCRIPTION_TAKE_FAILED) {
fprintf(stderr,
"[rclcpp::error] take failed for intra process subscription on topic '%s': %s\n",
subscription->get_topic_name(), rcl_get_error_string_safe());
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"take failed for intra process subscription on topic '%s': %s",
subscription->get_topic_name(), rcl_get_error_string().str);
rcl_reset_error();
}
}
@@ -324,15 +368,16 @@ Executor::execute_service(
auto request_header = service->create_request_header();
std::shared_ptr<void> request = service->create_request();
rcl_ret_t status = rcl_take_request(
service->get_service_handle(),
service->get_service_handle().get(),
request_header.get(),
request.get());
if (status == RCL_RET_OK) {
service->handle_request(request_header, request);
} else if (status != RCL_RET_SERVICE_TAKE_FAILED) {
fprintf(stderr,
"[rclcpp::error] take request failed for server of service '%s': %s\n",
service->get_service_name().c_str(), rcl_get_error_string_safe());
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"take request failed for server of service '%s': %s",
service->get_service_name(), rcl_get_error_string().str);
rcl_reset_error();
}
}
@@ -344,15 +389,16 @@ Executor::execute_client(
auto request_header = client->create_request_header();
std::shared_ptr<void> response = client->create_response();
rcl_ret_t status = rcl_take_response(
client->get_client_handle(),
client->get_client_handle().get(),
request_header.get(),
response.get());
if (status == RCL_RET_OK) {
client->handle_response(request_header, response);
} else if (status != RCL_RET_CLIENT_TAKE_FAILED) {
fprintf(stderr,
"[rclcpp::error] take response failed for client of service '%s': %s\n",
client->get_service_name().c_str(), rcl_get_error_string_safe());
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"take response failed for client of service '%s': %s",
client->get_service_name(), rcl_get_error_string().str);
rcl_reset_error();
}
}
@@ -376,45 +422,18 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
)
);
}
if (rcl_wait_set_resize_subscriptions(
&wait_set_, memory_strategy_->number_of_ready_subscriptions()) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("Couldn't resize the number of subscriptions in wait set : ") +
rcl_get_error_string_safe());
// clear wait set
if (rcl_wait_set_clear(&wait_set_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear wait set");
}
if (rcl_wait_set_resize_services(
&wait_set_, memory_strategy_->number_of_ready_services()) != RCL_RET_OK)
{
rcl_ret_t ret = rcl_wait_set_resize(
&wait_set_, memory_strategy_->number_of_ready_subscriptions(),
memory_strategy_->number_of_guard_conditions(), memory_strategy_->number_of_ready_timers(),
memory_strategy_->number_of_ready_clients(), memory_strategy_->number_of_ready_services());
if (RCL_RET_OK != ret) {
throw std::runtime_error(
std::string("Couldn't resize the number of services in wait set : ") +
rcl_get_error_string_safe());
}
if (rcl_wait_set_resize_clients(
&wait_set_, memory_strategy_->number_of_ready_clients()) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("Couldn't resize the number of clients in wait set : ") +
rcl_get_error_string_safe());
}
if (rcl_wait_set_resize_guard_conditions(
&wait_set_, memory_strategy_->number_of_guard_conditions()) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("Couldn't resize the number of guard_conditions in wait set : ") +
rcl_get_error_string_safe());
}
if (rcl_wait_set_resize_timers(
&wait_set_, memory_strategy_->number_of_ready_timers()) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("Couldn't resize the number of timers in wait set : ") +
rcl_get_error_string_safe());
std::string("Couldn't resize the wait set : ") + rcl_get_error_string().str);
}
if (!memory_strategy_->add_handles_to_wait_set(&wait_set_)) {
@@ -423,7 +442,9 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
rcl_ret_t status =
rcl_wait(&wait_set_, std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count());
if (status == RCL_RET_WAIT_SET_EMPTY) {
fprintf(stderr, "Warning: empty wait set received in rcl_wait(). This should never happen.\n");
RCUTILS_LOG_WARN_NAMED(
"rclcpp",
"empty wait set received in rcl_wait(). This should never happen.");
} else if (status != RCL_RET_OK && status != RCL_RET_TIMEOUT) {
using rclcpp::exceptions::throw_from_rcl_error;
throw_from_rcl_error(status, "rcl_wait() failed");
@@ -432,21 +453,6 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
// check the null handles in the wait set and remove them from the handles in memory strategy
// for callback-based entities
memory_strategy_->remove_null_handles(&wait_set_);
if (rcl_wait_set_clear_subscriptions(&wait_set_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear subscriptions from wait set");
}
if (rcl_wait_set_clear_services(&wait_set_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear servicess from wait set");
}
if (rcl_wait_set_clear_clients(&wait_set_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear clients from wait set");
}
if (rcl_wait_set_clear_guard_conditions(&wait_set_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear guard conditions from wait set");
}
if (rcl_wait_set_clear_timers(&wait_set_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear timers from wait set");
}
}
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
@@ -495,7 +501,7 @@ Executor::get_group_by_timer(rclcpp::TimerBase::SharedPtr timer)
}
void
Executor::get_next_timer(AnyExecutable::SharedPtr any_exec)
Executor::get_next_timer(AnyExecutable & any_exec)
{
for (auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
@@ -510,8 +516,8 @@ Executor::get_next_timer(AnyExecutable::SharedPtr any_exec)
for (auto & timer_ref : group->get_timer_ptrs()) {
auto timer = timer_ref.lock();
if (timer && timer->is_ready()) {
any_exec->timer = timer;
any_exec->callback_group = group;
any_exec.timer = timer;
any_exec.callback_group = group;
node = get_node_by_group(group);
return;
}
@@ -520,67 +526,69 @@ Executor::get_next_timer(AnyExecutable::SharedPtr any_exec)
}
}
AnyExecutable::SharedPtr
Executor::get_next_ready_executable()
bool
Executor::get_next_ready_executable(AnyExecutable & any_executable)
{
auto any_exec = memory_strategy_->instantiate_next_executable();
// Check the timers to see if there are any that are ready, if so return
get_next_timer(any_exec);
if (any_exec->timer) {
return any_exec;
get_next_timer(any_executable);
if (any_executable.timer) {
return true;
}
// Check the subscriptions to see if there are any that are ready
memory_strategy_->get_next_subscription(any_exec, weak_nodes_);
if (any_exec->subscription || any_exec->subscription_intra_process) {
return any_exec;
memory_strategy_->get_next_subscription(any_executable, weak_nodes_);
if (any_executable.subscription || any_executable.subscription_intra_process) {
return true;
}
// Check the services to see if there are any that are ready
memory_strategy_->get_next_service(any_exec, weak_nodes_);
if (any_exec->service) {
return any_exec;
memory_strategy_->get_next_service(any_executable, weak_nodes_);
if (any_executable.service) {
return true;
}
// Check the clients to see if there are any that are ready
memory_strategy_->get_next_client(any_exec, weak_nodes_);
if (any_exec->client) {
return any_exec;
memory_strategy_->get_next_client(any_executable, weak_nodes_);
if (any_executable.client) {
return true;
}
// If there is no ready executable, return a null ptr
return nullptr;
return false;
}
AnyExecutable::SharedPtr
Executor::get_next_executable(std::chrono::nanoseconds timeout)
bool
Executor::get_next_executable(AnyExecutable & any_executable, std::chrono::nanoseconds timeout)
{
bool success = false;
// Check to see if there are any subscriptions or timers needing service
// TODO(wjwwood): improve run to run efficiency of this function
auto any_exec = get_next_ready_executable();
success = get_next_ready_executable(any_executable);
// If there are none
if (!any_exec) {
if (!success) {
// Wait for subscriptions or timers to work on
wait_for_work(timeout);
if (!spinning.load()) {
return nullptr;
return false;
}
// Try again
any_exec = get_next_ready_executable();
success = get_next_ready_executable(any_executable);
}
// At this point any_exec should be valid with either a valid subscription
// or a valid timer, or it should be a null shared_ptr
if (any_exec) {
if (success) {
// If it is valid, check to see if the group is mutually exclusive or
// not, then mark it accordingly
if (any_exec->callback_group && any_exec->callback_group->type() == \
callback_group::CallbackGroupType::MutuallyExclusive)
using callback_group::CallbackGroupType;
if (
any_executable.callback_group &&
any_executable.callback_group->type() == CallbackGroupType::MutuallyExclusive)
{
// It should not have been taken otherwise
assert(any_exec->callback_group->can_be_taken_from().load());
assert(any_executable.callback_group->can_be_taken_from().load());
// Set to false to indicate something is being run from this group
// This is reset to true either when the any_exec is executed or when the
// any_exec is destructued
any_exec->callback_group->can_be_taken_from().store(false);
any_executable.callback_group->can_be_taken_from().store(false);
}
}
return any_exec;
return success;
}
std::ostream &

View File

@@ -16,6 +16,7 @@
#include <chrono>
#include <functional>
#include <memory>
#include <vector>
#include "rclcpp/utilities.hpp"
@@ -23,10 +24,13 @@
using rclcpp::executors::MultiThreadedExecutor;
MultiThreadedExecutor::MultiThreadedExecutor(const rclcpp::executor::ExecutorArgs & args)
: executor::Executor(args)
MultiThreadedExecutor::MultiThreadedExecutor(
const rclcpp::executor::ExecutorArgs & args,
size_t number_of_threads,
bool yield_before_execute)
: executor::Executor(args), yield_before_execute_(yield_before_execute)
{
number_of_threads_ = std::thread::hardware_concurrency();
number_of_threads_ = number_of_threads ? number_of_threads : std::thread::hardware_concurrency();
if (number_of_threads_ == 0) {
number_of_threads_ = 1;
}
@@ -67,14 +71,36 @@ void
MultiThreadedExecutor::run(size_t)
{
while (rclcpp::ok() && spinning.load()) {
executor::AnyExecutable::SharedPtr any_exec;
executor::AnyExecutable any_exec;
{
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
if (!rclcpp::ok() || !spinning.load()) {
return;
}
any_exec = get_next_executable();
if (!get_next_executable(any_exec)) {
continue;
}
if (any_exec.timer) {
// Guard against multiple threads getting the same timer.
std::lock_guard<std::mutex> lock(scheduled_timers_mutex_);
if (scheduled_timers_.count(any_exec.timer) != 0) {
continue;
}
scheduled_timers_.insert(any_exec.timer);
}
}
if (yield_before_execute_) {
std::this_thread::yield();
}
execute_any_executable(any_exec);
if (any_exec.timer) {
std::lock_guard<std::mutex> lock(scheduled_timers_mutex_);
auto it = scheduled_timers_.find(any_exec.timer);
if (it != scheduled_timers_.end()) {
scheduled_timers_.erase(it);
}
}
}
}

View File

@@ -13,6 +13,7 @@
// limitations under the License.
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/any_executable.hpp"
#include "rclcpp/scope_exit.hpp"
using rclcpp::executors::SingleThreadedExecutor;
@@ -30,7 +31,9 @@ SingleThreadedExecutor::spin()
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
while (rclcpp::ok() && spinning.load()) {
auto any_exec = get_next_executable();
execute_any_executable(any_exec);
rclcpp::executor::AnyExecutable any_executable;
if (get_next_executable(any_executable)) {
execute_any_executable(any_executable);
}
}
}

View File

@@ -20,6 +20,7 @@
#include "rcl/validate_topic_name.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rcutils/logging_macros.h"
#include "rcutils/types/string_map.h"
#include "rmw/error_handling.h"
#include "rmw/validate_namespace.h"
@@ -50,25 +51,18 @@ rclcpp::expand_topic_or_service_name(
}
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);
});
const rcutils_error_state_t * error_state = rcl_get_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_LOG_ERROR_NAMED(
"rclcpp",
"failed to fini string_map (%d) during error handling: %s",
rcutils_ret,
rcutils_get_error_string_safe());
rcutils_get_error_string().str);
rcutils_reset_error();
}
throw_from_rcl_error(ret, "", &error_state);
throw_from_rcl_error(ret, "", error_state);
}
ret = rcl_expand_topic_name(
@@ -102,20 +96,20 @@ rclcpp::expand_topic_or_service_name(
throw_from_rcl_error(ret);
}
if (validation_result == RCL_TOPIC_NAME_VALID) {
if (validation_result != RCL_TOPIC_NAME_VALID) {
const char * validation_message =
rcl_topic_name_validation_result_string(validation_result);
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);
}
} else {
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()
@@ -133,10 +127,16 @@ rclcpp::expand_topic_or_service_name(
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 (validation_result != RMW_NODE_NAME_VALID) {
throw rclcpp::exceptions::InvalidNodeNameError(
node_name.c_str(),
rmw_node_name_validation_result_string(validation_result),
invalid_index);
} else {
throw std::runtime_error("invalid rcl node name but valid rmw node name");
}
// if invalid namespace
} else if (ret == RCL_RET_NODE_INVALID_NAMESPACE) {
rcl_reset_error(); // explicitly discard error from rcl_expand_topic_name()
@@ -154,10 +154,15 @@ rclcpp::expand_topic_or_service_name(
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);
if (validation_result != RMW_NAMESPACE_VALID) {
throw rclcpp::exceptions::InvalidNamespaceError(
namespace_.c_str(),
rmw_namespace_validation_result_string(validation_result),
invalid_index);
} else {
throw std::runtime_error("invalid rcl namespace but valid rmw namespace");
}
// something else happened
} else {
throw_from_rcl_error(ret);
@@ -179,6 +184,7 @@ rclcpp::expand_topic_or_service_name(
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(

View File

@@ -26,6 +26,8 @@
#include "rclcpp/node.hpp"
#include "rmw/impl/cpp/demangle.hpp"
#include "rcutils/logging_macros.h"
using rclcpp::exceptions::throw_from_rcl_error;
namespace rclcpp
@@ -94,13 +96,16 @@ GraphListener::run()
try {
run_loop();
} catch (const std::exception & exc) {
fprintf(stderr,
"[rclcpp] caught %s exception in GraphListener thread: %s\n",
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"caught %s exception in GraphListener thread: %s",
rmw::impl::cpp::demangle(exc).c_str(),
exc.what());
std::rethrow_exception(std::current_exception());
} catch (...) {
fprintf(stderr, "[rclcpp] unknown error in GraphListener thread\n");
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"unknown error in GraphListener thread");
std::rethrow_exception(std::current_exception());
}
}
@@ -126,13 +131,13 @@ GraphListener::run_loop()
// Resize the wait set if necessary.
if (wait_set_.size_of_guard_conditions < (node_graph_interfaces_.size() + 2)) {
ret = rcl_wait_set_resize_guard_conditions(&wait_set_, node_graph_interfaces_.size() + 2);
ret = rcl_wait_set_resize(&wait_set_, 0, node_graph_interfaces_.size() + 2, 0, 0, 0);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to resize wait set");
}
}
// Clear the wait set's guard conditions.
ret = rcl_wait_set_clear_guard_conditions(&wait_set_);
// Clear the wait set.
ret = rcl_wait_set_clear(&wait_set_);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to clear wait set");
}

View File

@@ -13,12 +13,14 @@
// limitations under the License.
#include "rclcpp/memory_strategy.hpp"
#include <memory>
using rclcpp::memory_strategy::MemoryStrategy;
rclcpp::SubscriptionBase::SharedPtr
MemoryStrategy::get_subscription_by_handle(
const rcl_subscription_t * subscriber_handle, const WeakNodeVector & weak_nodes)
std::shared_ptr<const rcl_subscription_t> subscriber_handle,
const WeakNodeVector & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
@@ -48,7 +50,7 @@ MemoryStrategy::get_subscription_by_handle(
rclcpp::ServiceBase::SharedPtr
MemoryStrategy::get_service_by_handle(
const rcl_service_t * service_handle,
std::shared_ptr<const rcl_service_t> service_handle,
const WeakNodeVector & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
@@ -74,7 +76,7 @@ MemoryStrategy::get_service_by_handle(
rclcpp::ClientBase::SharedPtr
MemoryStrategy::get_client_by_handle(
const rcl_client_t * client_handle,
std::shared_ptr<const rcl_client_t> client_handle,
const WeakNodeVector & weak_nodes)
{
for (auto & weak_node : weak_nodes) {

View File

@@ -43,23 +43,36 @@ Node::Node(
node_name,
namespace_,
rclcpp::contexts::default_context::get_global_default_context(),
use_intra_process_comms)
{},
{},
true,
use_intra_process_comms,
true)
{}
Node::Node(
const std::string & node_name,
const std::string & namespace_,
rclcpp::Context::SharedPtr context,
bool use_intra_process_comms)
: node_base_(new rclcpp::node_interfaces::NodeBase(node_name, namespace_, context)),
const std::vector<std::string> & arguments,
const std::vector<rclcpp::Parameter> & initial_parameters,
bool use_global_arguments,
bool use_intra_process_comms,
bool start_parameter_services)
: node_base_(new rclcpp::node_interfaces::NodeBase(
node_name, namespace_, context, arguments, use_global_arguments)),
node_graph_(new rclcpp::node_interfaces::NodeGraph(node_base_.get())),
node_logging_(new rclcpp::node_interfaces::NodeLogging(node_base_.get())),
node_timers_(new rclcpp::node_interfaces::NodeTimers(node_base_.get())),
node_topics_(new rclcpp::node_interfaces::NodeTopics(node_base_.get())),
node_services_(new rclcpp::node_interfaces::NodeServices(node_base_.get())),
node_parameters_(new rclcpp::node_interfaces::NodeParameters(
node_topics_.get(),
use_intra_process_comms
node_base_,
node_topics_,
node_services_,
initial_parameters,
use_intra_process_comms,
start_parameter_services
)),
node_clock_(new rclcpp::node_interfaces::NodeClock(
node_base_,
@@ -107,26 +120,26 @@ Node::group_in_node(rclcpp::callback_group::CallbackGroup::SharedPtr group)
std::vector<rcl_interfaces::msg::SetParametersResult>
Node::set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
const std::vector<rclcpp::Parameter> & parameters)
{
return node_parameters_->set_parameters(parameters);
}
rcl_interfaces::msg::SetParametersResult
Node::set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
const std::vector<rclcpp::Parameter> & parameters)
{
return node_parameters_->set_parameters_atomically(parameters);
}
std::vector<rclcpp::parameter::ParameterVariant>
std::vector<rclcpp::Parameter>
Node::get_parameters(
const std::vector<std::string> & names) const
{
return node_parameters_->get_parameters(names);
}
rclcpp::parameter::ParameterVariant
rclcpp::Parameter
Node::get_parameter(const std::string & name) const
{
return node_parameters_->get_parameter(name);
@@ -134,7 +147,7 @@ Node::get_parameter(const std::string & name) const
bool Node::get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const
rclcpp::Parameter & parameter) const
{
return node_parameters_->get_parameter(name, parameter);
}
@@ -160,6 +173,12 @@ Node::list_parameters(
return node_parameters_->list_parameters(prefixes, depth);
}
std::vector<std::string>
Node::get_node_names() const
{
return node_graph_->get_node_names();
}
std::map<std::string, std::vector<std::string>>
Node::get_topic_names_and_types() const
{

View File

@@ -19,7 +19,9 @@
#include "rclcpp/node_interfaces/node_base.hpp"
#include "rcl/arguments.h"
#include "rclcpp/exceptions.hpp"
#include "rcutils/logging_macros.h"
#include "rmw/validate_node_name.h"
#include "rmw/validate_namespace.h"
@@ -30,7 +32,9 @@ using rclcpp::node_interfaces::NodeBase;
NodeBase::NodeBase(
const std::string & node_name,
const std::string & namespace_,
rclcpp::Context::SharedPtr context)
rclcpp::Context::SharedPtr context,
const std::vector<std::string> & arguments,
bool use_global_arguments)
: context_(context),
node_handle_(nullptr),
default_callback_group_(nullptr),
@@ -48,8 +52,9 @@ NodeBase::NodeBase(
auto finalize_notify_guard_condition = [this]() {
// Finalize the interrupt guard condition.
if (rcl_guard_condition_fini(&notify_guard_condition_) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy guard condition: %s\n", rcl_get_error_string_safe());
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"failed to destroy guard condition: %s", rcl_get_error_string().str);
}
};
@@ -82,17 +87,44 @@ NodeBase::NodeBase(
}
// Create the rcl node and store it in a shared_ptr with a custom destructor.
rcl_node_t * rcl_node = new rcl_node_t(rcl_get_zero_initialized_node());
std::unique_ptr<rcl_node_t> rcl_node(new rcl_node_t(rcl_get_zero_initialized_node()));
rcl_node_options_t options = rcl_node_get_default_options();
std::unique_ptr<const char *[]> c_args;
if (!arguments.empty()) {
c_args.reset(new const char *[arguments.size()]);
for (std::size_t i = 0; i < arguments.size(); ++i) {
c_args[i] = arguments[i].c_str();
}
}
// TODO(sloretz) Pass an allocator to argument parsing
if (arguments.size() > std::numeric_limits<int>::max()) {
throw_from_rcl_error(RCL_RET_INVALID_ARGUMENT, "Too many args");
}
ret = rcl_parse_arguments(
static_cast<int>(arguments.size()), c_args.get(), rcl_get_default_allocator(),
&(options.arguments));
if (RCL_RET_OK != ret) {
finalize_notify_guard_condition();
throw_from_rcl_error(ret, "failed to parse arguments");
}
options.use_global_arguments = use_global_arguments;
// TODO(wjwwood): pass the Allocator to the options
options.domain_id = domain_id;
ret = rcl_node_init(rcl_node, node_name.c_str(), namespace_.c_str(), &options);
ret = rcl_node_init(rcl_node.get(), node_name.c_str(), namespace_.c_str(), &options);
if (ret != RCL_RET_OK) {
// Finalize the interrupt guard condition.
finalize_notify_guard_condition();
delete rcl_node;
// Finalize previously allocated node arguments
if (RCL_RET_OK != rcl_arguments_fini(&options.arguments)) {
// Print message because exception will be thrown later in this code block
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Failed to fini arguments during error handling: %s", rcl_get_error_string().str);
rcl_reset_error();
}
if (ret == RCL_RET_NODE_INVALID_NAME) {
rcl_reset_error(); // discard rcl_node_init error
@@ -106,10 +138,15 @@ NodeBase::NodeBase(
}
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 (validation_result != RMW_NODE_NAME_VALID) {
throw rclcpp::exceptions::InvalidNodeNameError(
node_name.c_str(),
rmw_node_name_validation_result_string(validation_result),
invalid_index);
} else {
throw std::runtime_error("valid rmw node name but invalid rcl node name");
}
}
if (ret == RCL_RET_NODE_INVALID_NAMESPACE) {
@@ -124,21 +161,26 @@ NodeBase::NodeBase(
}
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);
}
if (validation_result != RMW_NAMESPACE_VALID) {
throw rclcpp::exceptions::InvalidNamespaceError(
namespace_.c_str(),
rmw_namespace_validation_result_string(validation_result),
invalid_index);
} else {
throw std::runtime_error("valid rmw node namespace but invalid rcl node namespace");
}
}
throw_from_rcl_error(ret, "failed to initialize rcl node");
}
node_handle_.reset(
rcl_node,
rcl_node.release(),
[](rcl_node_t * node) -> void {
if (rcl_node_fini(node) != RCL_RET_OK) {
fprintf(
stderr, "Error in destruction of rcl node handle: %s\n", rcl_get_error_string_safe());
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Error in destruction of rcl node handle: %s", rcl_get_error_string().str);
}
delete node;
});
@@ -149,6 +191,15 @@ NodeBase::NodeBase(
// Indicate the notify_guard_condition is now valid.
notify_guard_condition_is_valid_ = true;
// Finalize previously allocated node arguments
if (RCL_RET_OK != rcl_arguments_fini(&options.arguments)) {
// print message because throwing would prevent the destructor from being called
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Failed to fini arguments: %s", rcl_get_error_string().str);
rcl_reset_error();
}
}
NodeBase::~NodeBase()
@@ -158,8 +209,9 @@ NodeBase::~NodeBase()
std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
notify_guard_condition_is_valid_ = false;
if (rcl_guard_condition_fini(&notify_guard_condition_) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy guard condition: %s\n", rcl_get_error_string_safe());
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"failed to destroy guard condition: %s", rcl_get_error_string().str);
}
}
}

View File

@@ -16,6 +16,7 @@
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "rcl/graph.h"
@@ -58,13 +59,13 @@ NodeGraph::get_topic_names_and_types(bool no_demangle) const
&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_get_error_string().str;
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();
rcl_get_error_string().str;
}
throw std::runtime_error(error_msg + rcl_get_error_string_safe());
throw std::runtime_error(error_msg + rcl_get_error_string().str);
}
std::map<std::string, std::vector<std::string>> topics_and_types;
@@ -79,7 +80,7 @@ NodeGraph::get_topic_names_and_types(bool no_demangle) const
if (ret != RCL_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not destroy topic names and types: ") + rcl_get_error_string_safe());
std::string("could not destroy topic names and types: ") + rcl_get_error_string().str);
// *INDENT-ON*
}
@@ -98,14 +99,14 @@ NodeGraph::get_service_names_and_types() const
&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_get_error_string().str;
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();
rcl_get_error_string().str;
}
throw std::runtime_error(error_msg + rcl_get_error_string_safe());
throw std::runtime_error(error_msg + rcl_get_error_string().str);
}
std::map<std::string, std::vector<std::string>> services_and_types;
@@ -120,7 +121,7 @@ NodeGraph::get_service_names_and_types() const
if (ret != RCL_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not destroy service names and types: ") + rcl_get_error_string_safe());
std::string("could not destroy service names and types: ") + rcl_get_error_string().str);
// *INDENT-ON*
}
@@ -129,36 +130,73 @@ NodeGraph::get_service_names_and_types() const
std::vector<std::string>
NodeGraph::get_node_names() const
{
std::vector<std::string> nodes;
auto names_and_namespaces = get_node_names_and_namespaces();
for (const auto & it : names_and_namespaces) {
nodes.push_back(it.first);
}
return nodes;
}
std::vector<std::pair<std::string, std::string>>
NodeGraph::get_node_names_and_namespaces() const
{
rcutils_string_array_t node_names_c =
rcutils_get_zero_initialized_string_array();
rcutils_string_array_t node_namespaces_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);
&node_names_c,
&node_namespaces_c);
if (ret != RCL_RET_OK) {
auto error_msg = std::string("failed to get node names: ") + rcl_get_error_string_safe();
auto error_msg = std::string("failed to get node names: ") + rcl_get_error_string().str;
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();
rcl_get_error_string().str;
rcl_reset_error();
}
if (rcutils_string_array_fini(&node_namespaces_c) != RCUTILS_RET_OK) {
error_msg += std::string(", failed also to cleanup node namespaces, leaking memory: ") +
rcl_get_error_string().str;
rcl_reset_error();
}
// 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) {
std::vector<std::pair<std::string, std::string>> node_names(node_names_c.size);
for (size_t i = 0; i < node_names_c.size; ++i) {
if (node_names_c.data[i] && node_namespaces_c.data[i]) {
node_names.push_back(std::make_pair(node_names_c.data[i], node_namespaces_c.data[i]));
}
}
std::string error;
rcl_ret_t ret_names = rcutils_string_array_fini(&node_names_c);
if (ret_names != 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: "));
error = "could not destroy node names";
// *INDENT-ON*
}
rcl_ret_t ret_ns = rcutils_string_array_fini(&node_namespaces_c);
if (ret_ns != RCUTILS_RET_OK) {
// *INDENT-OFF*
// TODO(karsten1987): Append rcutils_error_message once it's in master
error += ", could not destroy node namespaces";
// *INDENT-ON*
}
if (ret_names != RCUTILS_RET_OK || ret_ns != RCUTILS_RET_OK) {
throw std::runtime_error(error);
}
return node_names;
}
@@ -166,20 +204,20 @@ NodeGraph::get_node_names() const
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 rcl_node_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_,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
false); // false = not a service
size_t count;
// TODO(wjwwood): use the rcl equivalent methods
auto ret = rmw_count_publishers(rmw_node_handle, fqdn.c_str(), &count);
auto ret = rcl_count_publishers(rcl_node_handle, fqdn.c_str(), &count);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not count publishers: ") + rmw_get_error_string_safe());
std::string("could not count publishers: ") + rmw_get_error_string().str);
// *INDENT-ON*
}
return count;
@@ -188,20 +226,20 @@ 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 rcl_node_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_,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
false); // false = not a service
size_t count;
// TODO(wjwwood): use the rcl equivalent methods
auto ret = rmw_count_subscribers(rmw_node_handle, fqdn.c_str(), &count);
auto ret = rcl_count_subscribers(rcl_node_handle, fqdn.c_str(), &count);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not count subscribers: ") + rmw_get_error_string_safe());
std::string("could not count subscribers: ") + rmw_get_error_string().str);
// *INDENT-ON*
}
return count;

View File

@@ -19,8 +19,7 @@ using rclcpp::node_interfaces::NodeLogging;
NodeLogging::NodeLogging(rclcpp::node_interfaces::NodeBaseInterface * node_base)
: node_base_(node_base)
{
// TODO(dhood): use the namespace (slashes converted to dots)
logger_ = rclcpp::get_logger(node_base_->get_name());
logger_ = rclcpp::get_logger(this->get_logger_name());
}
NodeLogging::~NodeLogging()
@@ -32,3 +31,9 @@ NodeLogging::get_logger() const
{
return logger_;
}
const char *
NodeLogging::get_logger_name() const
{
return rcl_node_get_logger_name(node_base_->get_rcl_node_handle());
}

View File

@@ -14,23 +14,31 @@
#include "rclcpp/node_interfaces/node_parameters.hpp"
#include <rcl_yaml_param_parser/parser.h>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "rcl_interfaces/srv/list_parameters.hpp"
#include "rclcpp/create_publisher.hpp"
#include "rclcpp/parameter_map.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rcutils/logging_macros.h"
#include "rmw/qos_profiles.h"
using rclcpp::node_interfaces::NodeParameters;
NodeParameters::NodeParameters(
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
bool use_intra_process)
: node_topics_(node_topics)
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services,
const std::vector<rclcpp::Parameter> & initial_parameters,
bool use_intra_process,
bool start_parameter_services)
{
using MessageT = rcl_interfaces::msg::ParameterEvent;
using PublisherT = rclcpp::Publisher<MessageT>;
@@ -38,12 +46,120 @@ NodeParameters::NodeParameters(
// TODO(wjwwood): expose this allocator through the Parameter interface.
auto allocator = std::make_shared<AllocatorT>();
if (start_parameter_services) {
parameter_service_ = std::make_shared<ParameterService>(node_base, node_services, this);
}
events_publisher_ = rclcpp::create_publisher<MessageT, AllocatorT, PublisherT>(
node_topics_,
node_topics.get(),
"parameter_events",
rmw_qos_profile_parameter_events,
use_intra_process,
allocator);
// Get the node options
const rcl_node_t * node = node_base->get_rcl_node_handle();
if (nullptr == node) {
throw std::runtime_error("Need valid node handle in NodeParameters");
}
const rcl_node_options_t * options = rcl_node_get_options(node);
if (nullptr == options) {
throw std::runtime_error("Need valid node options NodeParameters");
}
// Get paths to yaml files containing initial parameter values
std::vector<std::string> yaml_paths;
auto get_yaml_paths = [&yaml_paths, &options](const rcl_arguments_t * args) {
int num_yaml_files = rcl_arguments_get_param_files_count(args);
if (num_yaml_files > 0) {
char ** param_files;
rcl_ret_t ret = rcl_arguments_get_param_files(args, options->allocator, &param_files);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
auto cleanup_param_files = make_scope_exit(
[&param_files, &num_yaml_files, &options]() {
for (int i = 0; i < num_yaml_files; ++i) {
options->allocator.deallocate(param_files[i], options->allocator.state);
}
options->allocator.deallocate(param_files, options->allocator.state);
});
for (int i = 0; i < num_yaml_files; ++i) {
yaml_paths.emplace_back(param_files[i]);
}
}
};
// global before local so that local overwrites global
if (options->use_global_arguments) {
get_yaml_paths(rcl_get_global_arguments());
}
get_yaml_paths(&(options->arguments));
// Get fully qualified node name post-remapping to use to find node's params in yaml files
const std::string node_name = node_base->get_name();
const std::string node_namespace = node_base->get_namespace();
if (0u == node_namespace.size() || 0u == node_name.size()) {
// Should never happen
throw std::runtime_error("Node name and namespace were not set");
}
std::string combined_name;
if ('/' == node_namespace.at(node_namespace.size() - 1)) {
combined_name = node_namespace + node_name;
} else {
combined_name = node_namespace + '/' + node_name;
}
std::map<std::string, rclcpp::Parameter> parameters;
// TODO(sloretz) use rcl to parse yaml when circular dependency is solved
// See https://github.com/ros2/rcl/issues/252
for (const std::string & yaml_path : yaml_paths) {
rcl_params_t * yaml_params = rcl_yaml_node_struct_init(options->allocator);
if (nullptr == yaml_params) {
throw std::bad_alloc();
}
if (!rcl_parse_yaml_file(yaml_path.c_str(), yaml_params)) {
std::ostringstream ss;
ss << "Failed to parse parameters from file '" << yaml_path << "': " <<
rcl_get_error_string().str;
rcl_reset_error();
throw std::runtime_error(ss.str());
}
rclcpp::ParameterMap initial_map = rclcpp::parameter_map_from(yaml_params);
rcl_yaml_node_struct_fini(yaml_params);
auto iter = initial_map.find(combined_name);
if (initial_map.end() == iter) {
continue;
}
// Combine parameter yaml files, overwriting values in older ones
for (auto & param : iter->second) {
parameters[param.get_name()] = param;
}
}
// initial values passed to constructor overwrite yaml file sources
for (auto & param : initial_parameters) {
parameters[param.get_name()] = param;
}
std::vector<rclcpp::Parameter> combined_values;
combined_values.reserve(parameters.size());
for (auto & kv : parameters) {
combined_values.emplace_back(kv.second);
}
// TODO(sloretz) store initial values and use them when a parameter is created ros2/rclcpp#475
// Set initial parameter values
if (!combined_values.empty()) {
rcl_interfaces::msg::SetParametersResult result = set_parameters_atomically(combined_values);
if (!result.successful) {
throw std::runtime_error("Failed to set initial parameters");
}
}
}
NodeParameters::~NodeParameters()
@@ -51,7 +167,7 @@ NodeParameters::~NodeParameters()
std::vector<rcl_interfaces::msg::SetParametersResult>
NodeParameters::set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
const std::vector<rclcpp::Parameter> & parameters)
{
std::vector<rcl_interfaces::msg::SetParametersResult> results;
for (auto p : parameters) {
@@ -63,10 +179,10 @@ NodeParameters::set_parameters(
rcl_interfaces::msg::SetParametersResult
NodeParameters::set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
const std::vector<rclcpp::Parameter> & parameters)
{
std::lock_guard<std::mutex> lock(mutex_);
std::map<std::string, rclcpp::parameter::ParameterVariant> tmp_map;
std::map<std::string, rclcpp::Parameter> tmp_map;
auto parameter_event = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
// TODO(jacquelinekay): handle parameter constraints
@@ -82,27 +198,34 @@ 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());
if (p.get_type() == rclcpp::ParameterType::PARAMETER_NOT_SET) {
if (parameters_.find(p.get_name()) != parameters_.end()) {
// case: parameter was set before, and input is "NOT_SET"
// therefore we will erase the parameter from parameters_ later
parameter_event->deleted_parameters.push_back(p.to_parameter_msg());
}
} 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());
if (parameters_.find(p.get_name()) == parameters_.end()) {
// case: parameter not set before, and input is something other than "NOT_SET"
parameter_event->new_parameters.push_back(p.to_parameter_msg());
} else {
// case: parameter was set before, and input is something other than "NOT_SET"
parameter_event->changed_parameters.push_back(p.to_parameter_msg());
}
tmp_map[p.get_name()] = p;
}
tmp_map[p.get_name()] = p;
}
// std::map::insert will not overwrite elements, so we'll keep the new
// ones and add only those that already exist in the Node's internal map
tmp_map.insert(parameters_.begin(), parameters_.end());
// remove explicitly deleted parameters
for (auto p : parameters) {
if (p.get_type() == rclcpp::ParameterType::PARAMETER_NOT_SET) {
tmp_map.erase(p.get_name());
}
}
std::swap(tmp_map, parameters_);
events_publisher_->publish(parameter_event);
@@ -110,15 +233,15 @@ NodeParameters::set_parameters_atomically(
return result;
}
std::vector<rclcpp::parameter::ParameterVariant>
std::vector<rclcpp::Parameter>
NodeParameters::get_parameters(const std::vector<std::string> & names) const
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<rclcpp::parameter::ParameterVariant> results;
std::vector<rclcpp::Parameter> results;
for (auto & name : names) {
if (std::any_of(parameters_.cbegin(), parameters_.cend(),
[&name](const std::pair<std::string, rclcpp::parameter::ParameterVariant> & kv) {
[&name](const std::pair<std::string, rclcpp::Parameter> & kv) {
return name == kv.first;
}))
{
@@ -128,10 +251,10 @@ NodeParameters::get_parameters(const std::vector<std::string> & names) const
return results;
}
rclcpp::parameter::ParameterVariant
rclcpp::Parameter
NodeParameters::get_parameter(const std::string & name) const
{
rclcpp::parameter::ParameterVariant parameter;
rclcpp::Parameter parameter;
if (get_parameter(name, parameter)) {
return parameter;
@@ -143,7 +266,7 @@ NodeParameters::get_parameter(const std::string & name) const
bool
NodeParameters::get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const
rclcpp::Parameter & parameter) const
{
std::lock_guard<std::mutex> lock(mutex_);
@@ -239,7 +362,7 @@ NodeParameters::register_param_change_callback(ParametersCallbackFunction callba
{
if (parameters_callback_) {
RCUTILS_LOG_WARN("param_change_callback already registered, "
"overwriting previous callback")
"overwriting previous callback");
}
parameters_callback_ = callback;
}

View File

@@ -45,8 +45,8 @@ NodeServices::add_service(
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify wait set on service creation: ") + rmw_get_error_string()
std::string("Failed to notify wait set on service creation: ") +
rmw_get_error_string().str
);
}
}
@@ -72,7 +72,8 @@ NodeServices::add_client(
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string("Failed to notify wait set on client creation: ") + rmw_get_error_string()
std::string("Failed to notify wait set on client creation: ") +
rmw_get_error_string().str
);
}
}

View File

@@ -41,7 +41,7 @@ NodeTimers::add_timer(
}
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify wait set on timer creation: ") + rmw_get_error_string());
std::string("Failed to notify wait set on timer creation: ") +
rmw_get_error_string().str);
}
}

View File

@@ -73,8 +73,8 @@ NodeTopics::add_publisher(
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify wait set on publisher creation: ") + rmw_get_error_string());
std::string("Failed to notify wait set on publisher creation: ") +
rmw_get_error_string().str);
}
}
}
@@ -123,8 +123,8 @@ NodeTopics::add_subscription(
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify wait set on subscription creation: ") + rmw_get_error_string()
std::string("Failed to notify wait set on subscription creation: ") +
rmw_get_error_string().str
);
}
}

View File

@@ -20,214 +20,120 @@
#include "rclcpp/parameter.hpp"
#include "rclcpp/utilities.hpp"
using rclcpp::parameter::ParameterType;
using rclcpp::parameter::ParameterVariant;
using rclcpp::ParameterType;
using rclcpp::Parameter;
ParameterVariant::ParameterVariant()
Parameter::Parameter()
: name_("")
{
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET;
}
ParameterVariant::ParameterVariant(const std::string & name, const bool bool_value)
: name_(name)
Parameter::Parameter(const std::string & name, const rclcpp::ParameterValue & value)
: name_(name), value_(value)
{
value_.bool_value = bool_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL;
}
ParameterVariant::ParameterVariant(const std::string & name, const int int_value)
: name_(name)
{
value_.integer_value = int_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER;
}
ParameterVariant::ParameterVariant(const std::string & name, const int64_t int_value)
: name_(name)
{
value_.integer_value = int_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER;
}
ParameterVariant::ParameterVariant(const std::string & name, const float double_value)
: name_(name)
{
value_.double_value = double_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
}
ParameterVariant::ParameterVariant(const std::string & name, const double double_value)
: name_(name)
{
value_.double_value = double_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
}
ParameterVariant::ParameterVariant(const std::string & name, const std::string & string_value)
: name_(name)
{
value_.string_value = string_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_STRING;
}
ParameterVariant::ParameterVariant(const std::string & name, const char * string_value)
: ParameterVariant(name, std::string(string_value))
{}
ParameterVariant::ParameterVariant(
const std::string & name, const std::vector<uint8_t> & bytes_value)
: name_(name)
{
value_.bytes_value = bytes_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_BYTES;
}
ParameterType
ParameterVariant::get_type() const
Parameter::get_type() const
{
return static_cast<ParameterType>(value_.type);
return value_.get_type();
}
std::string
ParameterVariant::get_type_name() const
Parameter::get_type_name() const
{
switch (get_type()) {
case rclcpp::parameter::ParameterType::PARAMETER_BOOL:
return "bool";
case rclcpp::parameter::ParameterType::PARAMETER_INTEGER:
return "integer";
case rclcpp::parameter::ParameterType::PARAMETER_DOUBLE:
return "double";
case rclcpp::parameter::ParameterType::PARAMETER_STRING:
return "string";
case rclcpp::parameter::ParameterType::PARAMETER_BYTES:
return "bytes";
case rclcpp::parameter::ParameterType::PARAMETER_NOT_SET:
return "not set";
default:
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
"Unexpected type from ParameterVariant: " + std::to_string(get_type()));
// *INDENT-ON*
}
return rclcpp::to_string(get_type());
}
const std::string &
ParameterVariant::get_name() const
Parameter::get_name() const
{
return name_;
}
rcl_interfaces::msg::ParameterValue
ParameterVariant::get_parameter_value() const
Parameter::get_value_message() const
{
return value_;
return value_.to_value_msg();
}
bool
Parameter::as_bool() const
{
return get_value<ParameterType::PARAMETER_BOOL>();
}
int64_t
ParameterVariant::as_int() const
Parameter::as_int() const
{
return get_value<ParameterType::PARAMETER_INTEGER>();
}
double
ParameterVariant::as_double() const
Parameter::as_double() const
{
return get_value<ParameterType::PARAMETER_DOUBLE>();
}
const std::string &
ParameterVariant::as_string() const
Parameter::as_string() const
{
return get_value<ParameterType::PARAMETER_STRING>();
}
bool
ParameterVariant::as_bool() const
{
return get_value<ParameterType::PARAMETER_BOOL>();
}
const std::vector<uint8_t> &
ParameterVariant::as_bytes() const
Parameter::as_byte_array() const
{
return get_value<ParameterType::PARAMETER_BYTES>();
return get_value<ParameterType::PARAMETER_BYTE_ARRAY>();
}
ParameterVariant
ParameterVariant::from_parameter(const rcl_interfaces::msg::Parameter & parameter)
const std::vector<bool> &
Parameter::as_bool_array() const
{
switch (parameter.value.type) {
case PARAMETER_BOOL:
return ParameterVariant(parameter.name, parameter.value.bool_value);
case PARAMETER_INTEGER:
return ParameterVariant(parameter.name, parameter.value.integer_value);
case PARAMETER_DOUBLE:
return ParameterVariant(parameter.name, parameter.value.double_value);
case PARAMETER_STRING:
return ParameterVariant(parameter.name, parameter.value.string_value);
case PARAMETER_BYTES:
return ParameterVariant(parameter.name, parameter.value.bytes_value);
case PARAMETER_NOT_SET:
throw std::runtime_error("Type from ParameterValue is not set");
default:
// TODO(wjwwood): use custom exception
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
"Unexpected type from ParameterVariant: " + std::to_string(parameter.value.type));
// *INDENT-ON*
}
return get_value<ParameterType::PARAMETER_BOOL_ARRAY>();
}
const std::vector<int64_t> &
Parameter::as_integer_array() const
{
return get_value<ParameterType::PARAMETER_INTEGER_ARRAY>();
}
const std::vector<double> &
Parameter::as_double_array() const
{
return get_value<ParameterType::PARAMETER_DOUBLE_ARRAY>();
}
const std::vector<std::string> &
Parameter::as_string_array() const
{
return get_value<ParameterType::PARAMETER_STRING_ARRAY>();
}
Parameter
Parameter::from_parameter_msg(const rcl_interfaces::msg::Parameter & parameter)
{
return Parameter(parameter.name, parameter.value);
}
rcl_interfaces::msg::Parameter
ParameterVariant::to_parameter()
Parameter::to_parameter_msg() const
{
rcl_interfaces::msg::Parameter parameter;
parameter.name = name_;
parameter.value = value_;
parameter.value = value_.to_value_msg();
return parameter;
}
std::string
ParameterVariant::value_to_string() const
Parameter::value_to_string() const
{
switch (get_type()) {
case rclcpp::parameter::ParameterType::PARAMETER_BOOL:
return as_bool() ? "true" : "false";
case rclcpp::parameter::ParameterType::PARAMETER_INTEGER:
return std::to_string(as_int());
case rclcpp::parameter::ParameterType::PARAMETER_DOUBLE:
return std::to_string(as_double());
case rclcpp::parameter::ParameterType::PARAMETER_STRING:
return as_string();
case rclcpp::parameter::ParameterType::PARAMETER_BYTES:
{
std::stringstream bytes;
bool first_byte = true;
bytes << "[" << std::hex;
for (auto & byte : as_bytes()) {
bytes << "0x" << byte;
if (!first_byte) {
bytes << ", ";
} else {
first_byte = false;
}
}
return bytes.str();
}
case rclcpp::parameter::ParameterType::PARAMETER_NOT_SET:
return "not set";
default:
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
"Unexpected type from ParameterVariant: " + std::to_string(get_type()));
// *INDENT-ON*
}
return rclcpp::to_string(value_);
}
std::string
rclcpp::parameter::_to_json_dict_entry(const ParameterVariant & param)
rclcpp::_to_json_dict_entry(const Parameter & param)
{
std::stringstream ss;
ss << "\"" << param.get_name() << "\": ";
@@ -237,21 +143,21 @@ rclcpp::parameter::_to_json_dict_entry(const ParameterVariant & param)
}
std::ostream &
rclcpp::parameter::operator<<(std::ostream & os, const rclcpp::parameter::ParameterVariant & pv)
rclcpp::operator<<(std::ostream & os, const rclcpp::Parameter & pv)
{
os << std::to_string(pv);
return os;
}
std::ostream &
rclcpp::parameter::operator<<(std::ostream & os, const std::vector<ParameterVariant> & parameters)
rclcpp::operator<<(std::ostream & os, const std::vector<Parameter> & parameters)
{
os << std::to_string(parameters);
return os;
}
std::string
std::to_string(const rclcpp::parameter::ParameterVariant & param)
std::to_string(const rclcpp::Parameter & param)
{
std::stringstream ss;
ss << "{\"name\": \"" << param.get_name() << "\", ";
@@ -261,7 +167,7 @@ std::to_string(const rclcpp::parameter::ParameterVariant & param)
}
std::string
std::to_string(const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
std::to_string(const std::vector<rclcpp::Parameter> & parameters)
{
std::stringstream ss;
ss << "{";
@@ -272,7 +178,7 @@ std::to_string(const std::vector<rclcpp::parameter::ParameterVariant> & paramete
} else {
first = false;
}
ss << rclcpp::parameter::_to_json_dict_entry(pv);
ss << rclcpp::_to_json_dict_entry(pv);
}
ss << "}";
return ss.str();

View File

@@ -70,6 +70,15 @@ AsyncParametersClient::AsyncParametersClient(
auto set_parameters_base = std::dynamic_pointer_cast<ClientBase>(set_parameters_client_);
node_services_interface->add_client(set_parameters_base, nullptr);
set_parameters_atomically_client_ =
Client<rcl_interfaces::srv::SetParametersAtomically>::make_shared(node_base_interface.get(),
node_graph_interface,
remote_node_name_ + "/" + parameter_service_names::set_parameters_atomically,
options);
auto set_parameters_atomically_base = std::dynamic_pointer_cast<ClientBase>(
set_parameters_atomically_client_);
node_services_interface->add_client(set_parameters_atomically_base, nullptr);
list_parameters_client_ = Client<rcl_interfaces::srv::ListParameters>::make_shared(
node_base_interface.get(),
node_graph_interface,
@@ -114,15 +123,15 @@ AsyncParametersClient::AsyncParametersClient(
qos_profile)
{}
std::shared_future<std::vector<rclcpp::parameter::ParameterVariant>>
std::shared_future<std::vector<rclcpp::Parameter>>
AsyncParametersClient::get_parameters(
const std::vector<std::string> & names,
std::function<
void(std::shared_future<std::vector<rclcpp::parameter::ParameterVariant>>)
void(std::shared_future<std::vector<rclcpp::Parameter>>)
> callback)
{
auto promise_result =
std::make_shared<std::promise<std::vector<rclcpp::parameter::ParameterVariant>>>();
std::make_shared<std::promise<std::vector<rclcpp::Parameter>>>();
auto future_result = promise_result->get_future().share();
auto request = std::make_shared<rcl_interfaces::srv::GetParameters::Request>();
@@ -133,7 +142,7 @@ AsyncParametersClient::get_parameters(
[request, promise_result, future_result, callback](
rclcpp::Client<rcl_interfaces::srv::GetParameters>::SharedFuture cb_f)
{
std::vector<rclcpp::parameter::ParameterVariant> parameter_variants;
std::vector<rclcpp::Parameter> parameters;
auto & pvalues = cb_f.get()->values;
for (auto & pvalue : pvalues) {
@@ -141,11 +150,11 @@ AsyncParametersClient::get_parameters(
rcl_interfaces::msg::Parameter parameter;
parameter.name = request->names[i];
parameter.value = pvalue;
parameter_variants.push_back(rclcpp::parameter::ParameterVariant::from_parameter(
parameters.push_back(rclcpp::Parameter::from_parameter_msg(
parameter));
}
promise_result->set_value(parameter_variants);
promise_result->set_value(parameters);
if (callback != nullptr) {
callback(future_result);
}
@@ -155,15 +164,15 @@ AsyncParametersClient::get_parameters(
return future_result;
}
std::shared_future<std::vector<rclcpp::parameter::ParameterType>>
std::shared_future<std::vector<rclcpp::ParameterType>>
AsyncParametersClient::get_parameter_types(
const std::vector<std::string> & names,
std::function<
void(std::shared_future<std::vector<rclcpp::parameter::ParameterType>>)
void(std::shared_future<std::vector<rclcpp::ParameterType>>)
> callback)
{
auto promise_result =
std::make_shared<std::promise<std::vector<rclcpp::parameter::ParameterType>>>();
std::make_shared<std::promise<std::vector<rclcpp::ParameterType>>>();
auto future_result = promise_result->get_future().share();
auto request = std::make_shared<rcl_interfaces::srv::GetParameterTypes::Request>();
@@ -174,10 +183,10 @@ AsyncParametersClient::get_parameter_types(
[promise_result, future_result, callback](
rclcpp::Client<rcl_interfaces::srv::GetParameterTypes>::SharedFuture cb_f)
{
std::vector<rclcpp::parameter::ParameterType> types;
std::vector<rclcpp::ParameterType> types;
auto & pts = cb_f.get()->types;
for (auto & pt : pts) {
pts.push_back(static_cast<rclcpp::parameter::ParameterType>(pt));
pts.push_back(static_cast<rclcpp::ParameterType>(pt));
}
promise_result->set_value(types);
if (callback != nullptr) {
@@ -191,7 +200,7 @@ AsyncParametersClient::get_parameter_types(
std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
AsyncParametersClient::set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters,
const std::vector<rclcpp::Parameter> & parameters,
std::function<
void(std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>)
> callback)
@@ -203,8 +212,8 @@ AsyncParametersClient::set_parameters(
auto request = std::make_shared<rcl_interfaces::srv::SetParameters::Request>();
std::transform(parameters.begin(), parameters.end(), std::back_inserter(request->parameters),
[](rclcpp::parameter::ParameterVariant p) {
return p.to_parameter();
[](rclcpp::Parameter p) {
return p.to_parameter_msg();
}
);
@@ -225,7 +234,7 @@ AsyncParametersClient::set_parameters(
std::shared_future<rcl_interfaces::msg::SetParametersResult>
AsyncParametersClient::set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters,
const std::vector<rclcpp::Parameter> & parameters,
std::function<
void(std::shared_future<rcl_interfaces::msg::SetParametersResult>)
> callback)
@@ -237,8 +246,8 @@ AsyncParametersClient::set_parameters_atomically(
auto request = std::make_shared<rcl_interfaces::srv::SetParametersAtomically::Request>();
std::transform(parameters.begin(), parameters.end(), std::back_inserter(request->parameters),
[](rclcpp::parameter::ParameterVariant p) {
return p.to_parameter();
[](rclcpp::Parameter p) {
return p.to_parameter_msg();
}
);
@@ -347,7 +356,7 @@ SyncParametersClient::SyncParametersClient(
std::make_shared<AsyncParametersClient>(node, remote_node_name, qos_profile);
}
std::vector<rclcpp::parameter::ParameterVariant>
std::vector<rclcpp::Parameter>
SyncParametersClient::get_parameters(const std::vector<std::string> & parameter_names)
{
auto f = async_parameters_client_->get_parameters(parameter_names);
@@ -358,7 +367,7 @@ SyncParametersClient::get_parameters(const std::vector<std::string> & parameter_
return f.get();
}
// Return an empty vector if unsuccessful
return std::vector<rclcpp::parameter::ParameterVariant>();
return std::vector<rclcpp::Parameter>();
}
bool
@@ -370,7 +379,7 @@ SyncParametersClient::has_parameter(const std::string & parameter_name)
return vars.names.size() > 0;
}
std::vector<rclcpp::parameter::ParameterType>
std::vector<rclcpp::ParameterType>
SyncParametersClient::get_parameter_types(const std::vector<std::string> & parameter_names)
{
auto f = async_parameters_client_->get_parameter_types(parameter_names);
@@ -381,17 +390,18 @@ SyncParametersClient::get_parameter_types(const std::vector<std::string> & param
{
return f.get();
}
return std::vector<rclcpp::parameter::ParameterType>();
return std::vector<rclcpp::ParameterType>();
}
std::vector<rcl_interfaces::msg::SetParametersResult>
SyncParametersClient::set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
const std::vector<rclcpp::Parameter> & parameters)
{
auto f = async_parameters_client_->set_parameters(parameters);
auto node_base_interface = node_->get_node_base_interface();
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
if (spin_node_until_future_complete(*executor_, node_base_interface, f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();
@@ -401,7 +411,7 @@ SyncParametersClient::set_parameters(
rcl_interfaces::msg::SetParametersResult
SyncParametersClient::set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
const std::vector<rclcpp::Parameter> & parameters)
{
auto f = async_parameters_client_->set_parameters_atomically(parameters);

View File

@@ -0,0 +1,128 @@
// Copyright 2018 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include <vector>
#include "rclcpp/parameter_map.hpp"
using rclcpp::exceptions::InvalidParametersException;
using rclcpp::exceptions::InvalidParameterValueException;
using rclcpp::ParameterMap;
using rclcpp::ParameterValue;
ParameterMap
rclcpp::parameter_map_from(const rcl_params_t * const c_params)
{
if (NULL == c_params) {
throw InvalidParametersException("parameters struct is NULL");
} else if (NULL == c_params->node_names) {
throw InvalidParametersException("node names array is NULL");
} else if (NULL == c_params->params) {
throw InvalidParametersException("node params array is NULL");
}
// Convert c structs into a list of parameters to set
ParameterMap parameters;
for (size_t n = 0; n < c_params->num_nodes; ++n) {
const char * c_node_name = c_params->node_names[n];
if (NULL == c_node_name) {
throw InvalidParametersException("Node name at index " + std::to_string(n) + " is NULL");
}
/// make sure there is a leading slash on the fully qualified node name
std::string node_name("/");
if ('/' != c_node_name[0]) {
node_name += c_node_name;
} else {
node_name = c_node_name;
}
const rcl_node_params_t * const c_params_node = &(c_params->params[n]);
std::vector<Parameter> & params_node = parameters[node_name];
params_node.reserve(c_params_node->num_params);
for (size_t p = 0; p < c_params_node->num_params; ++p) {
const char * const c_param_name = c_params_node->parameter_names[p];
if (NULL == c_param_name) {
std::string message(
"At node " + std::to_string(n) + " parameter " + std::to_string(p) + " name is NULL");
throw InvalidParametersException(message);
}
const rcl_variant_t * const c_param_value = &(c_params_node->parameter_values[p]);
params_node.emplace_back(c_param_name, parameter_value_from(c_param_value));
}
}
return parameters;
}
ParameterValue
rclcpp::parameter_value_from(const rcl_variant_t * const c_param_value)
{
if (NULL == c_param_value) {
throw InvalidParameterValueException("Passed argument is NULL");
}
if (c_param_value->bool_value) {
return ParameterValue(*(c_param_value->bool_value));
} else if (c_param_value->integer_value) {
return ParameterValue(*(c_param_value->integer_value));
} else if (c_param_value->double_value) {
return ParameterValue(*(c_param_value->double_value));
} else if (c_param_value->string_value) {
return ParameterValue(std::string(c_param_value->string_value));
} else if (c_param_value->byte_array_value) {
const rcl_byte_array_t * const byte_array = c_param_value->byte_array_value;
std::vector<uint8_t> bytes;
bytes.reserve(byte_array->size);
for (size_t v = 0; v < byte_array->size; ++v) {
bytes.push_back(byte_array->values[v]);
}
return ParameterValue(bytes);
} else if (c_param_value->bool_array_value) {
const rcl_bool_array_t * const bool_array = c_param_value->bool_array_value;
std::vector<bool> bools;
bools.reserve(bool_array->size);
for (size_t v = 0; v < bool_array->size; ++v) {
bools.push_back(bool_array->values[v]);
}
return ParameterValue(bools);
} else if (c_param_value->integer_array_value) {
const rcl_int64_array_t * const int_array = c_param_value->integer_array_value;
std::vector<int64_t> integers;
integers.reserve(int_array->size);
for (size_t v = 0; v < int_array->size; ++v) {
integers.push_back(int_array->values[v]);
}
return ParameterValue(integers);
} else if (c_param_value->double_array_value) {
const rcl_double_array_t * const double_array = c_param_value->double_array_value;
std::vector<double> doubles;
doubles.reserve(double_array->size);
for (size_t v = 0; v < double_array->size; ++v) {
doubles.push_back(double_array->values[v]);
}
return ParameterValue(doubles);
} else if (c_param_value->string_array_value) {
const rcutils_string_array_t * const string_array = c_param_value->string_array_value;
std::vector<std::string> strings;
strings.reserve(string_array->size);
for (size_t v = 0; v < string_array->size; ++v) {
strings.emplace_back(string_array->data[v]);
}
return ParameterValue(strings);
}
throw InvalidParameterValueException("No parameter value set");
}

View File

@@ -24,120 +24,106 @@
using rclcpp::ParameterService;
ParameterService::ParameterService(
const rclcpp::Node::SharedPtr node,
const std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> node_base,
const std::shared_ptr<rclcpp::node_interfaces::NodeServicesInterface> node_services,
rclcpp::node_interfaces::NodeParametersInterface * node_params,
const rmw_qos_profile_t & qos_profile)
: node_(node)
{
std::weak_ptr<rclcpp::Node> captured_node = node_;
get_parameters_service_ = node_->create_service<rcl_interfaces::srv::GetParameters>(
std::string(node_->get_name()) + "/" + parameter_service_names::get_parameters,
[captured_node](
const std::string node_name = node_base->get_name();
get_parameters_service_ = create_service<rcl_interfaces::srv::GetParameters>(
node_base, node_services,
node_name + "/" + parameter_service_names::get_parameters,
[node_params](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::GetParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::GetParameters::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
auto values = node->get_parameters(request->names);
for (auto & pvariant : values) {
response->values.push_back(pvariant.get_parameter_value());
for (const auto & name : request->names) {
// Default construct param to NOT_SET
rclcpp::Parameter param;
node_params->get_parameter(name, param);
// push back NOT_SET when get_parameter() call fails
response->values.push_back(param.get_value_message());
}
},
qos_profile);
qos_profile, nullptr);
get_parameter_types_service_ = node_->create_service<rcl_interfaces::srv::GetParameterTypes>(
std::string(node_->get_name()) + "/" + parameter_service_names::get_parameter_types,
[captured_node](
get_parameter_types_service_ = create_service<rcl_interfaces::srv::GetParameterTypes>(
node_base, node_services,
node_name + "/" + parameter_service_names::get_parameter_types,
[node_params](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::GetParameterTypes::Request> request,
std::shared_ptr<rcl_interfaces::srv::GetParameterTypes::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
auto types = node->get_parameter_types(request->names);
auto types = node_params->get_parameter_types(request->names);
std::transform(types.cbegin(), types.cend(),
std::back_inserter(response->types), [](const uint8_t & type) {
return static_cast<rclcpp::parameter::ParameterType>(type);
return static_cast<rclcpp::ParameterType>(type);
});
},
qos_profile);
qos_profile, nullptr);
set_parameters_service_ = node_->create_service<rcl_interfaces::srv::SetParameters>(
std::string(node_->get_name()) + "/" + parameter_service_names::set_parameters,
[captured_node](
set_parameters_service_ = create_service<rcl_interfaces::srv::SetParameters>(
node_base, node_services,
node_name + "/" + parameter_service_names::set_parameters,
[node_params](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::SetParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::SetParameters::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
std::vector<rclcpp::parameter::ParameterVariant> pvariants;
std::vector<rclcpp::Parameter> pvariants;
for (auto & p : request->parameters) {
pvariants.push_back(rclcpp::parameter::ParameterVariant::from_parameter(p));
pvariants.push_back(rclcpp::Parameter::from_parameter_msg(p));
}
auto results = node->set_parameters(pvariants);
auto results = node_params->set_parameters(pvariants);
response->results = results;
},
qos_profile);
qos_profile, nullptr);
set_parameters_atomically_service_ =
node_->create_service<rcl_interfaces::srv::SetParametersAtomically>(
std::string(node_->get_name()) + "/" + parameter_service_names::set_parameters_atomically,
[captured_node](
set_parameters_atomically_service_ = create_service<rcl_interfaces::srv::SetParametersAtomically>(
node_base, node_services,
node_name + "/" + parameter_service_names::set_parameters_atomically,
[node_params](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::SetParametersAtomically::Request> request,
std::shared_ptr<rcl_interfaces::srv::SetParametersAtomically::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
std::vector<rclcpp::parameter::ParameterVariant> pvariants;
std::vector<rclcpp::Parameter> pvariants;
std::transform(request->parameters.cbegin(), request->parameters.cend(),
std::back_inserter(pvariants),
[](const rcl_interfaces::msg::Parameter & p) {
return rclcpp::parameter::ParameterVariant::from_parameter(p);
return rclcpp::Parameter::from_parameter_msg(p);
});
auto result = node->set_parameters_atomically(pvariants);
auto result = node_params->set_parameters_atomically(pvariants);
response->result = result;
},
qos_profile);
qos_profile, nullptr);
describe_parameters_service_ = node_->create_service<rcl_interfaces::srv::DescribeParameters>(
std::string(node_->get_name()) + "/" + parameter_service_names::describe_parameters,
[captured_node](
describe_parameters_service_ = create_service<rcl_interfaces::srv::DescribeParameters>(
node_base, node_services,
node_name + "/" + parameter_service_names::describe_parameters,
[node_params](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::DescribeParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::DescribeParameters::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
auto descriptors = node->describe_parameters(request->names);
auto descriptors = node_params->describe_parameters(request->names);
response->descriptors = descriptors;
},
qos_profile);
qos_profile, nullptr);
list_parameters_service_ = node_->create_service<rcl_interfaces::srv::ListParameters>(
std::string(node_->get_name()) + "/" + parameter_service_names::list_parameters,
[captured_node](
list_parameters_service_ = create_service<rcl_interfaces::srv::ListParameters>(
node_base, node_services,
node_name + "/" + parameter_service_names::list_parameters,
[node_params](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::ListParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::ListParameters::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
auto result = node->list_parameters(request->prefixes, request->depth);
auto result = node_params->list_parameters(request->prefixes, request->depth);
response->result = result;
},
qos_profile);
qos_profile, nullptr);
}

View File

@@ -0,0 +1,229 @@
// Copyright 2018 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/parameter_value.hpp"
#include <string>
#include <vector>
using rclcpp::ParameterType;
using rclcpp::ParameterValue;
std::string
rclcpp::to_string(const ParameterType type)
{
switch (type) {
case ParameterType::PARAMETER_NOT_SET:
return "not set";
case ParameterType::PARAMETER_BOOL:
return "bool";
case ParameterType::PARAMETER_INTEGER:
return "integer";
case ParameterType::PARAMETER_DOUBLE:
return "double";
case ParameterType::PARAMETER_STRING:
return "string";
case ParameterType::PARAMETER_BYTE_ARRAY:
return "byte_array";
case ParameterType::PARAMETER_BOOL_ARRAY:
return "bool_array";
case ParameterType::PARAMETER_INTEGER_ARRAY:
return "integer_array";
case ParameterType::PARAMETER_DOUBLE_ARRAY:
return "double_array";
case ParameterType::PARAMETER_STRING_ARRAY:
return "string_array";
default:
return "unknown type";
}
}
std::ostream &
rclcpp::operator<<(std::ostream & os, const ParameterType type)
{
os << rclcpp::to_string(type);
return os;
}
template<typename ValType, typename PrintType = ValType>
std::string
array_to_string(
const std::vector<ValType> & array,
const std::ios::fmtflags format_flags = std::ios::dec)
{
std::stringstream type_array;
bool first_item = true;
type_array << "[";
type_array.setf(format_flags, std::ios_base::basefield | std::ios::boolalpha);
type_array << std::showbase;
for (const ValType value : array) {
if (!first_item) {
type_array << ", ";
} else {
first_item = false;
}
type_array << static_cast<PrintType>(value);
}
type_array << "]";
return type_array.str();
}
std::string
rclcpp::to_string(const ParameterValue & value)
{
switch (value.get_type()) {
case ParameterType::PARAMETER_NOT_SET:
return "not set";
case ParameterType::PARAMETER_BOOL:
return value.get<bool>() ? "true" : "false";
case ParameterType::PARAMETER_INTEGER:
return std::to_string(value.get<int>());
case ParameterType::PARAMETER_DOUBLE:
return std::to_string(value.get<double>());
case ParameterType::PARAMETER_STRING:
return value.get<std::string>();
case ParameterType::PARAMETER_BYTE_ARRAY:
return array_to_string<uint8_t, int>(value.get<std::vector<uint8_t>>(), std::ios::hex);
case ParameterType::PARAMETER_BOOL_ARRAY:
return array_to_string(value.get<std::vector<bool>>(), std::ios::boolalpha);
case ParameterType::PARAMETER_INTEGER_ARRAY:
return array_to_string(value.get<std::vector<int64_t>>());
case ParameterType::PARAMETER_DOUBLE_ARRAY:
return array_to_string(value.get<std::vector<double>>());
case ParameterType::PARAMETER_STRING_ARRAY:
return array_to_string(value.get<std::vector<std::string>>());
default:
return "unknown type";
}
}
ParameterValue::ParameterValue()
{
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET;
}
ParameterValue::ParameterValue(const rcl_interfaces::msg::ParameterValue & value)
{
value_ = value;
switch (value.type) {
case PARAMETER_BOOL:
case PARAMETER_INTEGER:
case PARAMETER_DOUBLE:
case PARAMETER_STRING:
case PARAMETER_BYTE_ARRAY:
case PARAMETER_BOOL_ARRAY:
case PARAMETER_INTEGER_ARRAY:
case PARAMETER_DOUBLE_ARRAY:
case PARAMETER_STRING_ARRAY:
case PARAMETER_NOT_SET:
break;
default:
// TODO(wjwwood): use custom exception
throw std::runtime_error("Unknown type: " + std::to_string(value.type));
}
}
ParameterValue::ParameterValue(const bool bool_value)
{
value_.bool_value = bool_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL;
}
ParameterValue::ParameterValue(const int int_value)
{
value_.integer_value = int_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER;
}
ParameterValue::ParameterValue(const int64_t int_value)
{
value_.integer_value = int_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER;
}
ParameterValue::ParameterValue(const float double_value)
{
value_.double_value = double_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
}
ParameterValue::ParameterValue(const double double_value)
{
value_.double_value = double_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
}
ParameterValue::ParameterValue(const std::string & string_value)
{
value_.string_value = string_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_STRING;
}
ParameterValue::ParameterValue(const char * string_value)
: ParameterValue(std::string(string_value))
{}
ParameterValue::ParameterValue(const std::vector<uint8_t> & byte_array_value)
{
value_.byte_array_value = byte_array_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_BYTE_ARRAY;
}
ParameterValue::ParameterValue(const std::vector<bool> & bool_array_value)
{
value_.bool_array_value = bool_array_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL_ARRAY;
}
ParameterValue::ParameterValue(const std::vector<int> & int_array_value)
{
value_.integer_array_value.assign(int_array_value.cbegin(), int_array_value.cend());
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY;
}
ParameterValue::ParameterValue(const std::vector<int64_t> & int_array_value)
{
value_.integer_array_value = int_array_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY;
}
ParameterValue::ParameterValue(const std::vector<float> & float_array_value)
{
value_.double_array_value.assign(float_array_value.cbegin(), float_array_value.cend());
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY;
}
ParameterValue::ParameterValue(const std::vector<double> & double_array_value)
{
value_.double_array_value = double_array_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY;
}
ParameterValue::ParameterValue(const std::vector<std::string> & string_array_value)
{
value_.string_array_value = string_array_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_STRING_ARRAY;
}
ParameterType
ParameterValue::get_type() const
{
return static_cast<ParameterType>(value_.type);
}
rcl_interfaces::msg::ParameterValue
ParameterValue::to_value_msg() const
{
return value_;
}

View File

@@ -24,6 +24,7 @@
#include <string>
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rcutils/logging_macros.h"
#include "rmw/impl/cpp/demangle.hpp"
#include "rclcpp/allocator/allocator_common.hpp"
@@ -65,12 +66,12 @@ PublisherBase::PublisherBase(
// 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) {
auto msg = 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().str;
rcl_reset_error();
throw std::runtime_error(msg);
}
if (rmw_get_gid_for_publisher(publisher_rmw_handle, &rmw_gid_) != RMW_RET_OK) {
auto msg = std::string("failed to get publisher gid: ") + rmw_get_error_string_safe();
auto msg = std::string("failed to get publisher gid: ") + rmw_get_error_string().str;
rmw_reset_error();
throw std::runtime_error(msg);
}
@@ -79,18 +80,18 @@ PublisherBase::PublisherBase(
PublisherBase::~PublisherBase()
{
if (rcl_publisher_fini(&intra_process_publisher_handle_, rcl_node_handle_.get()) != RCL_RET_OK) {
fprintf(
stderr,
"Error in destruction of intra process rcl publisher handle: %s\n",
rcl_get_error_string_safe());
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Error in destruction of intra process rcl publisher handle: %s",
rcl_get_error_string().str);
rcl_reset_error();
}
if (rcl_publisher_fini(&publisher_handle_, rcl_node_handle_.get()) != RCL_RET_OK) {
fprintf(
stderr,
"Error in destruction of rcl publisher handle: %s\n",
rcl_get_error_string_safe());
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Error in destruction of rcl publisher handle: %s",
rcl_get_error_string().str);
rcl_reset_error();
}
}
@@ -106,7 +107,7 @@ PublisherBase::get_queue_size() const
{
const rcl_publisher_options_t * publisher_options = rcl_publisher_get_options(&publisher_handle_);
if (!publisher_options) {
auto msg = 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().str;
rcl_reset_error();
throw std::runtime_error(msg);
}
@@ -149,14 +150,14 @@ 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) {
auto msg = std::string("failed to compare gids: ") + rmw_get_error_string_safe();
auto msg = std::string("failed to compare gids: ") + rmw_get_error_string().str;
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) {
auto msg = std::string("failed to compare gids: ") + rmw_get_error_string_safe();
auto msg = std::string("failed to compare gids: ") + rmw_get_error_string().str;
rmw_reset_error();
throw std::runtime_error(msg);
}
@@ -170,7 +171,13 @@ 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";
const char * topic_name = this->get_topic_name();
if (!topic_name) {
throw std::runtime_error("failed to get topic name");
}
auto intra_process_topic_name = std::string(topic_name) + "/_intra";
rcl_ret_t ret = rcl_publisher_init(
&intra_process_publisher_handle_,
rcl_node_handle_.get(),
@@ -197,7 +204,7 @@ PublisherBase::setup_intra_process(
rmw_publisher_t * publisher_rmw_handle = rcl_publisher_get_rmw_handle(
&intra_process_publisher_handle_);
if (publisher_rmw_handle == nullptr) {
auto msg = 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().str;
rcl_reset_error();
throw std::runtime_error(msg);
}
@@ -205,7 +212,7 @@ PublisherBase::setup_intra_process(
publisher_rmw_handle, &intra_process_rmw_gid_);
if (rmw_ret != RMW_RET_OK) {
auto msg =
std::string("failed to create intra process publisher gid: ") + rmw_get_error_string_safe();
std::string("failed to create intra process publisher gid: ") + rmw_get_error_string().str;
rmw_reset_error();
throw std::runtime_error(msg);
}

View File

@@ -27,12 +27,6 @@
using rclcpp::ServiceBase;
ServiceBase::ServiceBase(
std::shared_ptr<rcl_node_t> node_handle,
const std::string & service_name)
: node_handle_(node_handle), service_name_(service_name)
{}
ServiceBase::ServiceBase(std::shared_ptr<rcl_node_t> node_handle)
: node_handle_(node_handle)
{}
@@ -40,19 +34,19 @@ ServiceBase::ServiceBase(std::shared_ptr<rcl_node_t> node_handle)
ServiceBase::~ServiceBase()
{}
std::string
const char *
ServiceBase::get_service_name()
{
return this->service_name_;
return rcl_service_get_service_name(this->get_service_handle().get());
}
rcl_service_t *
std::shared_ptr<rcl_service_t>
ServiceBase::get_service_handle()
{
return service_handle_;
}
const rcl_service_t *
std::shared_ptr<const rcl_service_t>
ServiceBase::get_service_handle() const
{
return service_handle_;

View File

@@ -20,22 +20,45 @@
#include "rclcpp/exceptions.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rclcpp/logging.hpp"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
using rclcpp::SubscriptionBase;
SubscriptionBase::SubscriptionBase(
std::shared_ptr<rcl_node_t> node_handle,
const rosidl_message_type_support_t & type_support_handle,
const std::string & topic_name,
const rcl_subscription_options_t & subscription_options)
: node_handle_(node_handle)
const rcl_subscription_options_t & subscription_options,
bool is_serialized)
: node_handle_(node_handle),
type_support_(type_support_handle),
is_serialized_(is_serialized)
{
auto custom_deletor = [node_handle](rcl_subscription_t * rcl_subs)
{
if (rcl_subscription_fini(rcl_subs, node_handle.get()) != RCL_RET_OK) {
RCLCPP_ERROR(
rclcpp::get_logger(rcl_node_get_logger_name(node_handle.get())).get_child("rclcpp"),
"Error in destruction of rcl subscription handle: %s",
rcl_get_error_string().str);
rcl_reset_error();
}
delete rcl_subs;
};
subscription_handle_ = std::shared_ptr<rcl_subscription_t>(
new rcl_subscription_t, custom_deletor);
*subscription_handle_.get() = rcl_get_zero_initialized_subscription();
intra_process_subscription_handle_ = std::shared_ptr<rcl_subscription_t>(
new rcl_subscription_t, custom_deletor);
*intra_process_subscription_handle_.get() = rcl_get_zero_initialized_subscription();
rcl_ret_t ret = rcl_subscription_init(
&subscription_handle_,
subscription_handle_.get(),
node_handle_.get(),
&type_support_handle,
topic_name.c_str(),
@@ -57,42 +80,40 @@ SubscriptionBase::SubscriptionBase(
SubscriptionBase::~SubscriptionBase()
{
if (rcl_subscription_fini(&subscription_handle_, node_handle_.get()) != RCL_RET_OK) {
std::stringstream ss;
ss << "Error in destruction of rcl subscription handle: " <<
rcl_get_error_string_safe() << '\n';
(std::cerr << ss.str()).flush();
}
if (rcl_subscription_fini(
&intra_process_subscription_handle_, node_handle_.get()) != RCL_RET_OK)
{
std::stringstream ss;
ss << "Error in destruction of rmw intra process subscription handle: " <<
rcl_get_error_string_safe() << '\n';
(std::cerr << ss.str()).flush();
}
}
const char *
SubscriptionBase::get_topic_name() const
{
return rcl_subscription_get_topic_name(&subscription_handle_);
return rcl_subscription_get_topic_name(subscription_handle_.get());
}
rcl_subscription_t *
std::shared_ptr<rcl_subscription_t>
SubscriptionBase::get_subscription_handle()
{
return &subscription_handle_;
return subscription_handle_;
}
const rcl_subscription_t *
const std::shared_ptr<rcl_subscription_t>
SubscriptionBase::get_subscription_handle() const
{
return &subscription_handle_;
return subscription_handle_;
}
const rcl_subscription_t *
const std::shared_ptr<rcl_subscription_t>
SubscriptionBase::get_intra_process_subscription_handle() const
{
return &intra_process_subscription_handle_;
return intra_process_subscription_handle_;
}
const rosidl_message_type_support_t &
SubscriptionBase::get_message_type_support_handle() const
{
return type_support_;
}
bool
SubscriptionBase::is_serialized() const
{
return is_serialized_;
}

View File

@@ -26,6 +26,8 @@
#include "rcutils/logging_macros.h"
#include "rclcpp/utilities.hpp"
namespace
{
@@ -50,11 +52,11 @@ Time::Time(int32_t seconds, uint32_t nanoseconds, rcl_clock_type_t clock_type)
throw std::runtime_error("cannot store a negative time point in rclcpp::Time");
}
rcl_time_.nanoseconds = RCL_S_TO_NS(static_cast<uint64_t>(seconds));
rcl_time_.nanoseconds = RCL_S_TO_NS(static_cast<int64_t>(seconds));
rcl_time_.nanoseconds += nanoseconds;
}
Time::Time(uint64_t nanoseconds, rcl_clock_type_t clock_type)
Time::Time(int64_t nanoseconds, rcl_clock_type_t clock_type)
: rcl_time_(init_time_point(clock_type))
{
rcl_time_.nanoseconds = nanoseconds;
@@ -75,7 +77,7 @@ Time::Time(
throw std::runtime_error("cannot store a negative time point in rclcpp::Time");
}
rcl_time_.nanoseconds = RCL_S_TO_NS(static_cast<uint64_t>(time_msg.sec));
rcl_time_.nanoseconds = RCL_S_TO_NS(static_cast<int64_t>(time_msg.sec));
rcl_time_.nanoseconds += time_msg.nanosec;
}
@@ -115,7 +117,7 @@ Time::operator=(const builtin_interfaces::msg::Time & time_msg)
rcl_clock_type_t ros_time = RCL_ROS_TIME;
rcl_time_ = init_time_point(ros_time); // TODO(tfoote) hard coded ROS here
rcl_time_.nanoseconds = RCL_S_TO_NS(static_cast<uint64_t>(time_msg.sec));
rcl_time_.nanoseconds = RCL_S_TO_NS(static_cast<int64_t>(time_msg.sec));
rcl_time_.nanoseconds += time_msg.nanosec;
return *this;
}
@@ -179,11 +181,11 @@ Time::operator>(const rclcpp::Time & rhs) const
Time
Time::operator+(const rclcpp::Duration & rhs) const
{
if (rhs.nanoseconds() > 0 && (uint64_t)rhs.nanoseconds() >
std::numeric_limits<rcl_time_point_value_t>::max() -
(rcl_time_point_value_t)this->nanoseconds())
{
throw std::overflow_error("addition leads to uint64_t overflow");
if (rclcpp::add_will_overflow(rhs.nanoseconds(), this->nanoseconds())) {
throw std::overflow_error("addition leads to int64_t overflow");
}
if (rclcpp::add_will_underflow(rhs.nanoseconds(), this->nanoseconds())) {
throw std::underflow_error("addition leads to int64_t underflow");
}
return Time(this->nanoseconds() + rhs.nanoseconds(), this->get_clock_type());
}
@@ -195,17 +197,12 @@ Time::operator-(const rclcpp::Time & rhs) const
throw std::runtime_error("can't subtract times with different time sources");
}
if (rcl_time_.nanoseconds >
(uint64_t)std::numeric_limits<rcl_duration_value_t>::max() + rhs.rcl_time_.nanoseconds)
{
throw std::underflow_error("time subtraction leads to int64_t overflow");
if (rclcpp::sub_will_overflow(rcl_time_.nanoseconds, rhs.rcl_time_.nanoseconds)) {
throw std::overflow_error("time subtraction leads to int64_t overflow");
}
if (rcl_time_.nanoseconds < rhs.rcl_time_.nanoseconds) {
rcl_time_point_value_t negative_delta = rhs.rcl_time_.nanoseconds - rcl_time_.nanoseconds;
if (negative_delta > (uint64_t) std::numeric_limits<rcl_duration_value_t>::min()) {
throw std::underflow_error("time subtraction leads to int64_t underflow");
}
if (rclcpp::sub_will_underflow(rcl_time_.nanoseconds, rhs.rcl_time_.nanoseconds)) {
throw std::underflow_error("time subtraction leads to int64_t underflow");
}
return Duration(rcl_time_.nanoseconds - rhs.rcl_time_.nanoseconds);
@@ -214,26 +211,28 @@ Time::operator-(const rclcpp::Time & rhs) const
Time
Time::operator-(const rclcpp::Duration & rhs) const
{
if (rhs.nanoseconds() > 0 && rcl_time_.nanoseconds >
std::numeric_limits<rcl_time_point_value_t>::max() - (uint64_t)rhs.nanoseconds())
{
throw std::underflow_error("time subtraction leads to uint64_t overflow");
if (rclcpp::sub_will_overflow(rcl_time_.nanoseconds, rhs.nanoseconds())) {
throw std::overflow_error("time subtraction leads to int64_t overflow");
}
if (rcl_time_.nanoseconds < (uint64_t) std::numeric_limits<rcl_duration_value_t>::max() &&
(int64_t)rcl_time_.nanoseconds < (int64_t)rhs.nanoseconds())
{
throw std::underflow_error("time subtraction leads to uint64_t underflow");
if (rclcpp::sub_will_underflow(rcl_time_.nanoseconds, rhs.nanoseconds())) {
throw std::underflow_error("time subtraction leads to int64_t underflow");
}
return Time(rcl_time_.nanoseconds - rhs.nanoseconds(), rcl_time_.clock_type);
}
uint64_t
int64_t
Time::nanoseconds() const
{
return rcl_time_.nanoseconds;
}
double
Time::seconds() const
{
return std::chrono::duration<double>(std::chrono::nanoseconds(rcl_time_.nanoseconds)).count();
}
rcl_clock_type_t
Time::get_clock_type() const
{
@@ -243,13 +242,20 @@ Time::get_clock_type() const
Time
operator+(const rclcpp::Duration & lhs, const rclcpp::Time & rhs)
{
if (rhs.nanoseconds() >
std::numeric_limits<rcl_time_point_value_t>::max() - (rcl_time_point_value_t)lhs.nanoseconds())
{
throw std::overflow_error("addition leads to uint64_t overflow");
if (rclcpp::add_will_overflow(rhs.nanoseconds(), lhs.nanoseconds())) {
throw std::overflow_error("addition leads to int64_t overflow");
}
if (rclcpp::add_will_underflow(rhs.nanoseconds(), lhs.nanoseconds())) {
throw std::underflow_error("addition leads to int64_t underflow");
}
return Time(lhs.nanoseconds() + rhs.nanoseconds(), rhs.get_clock_type());
}
Time
Time::max()
{
return Time(std::numeric_limits<int32_t>::max(), 999999999);
}
} // namespace rclcpp

View File

@@ -34,7 +34,8 @@ namespace rclcpp
{
TimeSource::TimeSource(std::shared_ptr<rclcpp::Node> node)
: ros_time_active_(false)
: clock_subscription_(nullptr),
ros_time_active_(false)
{
this->attachNode(node);
}
@@ -65,32 +66,12 @@ void TimeSource::attachNode(
node_services_ = node_services_interface;
// TODO(tfoote): Update QOS
const std::string & topic_name = "/clock";
rclcpp::callback_group::CallbackGroup::SharedPtr group;
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
auto msg_mem_strat = MessageMemoryStrategy<MessageT, Alloc>::create_default();
auto allocator = std::make_shared<Alloc>();
auto cb = std::bind(&TimeSource::clock_cb, this, std::placeholders::_1);
clock_subscription_ = rclcpp::create_subscription<MessageT, decltype(cb), Alloc, SubscriptionT>(
node_topics_.get(),
topic_name,
std::move(cb),
rmw_qos_profile_default,
group,
false,
false,
msg_mem_strat,
allocator);
parameter_client_ = std::make_shared<rclcpp::AsyncParametersClient>(
node_base_,
node_topics_,
node_graph_,
node_services_
);
);
parameter_subscription_ =
parameter_client_->on_parameter_event(std::bind(&TimeSource::on_parameter_event,
this, std::placeholders::_1));
@@ -118,7 +99,8 @@ void TimeSource::attachClock(std::shared_ptr<rclcpp::Clock> clock)
associated_clocks_.push_back(clock);
// Set the clock if there's already data for it
if (last_msg_set_) {
set_clock(last_msg_set_, ros_time_active_, clock);
auto time_msg = std::make_shared<builtin_interfaces::msg::Time>(last_msg_set_->clock);
set_clock(time_msg, ros_time_active_, clock);
}
}
@@ -144,70 +126,72 @@ void TimeSource::set_clock(
const builtin_interfaces::msg::Time::SharedPtr msg, bool set_ros_time_enabled,
std::shared_ptr<rclcpp::Clock> clock)
{
// Compute diff
rclcpp::Time msg_time = rclcpp::Time(*msg);
rclcpp::Time now = clock->now();
auto diff = now - msg_time;
rclcpp::TimeJump jump;
jump.delta_.nanoseconds = diff.nanoseconds();
// Compute jump type
if (clock->ros_time_is_active()) {
if (set_ros_time_enabled) {
jump.jump_type_ = TimeJump::ClockChange_t::ROS_TIME_NO_CHANGE;
} else {
jump.jump_type_ = TimeJump::ClockChange_t::ROS_TIME_DEACTIVATED;
}
} else if (!clock->ros_time_is_active()) {
if (set_ros_time_enabled) {
jump.jump_type_ = TimeJump::ClockChange_t::ROS_TIME_ACTIVATED;
} else {
jump.jump_type_ = TimeJump::ClockChange_t::SYSTEM_TIME_NO_CHANGE;
}
}
if (jump.jump_type_ == TimeJump::ClockChange_t::SYSTEM_TIME_NO_CHANGE) {
// No change/no updates don't act.
return;
}
auto active_callbacks = clock->get_triggered_callback_handlers(jump);
clock->invoke_prejump_callbacks(active_callbacks);
// Do change
if (jump.jump_type_ == TimeJump::ClockChange_t::ROS_TIME_DEACTIVATED) {
if (!set_ros_time_enabled && clock->ros_time_is_active()) {
disable_ros_time(clock);
} else if (jump.jump_type_ == TimeJump::ClockChange_t::ROS_TIME_ACTIVATED) {
} else if (set_ros_time_enabled && !clock->ros_time_is_active()) {
enable_ros_time(clock);
}
if (jump.jump_type_ == TimeJump::ClockChange_t::ROS_TIME_ACTIVATED ||
jump.jump_type_ == TimeJump::ClockChange_t::ROS_TIME_NO_CHANGE)
{
auto ret = rcl_set_ros_time_override(&(clock->rcl_clock_), msg_time.nanoseconds());
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(
ret, "Failed to set ros_time_override_status");
}
auto ret = rcl_set_ros_time_override(&(clock->rcl_clock_), rclcpp::Time(*msg).nanoseconds());
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(
ret, "Failed to set ros_time_override_status");
}
// Post change callbacks
clock->invoke_postjump_callbacks(active_callbacks, jump);
}
void TimeSource::clock_cb(const builtin_interfaces::msg::Time::SharedPtr msg)
void TimeSource::clock_cb(const rosgraph_msgs::msg::Clock::SharedPtr msg)
{
if (!this->ros_time_active_) {
if (!this->ros_time_active_ && SET_TRUE == this->parameter_state_) {
enable_ros_time();
}
// Cache the last message in case a new clock is attached.
last_msg_set_ = msg;
auto time_msg = std::make_shared<builtin_interfaces::msg::Time>(msg->clock);
std::lock_guard<std::mutex> guard(clock_list_lock_);
for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
set_clock(msg, true, *it);
if (SET_TRUE == this->parameter_state_) {
std::lock_guard<std::mutex> guard(clock_list_lock_);
for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
set_clock(time_msg, true, *it);
}
}
}
void TimeSource::create_clock_sub()
{
std::lock_guard<std::mutex> guard(clock_sub_lock_);
if (clock_subscription_) {
// Subscription already created.
return;
}
const std::string topic_name = "/clock";
rclcpp::callback_group::CallbackGroup::SharedPtr group;
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
auto msg_mem_strat = MessageMemoryStrategy<MessageT, Alloc>::create_default();
auto allocator = std::make_shared<Alloc>();
auto cb = std::bind(&TimeSource::clock_cb, this, std::placeholders::_1);
clock_subscription_ = rclcpp::create_subscription<
MessageT, decltype(cb), Alloc, MessageT, SubscriptionT
>(
node_topics_.get(),
topic_name,
std::move(cb),
rmw_qos_profile_default,
group,
false,
false,
msg_mem_strat,
allocator);
}
void TimeSource::destroy_clock_sub()
{
std::lock_guard<std::mutex> guard(clock_sub_lock_);
clock_subscription_.reset();
}
void TimeSource::on_parameter_event(const rcl_interfaces::msg::ParameterEvent::SharedPtr event)
{
// Filter for only 'use_sim_time' being added or changed.
@@ -215,16 +199,18 @@ void TimeSource::on_parameter_event(const rcl_interfaces::msg::ParameterEvent::S
{rclcpp::ParameterEventsFilter::EventType::NEW,
rclcpp::ParameterEventsFilter::EventType::CHANGED});
for (auto & it : filter.get_events()) {
if (it.second->value.type != parameter::ParameterType::PARAMETER_BOOL) {
if (it.second->value.type != ParameterType::PARAMETER_BOOL) {
RCUTILS_LOG_ERROR("use_sim_time parameter set to something besides a bool");
continue;
}
if (it.second->value.bool_value) {
parameter_state_ = SET_TRUE;
enable_ros_time();
create_clock_sub();
} else {
parameter_state_ = SET_FALSE;
disable_ros_time();
destroy_clock_sub();
}
}
// Handle the case that use_sim_time was deleted.

View File

@@ -16,16 +16,41 @@
#include <chrono>
#include <string>
#include <memory>
#include "rcutils/logging_macros.h"
using rclcpp::TimerBase;
TimerBase::TimerBase(std::chrono::nanoseconds period)
TimerBase::TimerBase(rclcpp::Clock::SharedPtr clock, std::chrono::nanoseconds period)
{
clock_ = clock;
timer_handle_ = std::shared_ptr<rcl_timer_t>(
new rcl_timer_t, [ = ](rcl_timer_t * timer) mutable
{
if (rcl_timer_fini(timer) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Failed to clean up rcl timer handle: %s", rcl_get_error_string().str);
rcl_reset_error();
}
delete timer;
// Captured shared pointer by copy, reset to make sure timer is finalized before clock
clock.reset();
});
*timer_handle_.get() = rcl_get_zero_initialized_timer();
rcl_clock_t * clock_handle = clock_->get_clock_handle();
if (rcl_timer_init(
&timer_handle_, period.count(), nullptr,
timer_handle_.get(), clock_handle, period.count(), nullptr,
rcl_get_default_allocator()) != RCL_RET_OK)
{
fprintf(stderr, "Couldn't initialize rcl timer handle: %s\n", rcl_get_error_string_safe());
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't initialize rcl timer handle: %s\n", rcl_get_error_string().str);
rcl_reset_error();
}
}
@@ -35,16 +60,16 @@ TimerBase::~TimerBase()
void
TimerBase::cancel()
{
if (rcl_timer_cancel(&timer_handle_) != RCL_RET_OK) {
throw std::runtime_error(std::string("Couldn't cancel timer: ") + rcl_get_error_string_safe());
if (rcl_timer_cancel(timer_handle_.get()) != RCL_RET_OK) {
throw std::runtime_error(std::string("Couldn't cancel timer: ") + rcl_get_error_string().str);
}
}
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());
if (rcl_timer_reset(timer_handle_.get()) != RCL_RET_OK) {
throw std::runtime_error(std::string("Couldn't reset timer: ") + rcl_get_error_string().str);
}
}
@@ -52,8 +77,8 @@ bool
TimerBase::is_ready()
{
bool ready = false;
if (rcl_timer_is_ready(&timer_handle_, &ready) != RCL_RET_OK) {
throw std::runtime_error(std::string("Failed to check timer: ") + rcl_get_error_string_safe());
if (rcl_timer_is_ready(timer_handle_.get(), &ready) != RCL_RET_OK) {
throw std::runtime_error(std::string("Failed to check timer: ") + rcl_get_error_string().str);
}
return ready;
}
@@ -62,16 +87,18 @@ std::chrono::nanoseconds
TimerBase::time_until_trigger()
{
int64_t time_until_next_call = 0;
if (rcl_timer_get_time_until_next_call(&timer_handle_, &time_until_next_call) != RCL_RET_OK) {
if (rcl_timer_get_time_until_next_call(timer_handle_.get(),
&time_until_next_call) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("Timer could not get time until next call: ") +
rcl_get_error_string_safe());
rcl_get_error_string().str);
}
return std::chrono::nanoseconds(time_until_next_call);
}
const rcl_timer_t *
std::shared_ptr<const rcl_timer_t>
TimerBase::get_timer_handle()
{
return &timer_handle_;
return timer_handle_;
}

View File

@@ -24,12 +24,16 @@
#include <string>
#include <vector>
#include "rclcpp/exceptions.hpp"
#include "rcl/error_handling.h"
#include "rcl/rcl.h"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
#include "rcutils/logging_macros.h"
// Determine if sigaction is available
#if __APPLE__ || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
#define HAS_SIGACTION
@@ -114,8 +118,9 @@ trigger_interrupt_guard_condition(int signal_value)
for (auto & kv : g_sigint_guard_cond_handles) {
rcl_ret_t status = rcl_trigger_guard_condition(&(kv.second));
if (status != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to trigger guard condition: %s\n", rcl_get_error_string_safe());
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"failed to trigger guard condition: %s", rcl_get_error_string().str);
}
}
}
@@ -157,12 +162,12 @@ signal_handler(int signal_value)
}
void
rclcpp::init(int argc, char * argv[])
rclcpp::init(int argc, char const * const argv[])
{
g_is_interrupted.store(false);
if (rcl_init(argc, argv, rcl_get_default_allocator()) != RCL_RET_OK) {
std::string msg = "failed to initialize rmw implementation: ";
msg += rcl_get_error_string_safe();
msg += rcl_get_error_string().str;
rcl_reset_error();
throw std::runtime_error(msg);
}
@@ -188,12 +193,83 @@ rclcpp::init(int argc, char * argv[])
#endif
}
std::vector<std::string>
rclcpp::init_and_remove_ros_arguments(int argc, char const * const argv[])
{
rclcpp::init(argc, argv);
return rclcpp::remove_ros_arguments(argc, argv);
}
std::vector<std::string>
rclcpp::remove_ros_arguments(int argc, char const * const argv[])
{
rcl_allocator_t alloc = rcl_get_default_allocator();
rcl_arguments_t parsed_args = rcl_get_zero_initialized_arguments();
rcl_ret_t ret;
ret = rcl_parse_arguments(argc, argv, alloc, &parsed_args);
if (RCL_RET_OK != ret) {
exceptions::throw_from_rcl_error(ret, "failed to parse arguments");
}
int nonros_argc = 0;
const char ** nonros_argv = NULL;
ret = rcl_remove_ros_arguments(
argv,
&parsed_args,
alloc,
&nonros_argc,
&nonros_argv);
if (RCL_RET_OK != ret) {
// Not using throw_from_rcl_error, because we may need to append deallocation failures.
exceptions::RCLErrorBase base_exc(ret, rcl_get_error_state());
rcl_reset_error();
if (NULL != nonros_argv) {
alloc.deallocate(nonros_argv, alloc.state);
}
if (RCL_RET_OK != rcl_arguments_fini(&parsed_args)) {
base_exc.formatted_message += std::string(
", failed also to cleanup parsed arguments, leaking memory: ") +
rcl_get_error_string().str;
rcl_reset_error();
}
throw exceptions::RCLError(base_exc, "");
}
std::vector<std::string> return_arguments;
return_arguments.resize(nonros_argc);
for (int ii = 0; ii < nonros_argc; ++ii) {
return_arguments[ii] = std::string(nonros_argv[ii]);
}
if (NULL != nonros_argv) {
alloc.deallocate(nonros_argv, alloc.state);
}
ret = rcl_arguments_fini(&parsed_args);
if (RCL_RET_OK != ret) {
exceptions::throw_from_rcl_error(ret, "failed to cleanup parsed arguments, leaking memory");
}
return return_arguments;
}
bool
rclcpp::ok()
{
return ::g_signal_status == 0;
}
bool
rclcpp::is_initialized()
{
return rcl_ok();
}
static std::mutex on_shutdown_mutex_;
static std::vector<std::function<void(void)>> on_shutdown_callbacks_;
@@ -231,7 +307,7 @@ rclcpp::get_sigint_guard_condition(rcl_wait_set_t * wait_set)
if (rcl_guard_condition_init(&handle, options) != RCL_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(std::string(
"Couldn't initialize guard condition: ") + rcl_get_error_string_safe());
"Couldn't initialize guard condition: ") + rcl_get_error_string().str);
// *INDENT-ON*
}
g_sigint_guard_cond_handles[wait_set] = handle;
@@ -249,7 +325,7 @@ rclcpp::release_sigint_guard_condition(rcl_wait_set_t * wait_set)
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(std::string(
"Failed to destroy sigint guard condition: ") +
rcl_get_error_string_safe());
rcl_get_error_string().str);
// *INDENT-ON*
}
g_sigint_guard_cond_handles.erase(kv);
@@ -277,3 +353,15 @@ rclcpp::sleep_for(const std::chrono::nanoseconds & nanoseconds)
// Return true if the timeout elapsed successfully, otherwise false.
return !g_is_interrupted;
}
const char *
rclcpp::get_c_string(const char * string_in)
{
return string_in;
}
const char *
rclcpp::get_c_string(const std::string & string_in)
{
return string_in.c_str();
}

View File

@@ -0,0 +1,103 @@
// Copyright 2018 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 <chrono>
#include <string>
#include <memory>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/executors.hpp"
#include "rcl_interfaces/msg/intra_process_message.hpp"
using namespace std::chrono_literals;
using rcl_interfaces::msg::IntraProcessMessage;
class TestMultiThreadedExecutor : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
};
/*
Test that timers are not taken multiple times when using reentrant callback groups.
*/
TEST_F(TestMultiThreadedExecutor, timer_over_take) {
#ifdef __linux__
// This seems to be the most effective way to force the bug to happen on Linux.
// This is unnecessary on MacOS, since the default scheduler causes it.
struct sched_param param;
param.sched_priority = 0;
if (sched_setscheduler(0, SCHED_BATCH, &param) != 0) {
perror("sched_setscheduler");
}
#endif
bool yield_before_execute = true;
rclcpp::executors::MultiThreadedExecutor executor(
rclcpp::executor::create_default_executor_arguments(), 2u, yield_before_execute);
ASSERT_GT(executor.get_number_of_threads(), 1u);
std::shared_ptr<rclcpp::Node> node =
std::make_shared<rclcpp::Node>("test_multi_threaded_executor_timer_over_take");
auto cbg = node->create_callback_group(rclcpp::callback_group::CallbackGroupType::Reentrant);
rclcpp::Clock system_clock(RCL_STEADY_TIME);
std::mutex last_mutex;
auto last = system_clock.now();
std::atomic_int timer_count {0};
auto timer_callback = [&timer_count, &executor, &system_clock, &last_mutex, &last]() {
// While this tolerance is a little wide, if the bug occurs, the next step will
// happen almost instantly. The purpose of this test is not to measure the jitter
// in timers, just assert that a reasonable amount of time has passed.
const double PERIOD = 0.1f;
const double TOLERANCE = 0.025f;
rclcpp::Time now = system_clock.now();
timer_count++;
if (timer_count > 5) {
executor.cancel();
}
{
std::lock_guard<std::mutex> lock(last_mutex);
double diff = std::abs((now - last).nanoseconds()) / 1.0e9;
last = now;
if (diff < PERIOD - TOLERANCE || diff > PERIOD + TOLERANCE) {
executor.cancel();
ASSERT_GT(diff, PERIOD - TOLERANCE);
ASSERT_LT(diff, PERIOD + TOLERANCE);
}
}
};
auto timer = node->create_wall_timer(100ms, timer_callback, cbg);
executor.add_node(node);
executor.spin();
}

View File

@@ -54,6 +54,9 @@ TEST(TestDuration, operators) {
EXPECT_EQ(sub.nanoseconds(), (rcl_duration_value_t)(young.nanoseconds() - old.nanoseconds()));
EXPECT_EQ(sub, young - old);
rclcpp::Duration scale = old * 3;
EXPECT_EQ(scale.nanoseconds(), (rcl_duration_value_t)(old.nanoseconds() * 3));
rclcpp::Duration time = rclcpp::Duration(0, 0);
rclcpp::Duration copy_constructor_duration(time);
rclcpp::Duration assignment_op_duration = rclcpp::Duration(1, 0);
@@ -85,4 +88,42 @@ TEST(TestDuration, overflows) {
EXPECT_THROW(min - one, std::underflow_error);
EXPECT_THROW(negative_one + min, std::underflow_error);
EXPECT_THROW(negative_one - max, std::underflow_error);
rclcpp::Duration base_d = max * 0.3;
EXPECT_THROW(base_d * 4, std::overflow_error);
EXPECT_THROW(base_d * (-4), std::underflow_error);
rclcpp::Duration base_d_neg = max * (-0.3);
EXPECT_THROW(base_d_neg * (-4), std::overflow_error);
EXPECT_THROW(base_d_neg * 4, std::underflow_error);
}
TEST(TestDuration, negative_duration) {
rclcpp::Duration assignable_duration = rclcpp::Duration(0) - rclcpp::Duration(5, 0);
{
// avoid windows converting a literal number less than -INT_MAX to unsigned int C4146
int64_t expected_value = -5000;
expected_value *= 1000 * 1000;
EXPECT_EQ(expected_value, assignable_duration.nanoseconds());
}
{
builtin_interfaces::msg::Duration duration_msg;
duration_msg.sec = -4;
duration_msg.nanosec = 250000000;
assignable_duration = duration_msg;
// avoid windows converting a literal number less than -INT_MAX to unsigned int C4146
int64_t expected_value = -3750;
expected_value *= 1000 * 1000;
EXPECT_EQ(expected_value, assignable_duration.nanoseconds());
}
}
TEST(TestDuration, maximum_duration) {
rclcpp::Duration max_duration = rclcpp::Duration::max();
rclcpp::Duration max(std::numeric_limits<int32_t>::max(), 999999999);
EXPECT_EQ(max_duration, max);
}

View File

@@ -28,7 +28,7 @@
using namespace std::chrono_literals;
class TestExcutors : public ::testing::Test
class TestExecutors : public ::testing::Test
{
protected:
static void SetUpTestCase()
@@ -50,7 +50,7 @@ protected:
};
// Make sure that executors detach from nodes when destructing
TEST_F(TestExcutors, detachOnDestruction) {
TEST_F(TestExecutors, detachOnDestruction) {
{
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);

View File

@@ -106,6 +106,15 @@ TEST_F(TestExternallyDefinedServices, extern_defined_initialized) {
return;
}
// Destruct the service
ret = rcl_service_fini(
&service_handle,
node_handle->get_node_base_interface()->get_rcl_node_handle());
if (ret != RCL_RET_OK) {
FAIL();
return;
}
SUCCEED();
}
@@ -139,5 +148,15 @@ TEST_F(TestExternallyDefinedServices, extern_defined_destructor) {
FAIL();
return;
}
// Destruct the service
ret = rcl_service_fini(
&service_handle,
node_handle->get_node_base_interface()->get_rcl_node_handle());
if (ret != RCL_RET_OK) {
FAIL();
return;
}
SUCCEED();
}

View File

@@ -52,6 +52,9 @@ TEST_F(TestFindWeakNodes, allocator_strategy_with_weak_nodes) {
// THEN
// The result of finding dangling node pointers should be true
ASSERT_TRUE(has_invalid_weak_nodes);
// Prevent memory leak due to the order of destruction
memory_strategy->clear_handles();
}
TEST_F(TestFindWeakNodes, allocator_strategy_no_weak_nodes) {
@@ -73,4 +76,7 @@ TEST_F(TestFindWeakNodes, allocator_strategy_no_weak_nodes) {
// THEN
// The result of finding dangling node pointers should be false
ASSERT_FALSE(has_invalid_weak_nodes);
// Prevent memory leak due to the order of destruction
memory_strategy->clear_handles();
}

View File

@@ -706,3 +706,23 @@ TEST(TestFunctionTraits, sfinae_match) {
EXPECT_EQ("foo", func_accept_callback_return_type(lambda_no_args_string));
}
class TestMember : public ::testing::Test
{
public:
void MemberFunctor(int, float, std::string) {}
};
/*
Regression test for https://github.com/ros2/rclcpp/issues/479, specific to classes using the
TEST_F GTest macro.
*/
TEST_F(TestMember, bind_member_functor) {
auto bind_member_functor = std::bind(&TestMember::MemberFunctor, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3);
static_assert(
rclcpp::function_traits::check_arguments<decltype(bind_member_functor), int, float,
std::string>::value,
"Functor accepts an int, a float and a string as arguments");
}

29
rclcpp/test/test_init.cpp Normal file
View File

@@ -0,0 +1,29 @@
// Copyright 2018 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/utilities.hpp"
TEST(TestInit, is_initialized) {
EXPECT_FALSE(rclcpp::is_initialized());
rclcpp::init(0, nullptr);
EXPECT_TRUE(rclcpp::is_initialized());
rclcpp::shutdown();
EXPECT_TRUE(rclcpp::is_initialized());
}

View File

@@ -31,9 +31,10 @@ rclcpp::Logger g_logger = rclcpp::get_logger("name");
struct LogEvent
{
rcutils_log_location_t * location;
const rcutils_log_location_t * location;
int level;
std::string name;
rcutils_time_point_value_t timestamp;
std::string message;
};
LogEvent g_last_log_event;
@@ -49,13 +50,15 @@ public:
rcutils_logging_set_default_logger_level(RCUTILS_LOG_SEVERITY_DEBUG);
auto rcutils_logging_console_output_handler = [](
rcutils_log_location_t * location,
int level, const char * name, const char * format, va_list * args) -> void
const rcutils_log_location_t * location,
int level, const char * name, rcutils_time_point_value_t timestamp,
const char * format, va_list * args) -> void
{
g_log_calls += 1;
g_last_log_event.location = location;
g_last_log_event.level = level;
g_last_log_event.name = name ? name : "";
g_last_log_event.timestamp = timestamp;
char buffer[1024];
vsnprintf(buffer, sizeof(buffer), format, *args);
g_last_log_event.message = buffer;
@@ -68,7 +71,7 @@ public:
void TearDown()
{
rcutils_logging_set_output_handler(this->previous_output_handler);
g_rcutils_logging_initialized = false;
ASSERT_EQ(RCUTILS_RET_OK, rcutils_logging_shutdown());
EXPECT_FALSE(g_rcutils_logging_initialized);
}
};
@@ -82,13 +85,33 @@ TEST_F(TestLoggingMacros, test_logging_named) {
if (g_last_log_event.location) {
EXPECT_STREQ("TestBody", g_last_log_event.location->function_name);
EXPECT_THAT(g_last_log_event.location->file_name, EndsWith("test_logging.cpp"));
EXPECT_EQ(78u, g_last_log_event.location->line_number);
EXPECT_EQ(81u, g_last_log_event.location->line_number);
}
EXPECT_EQ(RCUTILS_LOG_SEVERITY_DEBUG, g_last_log_event.level);
EXPECT_EQ("name", g_last_log_event.name);
EXPECT_EQ("message 3", g_last_log_event.message);
}
TEST_F(TestLoggingMacros, test_logging_string) {
for (std::string i : {"one", "two", "three"}) {
RCLCPP_DEBUG(g_logger, "message " + i);
}
EXPECT_EQ(3u, g_log_calls);
EXPECT_EQ("message three", g_last_log_event.message);
RCLCPP_DEBUG(g_logger, "message " "four");
EXPECT_EQ("message four", g_last_log_event.message);
RCLCPP_DEBUG(g_logger, std::string("message " "five"));
EXPECT_EQ("message five", g_last_log_event.message);
RCLCPP_DEBUG(g_logger, std::string("message %s"), "six");
EXPECT_EQ("message six", g_last_log_event.message);
RCLCPP_DEBUG(g_logger, "message seven");
EXPECT_EQ("message seven", g_last_log_event.message);
}
TEST_F(TestLoggingMacros, test_logging_once) {
for (int i : {1, 2, 3}) {
RCLCPP_INFO_ONCE(g_logger, "message %d", i);

View File

@@ -16,6 +16,7 @@
#include <string>
#include <memory>
#include <vector>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/node.hpp"
@@ -75,14 +76,25 @@ TEST_F(TestNode, get_name_and_namespace) {
}
TEST_F(TestNode, get_logger) {
// Currently the namespace is not taken into account with the node logger name
{
auto node = std::make_shared<rclcpp::Node>("my_node");
EXPECT_STREQ("my_node", node->get_logger().get_name());
}
{
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
EXPECT_STREQ("my_node", node->get_logger().get_name());
EXPECT_STREQ("ns.my_node", node->get_logger().get_name());
}
{
auto node = std::make_shared<rclcpp::Node>("my_node", "ns");
EXPECT_STREQ("ns.my_node", node->get_logger().get_name());
}
{
auto node = std::make_shared<rclcpp::Node>("my_node", "/my/ns");
EXPECT_STREQ("my.ns.my_node", node->get_logger().get_name());
}
{
auto node = std::make_shared<rclcpp::Node>("my_node", "my/ns");
EXPECT_STREQ("my.ns.my_node", node->get_logger().get_name());
}
}
@@ -99,5 +111,5 @@ TEST_F(TestNode, now) {
auto now_builtin = node->now().nanoseconds();
auto now_external = clock->now().nanoseconds();
EXPECT_GE(now_external, now_builtin);
EXPECT_LT(now_external - now_builtin, 50000ul);
EXPECT_LT(now_external - now_builtin, 5000000L);
}

View File

@@ -0,0 +1,64 @@
// Copyright 2018 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 <vector>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/rclcpp.hpp"
class TestNodeWithGlobalArgs : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
const char * const args[] = {"proc", "__node:=global_node_name"};
rclcpp::init(2, args);
}
};
TEST_F(TestNodeWithGlobalArgs, local_arguments_before_global) {
auto context = rclcpp::contexts::default_context::get_global_default_context();
const std::vector<std::string> arguments = {"__node:=local_arguments_test"};
const std::vector<rclcpp::Parameter> initial_values = {};
const bool use_global_arguments = true;
const bool use_intra_process = false;
auto node = rclcpp::Node::make_shared(
"orig_name", "", context, arguments, initial_values, use_global_arguments, use_intra_process);
EXPECT_STREQ("local_arguments_test", node->get_name());
}
TEST_F(TestNodeWithGlobalArgs, use_or_ignore_global_arguments) {
auto context = rclcpp::contexts::default_context::get_global_default_context();
const std::vector<std::string> arguments = {};
const std::vector<rclcpp::Parameter> initial_values = {};
const bool use_intra_process = false;
{ // Don't use global args
const bool use_global_arguments = false;
auto node = rclcpp::Node::make_shared(
"orig_name", "", context, arguments, initial_values, use_global_arguments, use_intra_process);
EXPECT_STREQ("orig_name", node->get_name());
}
{ // Do use global args
const bool use_global_arguments = true;
auto node = rclcpp::Node::make_shared(
"orig_name", "", context, arguments, initial_values, use_global_arguments, use_intra_process);
EXPECT_STREQ("global_node_name", node->get_name());
}
}

View File

@@ -0,0 +1,70 @@
// Copyright 2018 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 <vector>
#include "rclcpp/node.hpp"
#include "rclcpp/rclcpp.hpp"
class TestNodeWithInitialValues : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, NULL);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
TEST_F(TestNodeWithInitialValues, no_initial_values) {
auto context = rclcpp::contexts::default_context::get_global_default_context();
const std::vector<std::string> arguments = {};
const std::vector<rclcpp::Parameter> initial_values = {};
const bool use_global_arguments = false;
const bool use_intra_process = false;
auto node = rclcpp::Node::make_shared(
"node_name", "", context, arguments, initial_values, use_global_arguments, use_intra_process);
auto list_params_result = node->list_parameters({}, 0);
EXPECT_EQ(0u, list_params_result.names.size());
}
TEST_F(TestNodeWithInitialValues, multiple_initial_values) {
auto context = rclcpp::contexts::default_context::get_global_default_context();
const std::vector<std::string> arguments = {};
const std::vector<rclcpp::Parameter> initial_values = {
rclcpp::Parameter("foo", true),
rclcpp::Parameter("bar", "hello world"),
rclcpp::Parameter("baz", std::vector<double>{3.14, 2.718})
};
const bool use_global_arguments = false;
const bool use_intra_process = false;
auto node = rclcpp::Node::make_shared(
"node_name", "", context, arguments, initial_values, use_global_arguments, use_intra_process);
auto list_params_result = node->list_parameters({}, 0);
EXPECT_EQ(3u, list_params_result.names.size());
EXPECT_TRUE(node->get_parameter("foo").get_value<bool>());
EXPECT_STREQ("hello world", node->get_parameter("bar").get_value<std::string>().c_str());
std::vector<double> double_array = node->get_parameter("baz").get_value<std::vector<double>>();
ASSERT_EQ(2u, double_array.size());
EXPECT_DOUBLE_EQ(3.14, double_array.at(0));
EXPECT_DOUBLE_EQ(2.718, double_array.at(1));
}

View File

@@ -0,0 +1,711 @@
// Copyright 2018 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 <algorithm>
#include <limits>
#include <stdexcept>
#include <string>
#include <vector>
#include "rclcpp/parameter.hpp"
#include "rclcpp/rclcpp.hpp"
class TestParameter : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
};
TEST(TestParameter, not_set_variant) {
// Direct instantiation
rclcpp::Parameter not_set_variant;
EXPECT_EQ(rclcpp::PARAMETER_NOT_SET, not_set_variant.get_type());
EXPECT_EQ("not set", not_set_variant.get_type_name());
EXPECT_THROW(not_set_variant.as_bool(), rclcpp::ParameterTypeException);
EXPECT_THROW(not_set_variant.as_int(), rclcpp::ParameterTypeException);
EXPECT_THROW(not_set_variant.as_double(), rclcpp::ParameterTypeException);
EXPECT_THROW(not_set_variant.as_string(), rclcpp::ParameterTypeException);
EXPECT_THROW(not_set_variant.as_byte_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(not_set_variant.as_bool_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(not_set_variant.as_integer_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(not_set_variant.as_double_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(not_set_variant.as_string_array(), rclcpp::ParameterTypeException);
rcl_interfaces::msg::Parameter not_set_param = not_set_variant.to_parameter_msg();
EXPECT_EQ("", not_set_param.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET, not_set_param.value.type);
// From parameter message
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_NOT_SET,
rclcpp::Parameter::from_parameter_msg(not_set_param).get_type());
}
TEST(TestParameter, bool_variant) {
// Direct instantiation
rclcpp::Parameter bool_variant_true("bool_param", true);
EXPECT_EQ("bool_param", bool_variant_true.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_BOOL, bool_variant_true.get_type());
EXPECT_EQ("bool", bool_variant_true.get_type_name());
EXPECT_TRUE(bool_variant_true.get_value<rclcpp::ParameterType::PARAMETER_BOOL>());
EXPECT_TRUE(bool_variant_true.get_value_message().bool_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_BOOL,
bool_variant_true.get_value_message().type);
EXPECT_TRUE(bool_variant_true.as_bool());
EXPECT_THROW(bool_variant_true.as_int(), rclcpp::ParameterTypeException);
EXPECT_THROW(bool_variant_true.as_double(), rclcpp::ParameterTypeException);
EXPECT_THROW(bool_variant_true.as_string(), rclcpp::ParameterTypeException);
EXPECT_THROW(bool_variant_true.as_byte_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(bool_variant_true.as_bool_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(bool_variant_true.as_integer_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(bool_variant_true.as_double_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(bool_variant_true.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("true", bool_variant_true.value_to_string());
rclcpp::Parameter bool_variant_false("bool_param", false);
EXPECT_FALSE(bool_variant_false.get_value<rclcpp::ParameterType::PARAMETER_BOOL>());
EXPECT_FALSE(bool_variant_false.get_value_message().bool_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_BOOL,
bool_variant_false.get_value_message().type);
rcl_interfaces::msg::Parameter bool_param = bool_variant_true.to_parameter_msg();
EXPECT_EQ("bool_param", bool_param.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_BOOL, bool_param.value.type);
EXPECT_TRUE(bool_param.value.bool_value);
// From parameter message
rclcpp::Parameter from_msg_true =
rclcpp::Parameter::from_parameter_msg(bool_param);
EXPECT_EQ("bool_param", from_msg_true.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_BOOL, from_msg_true.get_type());
EXPECT_EQ("bool", from_msg_true.get_type_name());
EXPECT_TRUE(from_msg_true.get_value<rclcpp::ParameterType::PARAMETER_BOOL>());
EXPECT_TRUE(from_msg_true.get_value_message().bool_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_BOOL,
bool_variant_false.get_value_message().type);
bool_param.value.bool_value = false;
rclcpp::Parameter from_msg_false =
rclcpp::Parameter::from_parameter_msg(bool_param);
EXPECT_FALSE(from_msg_false.get_value<rclcpp::ParameterType::PARAMETER_BOOL>());
EXPECT_FALSE(from_msg_false.get_value_message().bool_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_BOOL,
bool_variant_false.get_value_message().type);
}
TEST(TestParameter, integer_variant) {
const int TEST_VALUE {42};
// Direct instantiation
rclcpp::Parameter integer_variant("integer_param", TEST_VALUE);
EXPECT_EQ("integer_param", integer_variant.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, integer_variant.get_type());
EXPECT_EQ("integer", integer_variant.get_type_name());
EXPECT_EQ(TEST_VALUE,
integer_variant.get_value<rclcpp::ParameterType::PARAMETER_INTEGER>());
EXPECT_EQ(TEST_VALUE, integer_variant.get_value_message().integer_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER,
integer_variant.get_value_message().type);
EXPECT_EQ(TEST_VALUE, integer_variant.as_int());
EXPECT_THROW(integer_variant.as_bool(), rclcpp::ParameterTypeException);
EXPECT_THROW(integer_variant.as_double(), rclcpp::ParameterTypeException);
EXPECT_THROW(integer_variant.as_string(), rclcpp::ParameterTypeException);
EXPECT_THROW(integer_variant.as_byte_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(integer_variant.as_bool_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(integer_variant.as_integer_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(integer_variant.as_double_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(integer_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("42", integer_variant.value_to_string());
rcl_interfaces::msg::Parameter integer_param = integer_variant.to_parameter_msg();
EXPECT_EQ("integer_param", integer_param.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER, integer_param.value.type);
EXPECT_EQ(TEST_VALUE, integer_param.value.integer_value);
// From parameter message
rclcpp::Parameter from_msg =
rclcpp::Parameter::from_parameter_msg(integer_param);
EXPECT_EQ("integer_param", from_msg.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, from_msg.get_type());
EXPECT_EQ("integer", from_msg.get_type_name());
EXPECT_EQ(TEST_VALUE,
from_msg.get_value<rclcpp::ParameterType::PARAMETER_INTEGER>());
EXPECT_EQ(TEST_VALUE, from_msg.get_value_message().integer_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER,
from_msg.get_value_message().type);
}
TEST(TestParameter, long_integer_variant) {
const int64_t TEST_VALUE {std::numeric_limits<int64_t>::max()};
// Direct instantiation
rclcpp::Parameter long_variant("long_integer_param", TEST_VALUE);
EXPECT_EQ("long_integer_param", long_variant.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, long_variant.get_type());
EXPECT_EQ("integer", long_variant.get_type_name());
EXPECT_EQ(TEST_VALUE,
long_variant.get_value<rclcpp::ParameterType::PARAMETER_INTEGER>());
EXPECT_EQ(TEST_VALUE, long_variant.get_value_message().integer_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER,
long_variant.get_value_message().type);
EXPECT_EQ(TEST_VALUE, long_variant.as_int());
EXPECT_THROW(long_variant.as_bool(), rclcpp::ParameterTypeException);
EXPECT_THROW(long_variant.as_double(), rclcpp::ParameterTypeException);
EXPECT_THROW(long_variant.as_string(), rclcpp::ParameterTypeException);
EXPECT_THROW(long_variant.as_byte_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(long_variant.as_bool_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(long_variant.as_integer_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(long_variant.as_double_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(long_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("9223372036854775807", long_variant.value_to_string());
rcl_interfaces::msg::Parameter integer_param = long_variant.to_parameter_msg();
EXPECT_EQ("long_integer_param", integer_param.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER, integer_param.value.type);
EXPECT_EQ(TEST_VALUE, integer_param.value.integer_value);
// From parameter message
rclcpp::Parameter from_msg =
rclcpp::Parameter::from_parameter_msg(integer_param);
EXPECT_EQ("long_integer_param", from_msg.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, from_msg.get_type());
EXPECT_EQ("integer", from_msg.get_type_name());
EXPECT_EQ(TEST_VALUE,
from_msg.get_value<rclcpp::ParameterType::PARAMETER_INTEGER>());
EXPECT_EQ(TEST_VALUE, from_msg.get_value_message().integer_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER,
from_msg.get_value_message().type);
}
TEST(TestParameter, float_variant) {
const float TEST_VALUE {42.0f};
// Direct instantiation
rclcpp::Parameter float_variant("float_param", TEST_VALUE);
EXPECT_EQ("float_param", float_variant.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_DOUBLE, float_variant.get_type());
EXPECT_EQ("double", float_variant.get_type_name());
EXPECT_EQ(TEST_VALUE,
float_variant.get_value<rclcpp::ParameterType::PARAMETER_DOUBLE>());
EXPECT_EQ(TEST_VALUE, float_variant.get_value_message().double_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE,
float_variant.get_value_message().type);
EXPECT_EQ(TEST_VALUE, float_variant.as_double());
EXPECT_THROW(float_variant.as_bool(), rclcpp::ParameterTypeException);
EXPECT_THROW(float_variant.as_int(), rclcpp::ParameterTypeException);
EXPECT_THROW(float_variant.as_string(), rclcpp::ParameterTypeException);
EXPECT_THROW(float_variant.as_byte_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(float_variant.as_bool_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(float_variant.as_integer_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(float_variant.as_double_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(float_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("42.000000", float_variant.value_to_string());
rcl_interfaces::msg::Parameter float_param = float_variant.to_parameter_msg();
EXPECT_EQ("float_param", float_param.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE, float_param.value.type);
EXPECT_EQ(TEST_VALUE, float_param.value.double_value);
// From parameter message
rclcpp::Parameter from_msg =
rclcpp::Parameter::from_parameter_msg(float_param);
EXPECT_EQ("float_param", from_msg.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_DOUBLE, from_msg.get_type());
EXPECT_EQ("double", from_msg.get_type_name());
EXPECT_EQ(TEST_VALUE,
from_msg.get_value<rclcpp::ParameterType::PARAMETER_DOUBLE>());
EXPECT_EQ(TEST_VALUE, from_msg.get_value_message().double_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE,
from_msg.get_value_message().type);
}
TEST(TestParameter, double_variant) {
const double TEST_VALUE {-42.1};
// Direct instantiation
rclcpp::Parameter double_variant("double_param", TEST_VALUE);
EXPECT_EQ("double_param", double_variant.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_DOUBLE, double_variant.get_type());
EXPECT_EQ("double", double_variant.get_type_name());
EXPECT_EQ(TEST_VALUE,
double_variant.get_value<rclcpp::ParameterType::PARAMETER_DOUBLE>());
EXPECT_EQ(TEST_VALUE, double_variant.get_value_message().double_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE,
double_variant.get_value_message().type);
EXPECT_EQ(TEST_VALUE, double_variant.as_double());
EXPECT_THROW(double_variant.as_bool(), rclcpp::ParameterTypeException);
EXPECT_THROW(double_variant.as_int(), rclcpp::ParameterTypeException);
EXPECT_THROW(double_variant.as_string(), rclcpp::ParameterTypeException);
EXPECT_THROW(double_variant.as_byte_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(double_variant.as_bool_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(double_variant.as_integer_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(double_variant.as_double_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(double_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("-42.100000", double_variant.value_to_string());
rcl_interfaces::msg::Parameter double_param = double_variant.to_parameter_msg();
EXPECT_EQ("double_param", double_param.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE, double_param.value.type);
EXPECT_EQ(TEST_VALUE, double_param.value.double_value);
// From parameter message
rclcpp::Parameter from_msg =
rclcpp::Parameter::from_parameter_msg(double_param);
EXPECT_EQ("double_param", from_msg.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_DOUBLE, from_msg.get_type());
EXPECT_EQ("double", from_msg.get_type_name());
EXPECT_EQ(TEST_VALUE,
from_msg.get_value<rclcpp::ParameterType::PARAMETER_DOUBLE>());
EXPECT_EQ(TEST_VALUE, from_msg.get_value_message().double_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE,
from_msg.get_value_message().type);
}
TEST(TestParameter, string_variant) {
const std::string TEST_VALUE {"ROS2"};
// Direct instantiation
rclcpp::Parameter string_variant("string_param", TEST_VALUE);
EXPECT_EQ("string_param", string_variant.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_STRING, string_variant.get_type());
EXPECT_EQ("string", string_variant.get_type_name());
EXPECT_EQ(TEST_VALUE,
string_variant.get_value<rclcpp::ParameterType::PARAMETER_STRING>());
EXPECT_EQ(TEST_VALUE, string_variant.get_value_message().string_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_STRING,
string_variant.get_value_message().type);
EXPECT_EQ(TEST_VALUE, string_variant.as_string());
EXPECT_THROW(string_variant.as_bool(), rclcpp::ParameterTypeException);
EXPECT_THROW(string_variant.as_int(), rclcpp::ParameterTypeException);
EXPECT_THROW(string_variant.as_double(), rclcpp::ParameterTypeException);
EXPECT_THROW(string_variant.as_byte_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(string_variant.as_bool_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(string_variant.as_integer_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(string_variant.as_double_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(string_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ(TEST_VALUE, string_variant.value_to_string());
rcl_interfaces::msg::Parameter string_param = string_variant.to_parameter_msg();
EXPECT_EQ("string_param", string_param.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_STRING, string_param.value.type);
EXPECT_EQ(TEST_VALUE, string_param.value.string_value);
// From parameter message
rclcpp::Parameter from_msg =
rclcpp::Parameter::from_parameter_msg(string_param);
EXPECT_EQ("string_param", from_msg.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_STRING, from_msg.get_type());
EXPECT_EQ("string", from_msg.get_type_name());
EXPECT_EQ(TEST_VALUE, from_msg.get_value<rclcpp::ParameterType::PARAMETER_STRING>());
EXPECT_EQ(TEST_VALUE, from_msg.get_value_message().string_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_STRING,
from_msg.get_value_message().type);
}
TEST(TestParameter, byte_array_variant) {
const std::vector<uint8_t> TEST_VALUE {0x52, 0x4f, 0x53, 0x32};
// Direct instantiation
rclcpp::Parameter byte_array_variant("byte_array_param", TEST_VALUE);
EXPECT_EQ("byte_array_param", byte_array_variant.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_BYTE_ARRAY, byte_array_variant.get_type());
EXPECT_EQ("byte_array", byte_array_variant.get_type_name());
EXPECT_EQ(TEST_VALUE,
byte_array_variant.get_value<rclcpp::ParameterType::PARAMETER_BYTE_ARRAY>());
EXPECT_EQ(TEST_VALUE, byte_array_variant.get_value_message().byte_array_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_BYTE_ARRAY,
byte_array_variant.get_value_message().type);
EXPECT_EQ(TEST_VALUE, byte_array_variant.as_byte_array());
EXPECT_THROW(byte_array_variant.as_bool(), rclcpp::ParameterTypeException);
EXPECT_THROW(byte_array_variant.as_int(), rclcpp::ParameterTypeException);
EXPECT_THROW(byte_array_variant.as_double(), rclcpp::ParameterTypeException);
EXPECT_THROW(byte_array_variant.as_string(), rclcpp::ParameterTypeException);
EXPECT_THROW(byte_array_variant.as_bool_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(byte_array_variant.as_integer_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(byte_array_variant.as_double_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(byte_array_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("[0x52, 0x4f, 0x53, 0x32]", byte_array_variant.value_to_string());
rcl_interfaces::msg::Parameter byte_array_param = byte_array_variant.to_parameter_msg();
EXPECT_EQ("byte_array_param", byte_array_param.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_BYTE_ARRAY, byte_array_param.value.type);
EXPECT_EQ(TEST_VALUE, byte_array_param.value.byte_array_value);
// From parameter message
rclcpp::Parameter from_msg =
rclcpp::Parameter::from_parameter_msg(byte_array_param);
EXPECT_EQ("byte_array_param", from_msg.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_BYTE_ARRAY, from_msg.get_type());
EXPECT_EQ("byte_array", from_msg.get_type_name());
EXPECT_EQ(TEST_VALUE,
from_msg.get_value<rclcpp::ParameterType::PARAMETER_BYTE_ARRAY>());
EXPECT_EQ(TEST_VALUE, from_msg.get_value_message().byte_array_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_BYTE_ARRAY,
from_msg.get_value_message().type);
}
TEST(TestParameter, bool_array_variant) {
const std::vector<bool> TEST_VALUE {false, true, true, false, false, true};
// Direct instantiation
rclcpp::Parameter bool_array_variant("bool_array_param", TEST_VALUE);
EXPECT_EQ("bool_array_param", bool_array_variant.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_BOOL_ARRAY, bool_array_variant.get_type());
EXPECT_EQ("bool_array", bool_array_variant.get_type_name());
EXPECT_EQ(TEST_VALUE,
bool_array_variant.get_value<rclcpp::ParameterType::PARAMETER_BOOL_ARRAY>());
EXPECT_EQ(TEST_VALUE, bool_array_variant.get_value_message().bool_array_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_BOOL_ARRAY,
bool_array_variant.get_value_message().type);
EXPECT_EQ(TEST_VALUE, bool_array_variant.as_bool_array());
EXPECT_THROW(bool_array_variant.as_bool(), rclcpp::ParameterTypeException);
EXPECT_THROW(bool_array_variant.as_int(), rclcpp::ParameterTypeException);
EXPECT_THROW(bool_array_variant.as_double(), rclcpp::ParameterTypeException);
EXPECT_THROW(bool_array_variant.as_string(), rclcpp::ParameterTypeException);
EXPECT_THROW(bool_array_variant.as_byte_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(bool_array_variant.as_integer_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(bool_array_variant.as_double_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(bool_array_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("[false, true, true, false, false, true]", bool_array_variant.value_to_string());
rcl_interfaces::msg::Parameter bool_array_param = bool_array_variant.to_parameter_msg();
EXPECT_EQ("bool_array_param", bool_array_param.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_BOOL_ARRAY, bool_array_param.value.type);
EXPECT_EQ(TEST_VALUE, bool_array_param.value.bool_array_value);
// From parameter message
rclcpp::Parameter from_msg =
rclcpp::Parameter::from_parameter_msg(bool_array_param);
EXPECT_EQ("bool_array_param", from_msg.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_BOOL_ARRAY, from_msg.get_type());
EXPECT_EQ("bool_array", from_msg.get_type_name());
EXPECT_EQ(TEST_VALUE,
from_msg.get_value<rclcpp::ParameterType::PARAMETER_BOOL_ARRAY>());
EXPECT_EQ(TEST_VALUE, from_msg.get_value_message().bool_array_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_BOOL_ARRAY,
from_msg.get_value_message().type);
}
TEST(TestParameter, integer_array_variant) {
const std::vector<int> TEST_VALUE
{42, -99, std::numeric_limits<int>::max(), std::numeric_limits<int>::lowest(), 0};
// Direct instantiation
rclcpp::Parameter integer_array_variant("integer_array_param", TEST_VALUE);
EXPECT_EQ("integer_array_param", integer_array_variant.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER_ARRAY,
integer_array_variant.get_type());
EXPECT_EQ("integer_array", integer_array_variant.get_type_name());
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY,
integer_array_variant.get_value_message().type);
// No direct comparison of vectors of ints and long ints
const auto & param_value_ref =
integer_array_variant.get_value<rclcpp::ParameterType::PARAMETER_INTEGER_ARRAY>();
auto mismatches = std::mismatch(TEST_VALUE.begin(), TEST_VALUE.end(), param_value_ref.begin());
EXPECT_EQ(TEST_VALUE.end(), mismatches.first);
EXPECT_EQ(param_value_ref.end(), mismatches.second);
auto param_value = integer_array_variant.get_value_message().integer_array_value;
mismatches = std::mismatch(TEST_VALUE.begin(), TEST_VALUE.end(), param_value.begin());
EXPECT_EQ(TEST_VALUE.end(), mismatches.first);
EXPECT_EQ(param_value.end(), mismatches.second);
param_value = integer_array_variant.as_integer_array();
mismatches = std::mismatch(TEST_VALUE.begin(), TEST_VALUE.end(), param_value.begin());
EXPECT_EQ(TEST_VALUE.end(), mismatches.first);
EXPECT_EQ(param_value.end(), mismatches.second);
EXPECT_THROW(integer_array_variant.as_bool(), rclcpp::ParameterTypeException);
EXPECT_THROW(integer_array_variant.as_int(), rclcpp::ParameterTypeException);
EXPECT_THROW(integer_array_variant.as_double(), rclcpp::ParameterTypeException);
EXPECT_THROW(integer_array_variant.as_string(), rclcpp::ParameterTypeException);
EXPECT_THROW(integer_array_variant.as_byte_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(integer_array_variant.as_bool_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(integer_array_variant.as_double_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(integer_array_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ(
"[42, -99, 2147483647, -2147483648, 0]",
integer_array_variant.value_to_string());
rcl_interfaces::msg::Parameter integer_array_param = integer_array_variant.to_parameter_msg();
EXPECT_EQ("integer_array_param", integer_array_param.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY,
integer_array_param.value.type);
param_value = integer_array_param.value.integer_array_value;
mismatches = std::mismatch(TEST_VALUE.begin(), TEST_VALUE.end(), param_value.begin());
EXPECT_EQ(TEST_VALUE.end(), mismatches.first);
EXPECT_EQ(param_value.end(), mismatches.second);
// From parameter message
rclcpp::Parameter from_msg =
rclcpp::Parameter::from_parameter_msg(integer_array_param);
EXPECT_EQ("integer_array_param", from_msg.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER_ARRAY, from_msg.get_type());
EXPECT_EQ("integer_array", from_msg.get_type_name());
param_value = from_msg.get_value<rclcpp::ParameterType::PARAMETER_INTEGER_ARRAY>();
mismatches = std::mismatch(TEST_VALUE.begin(), TEST_VALUE.end(), param_value.begin());
EXPECT_EQ(TEST_VALUE.end(), mismatches.first);
EXPECT_EQ(param_value.end(), mismatches.second);
param_value = from_msg.get_value_message().integer_array_value;
mismatches = std::mismatch(TEST_VALUE.begin(), TEST_VALUE.end(), param_value.begin());
EXPECT_EQ(TEST_VALUE.end(), mismatches.first);
EXPECT_EQ(param_value.end(), mismatches.second);
EXPECT_EQ(from_msg.get_value_message().type,
rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY);
}
TEST(TestParameter, long_integer_array_variant) {
const std::vector<int64_t> TEST_VALUE
{42, -99, std::numeric_limits<int64_t>::max(), std::numeric_limits<int64_t>::lowest(), 0};
rclcpp::Parameter long_array_variant("long_integer_array_param", TEST_VALUE);
EXPECT_EQ("long_integer_array_param", long_array_variant.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER_ARRAY,
long_array_variant.get_type());
EXPECT_EQ("integer_array", long_array_variant.get_type_name());
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY,
long_array_variant.get_value_message().type);
EXPECT_EQ(TEST_VALUE,
long_array_variant.get_value<rclcpp::ParameterType::PARAMETER_INTEGER_ARRAY>());
EXPECT_EQ(TEST_VALUE, long_array_variant.get_value_message().integer_array_value);
EXPECT_EQ(TEST_VALUE, long_array_variant.as_integer_array());
EXPECT_THROW(long_array_variant.as_bool(), rclcpp::ParameterTypeException);
EXPECT_THROW(long_array_variant.as_int(), rclcpp::ParameterTypeException);
EXPECT_THROW(long_array_variant.as_double(), rclcpp::ParameterTypeException);
EXPECT_THROW(long_array_variant.as_string(), rclcpp::ParameterTypeException);
EXPECT_THROW(long_array_variant.as_byte_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(long_array_variant.as_bool_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(long_array_variant.as_double_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(long_array_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ(
"[42, -99, 9223372036854775807, -9223372036854775808, 0]",
long_array_variant.value_to_string());
rcl_interfaces::msg::Parameter integer_array_param = long_array_variant.to_parameter_msg();
EXPECT_EQ("long_integer_array_param", integer_array_param.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY,
integer_array_param.value.type);
EXPECT_EQ(TEST_VALUE, integer_array_param.value.integer_array_value);
// From parameter message
rclcpp::Parameter from_msg =
rclcpp::Parameter::from_parameter_msg(integer_array_param);
EXPECT_EQ("long_integer_array_param", from_msg.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER_ARRAY, from_msg.get_type());
EXPECT_EQ("integer_array", from_msg.get_type_name());
EXPECT_EQ(TEST_VALUE,
from_msg.get_value<rclcpp::ParameterType::PARAMETER_INTEGER_ARRAY>());
EXPECT_EQ(TEST_VALUE, from_msg.get_value_message().integer_array_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY,
from_msg.get_value_message().type);
}
TEST(TestParameter, float_array_variant) {
const std::vector<float> TEST_VALUE
{42.1f, -99.1f, std::numeric_limits<float>::max(), std::numeric_limits<float>::lowest(), 0.1f};
// Direct instantiation
rclcpp::Parameter float_array_variant("float_array_param", TEST_VALUE);
EXPECT_EQ("float_array_param", float_array_variant.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_DOUBLE_ARRAY,
float_array_variant.get_type());
EXPECT_EQ("double_array", float_array_variant.get_type_name());
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY,
float_array_variant.get_value_message().type);
// No direct comparison of vectors of floats and doubles
const auto & param_value_ref =
float_array_variant.get_value<rclcpp::ParameterType::PARAMETER_DOUBLE_ARRAY>();
auto mismatches = std::mismatch(TEST_VALUE.begin(), TEST_VALUE.end(), param_value_ref.begin());
EXPECT_EQ(TEST_VALUE.end(), mismatches.first);
EXPECT_EQ(param_value_ref.end(), mismatches.second);
auto param_value = float_array_variant.get_value_message().double_array_value;
mismatches = std::mismatch(TEST_VALUE.begin(), TEST_VALUE.end(), param_value.begin());
EXPECT_EQ(TEST_VALUE.end(), mismatches.first);
EXPECT_EQ(param_value.end(), mismatches.second);
param_value = float_array_variant.as_double_array();
mismatches = std::mismatch(TEST_VALUE.begin(), TEST_VALUE.end(), param_value.begin());
EXPECT_EQ(TEST_VALUE.end(), mismatches.first);
EXPECT_EQ(param_value.end(), mismatches.second);
EXPECT_THROW(float_array_variant.as_bool(), rclcpp::ParameterTypeException);
EXPECT_THROW(float_array_variant.as_int(), rclcpp::ParameterTypeException);
EXPECT_THROW(float_array_variant.as_double(), rclcpp::ParameterTypeException);
EXPECT_THROW(float_array_variant.as_string(), rclcpp::ParameterTypeException);
EXPECT_THROW(float_array_variant.as_byte_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(float_array_variant.as_bool_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(float_array_variant.as_integer_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(float_array_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ(
"[42.1, -99.1, 3.40282e+38, -3.40282e+38, 0.1]",
float_array_variant.value_to_string());
rcl_interfaces::msg::Parameter float_array_param = float_array_variant.to_parameter_msg();
EXPECT_EQ("float_array_param", float_array_param.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY,
float_array_param.value.type);
param_value = float_array_param.value.double_array_value;
mismatches = std::mismatch(TEST_VALUE.begin(), TEST_VALUE.end(), param_value.begin());
EXPECT_EQ(TEST_VALUE.end(), mismatches.first);
EXPECT_EQ(param_value.end(), mismatches.second);
// From parameter message
rclcpp::Parameter from_msg =
rclcpp::Parameter::from_parameter_msg(float_array_param);
EXPECT_EQ("float_array_param", from_msg.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_DOUBLE_ARRAY, from_msg.get_type());
EXPECT_EQ("double_array", from_msg.get_type_name());
param_value = from_msg.get_value<rclcpp::ParameterType::PARAMETER_DOUBLE_ARRAY>();
mismatches = std::mismatch(TEST_VALUE.begin(), TEST_VALUE.end(), param_value.begin());
EXPECT_EQ(TEST_VALUE.end(), mismatches.first);
EXPECT_EQ(param_value.end(), mismatches.second);
param_value = from_msg.get_value_message().double_array_value;
mismatches = std::mismatch(TEST_VALUE.begin(), TEST_VALUE.end(), param_value.begin());
EXPECT_EQ(TEST_VALUE.end(), mismatches.first);
EXPECT_EQ(param_value.end(), mismatches.second);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY,
from_msg.get_value_message().type);
}
TEST(TestParameter, double_array_variant) {
const std::vector<double> TEST_VALUE
{42.1, -99.1, std::numeric_limits<double>::max(), std::numeric_limits<double>::lowest(), 0.1};
rclcpp::Parameter double_array_variant("double_array_param", TEST_VALUE);
EXPECT_EQ("double_array_param", double_array_variant.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_DOUBLE_ARRAY,
double_array_variant.get_type());
EXPECT_EQ("double_array", double_array_variant.get_type_name());
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY,
double_array_variant.get_value_message().type);
EXPECT_EQ(TEST_VALUE,
double_array_variant.get_value<rclcpp::ParameterType::PARAMETER_DOUBLE_ARRAY>());
EXPECT_EQ(TEST_VALUE, double_array_variant.get_value_message().double_array_value);
EXPECT_EQ(TEST_VALUE, double_array_variant.as_double_array());
EXPECT_THROW(double_array_variant.as_bool(), rclcpp::ParameterTypeException);
EXPECT_THROW(double_array_variant.as_int(), rclcpp::ParameterTypeException);
EXPECT_THROW(double_array_variant.as_double(), rclcpp::ParameterTypeException);
EXPECT_THROW(double_array_variant.as_string(), rclcpp::ParameterTypeException);
EXPECT_THROW(double_array_variant.as_byte_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(double_array_variant.as_bool_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(double_array_variant.as_integer_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(double_array_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ(
"[42.1, -99.1, 1.79769e+308, -1.79769e+308, 0.1]",
double_array_variant.value_to_string());
rcl_interfaces::msg::Parameter double_array_param = double_array_variant.to_parameter_msg();
EXPECT_EQ("double_array_param", double_array_param.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY,
double_array_param.value.type);
EXPECT_EQ(TEST_VALUE, double_array_param.value.double_array_value);
// From parameter message
rclcpp::Parameter from_msg =
rclcpp::Parameter::from_parameter_msg(double_array_param);
EXPECT_EQ("double_array_param", from_msg.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_DOUBLE_ARRAY, from_msg.get_type());
EXPECT_EQ("double_array", from_msg.get_type_name());
EXPECT_EQ(TEST_VALUE,
from_msg.get_value<rclcpp::ParameterType::PARAMETER_DOUBLE_ARRAY>());
EXPECT_EQ(TEST_VALUE, from_msg.get_value_message().double_array_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY,
from_msg.get_value_message().type);
}
TEST(TestParameter, string_array_variant) {
const std::vector<std::string> TEST_VALUE {"R", "O", "S2"};
// Direct instantiation
rclcpp::Parameter string_array_variant("string_array_param", TEST_VALUE);
EXPECT_EQ("string_array_param", string_array_variant.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_STRING_ARRAY,
string_array_variant.get_type());
EXPECT_EQ("string_array", string_array_variant.get_type_name());
EXPECT_EQ(TEST_VALUE,
string_array_variant.get_value<rclcpp::ParameterType::PARAMETER_STRING_ARRAY>());
EXPECT_EQ(TEST_VALUE, string_array_variant.get_value_message().string_array_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_STRING_ARRAY,
string_array_variant.get_value_message().type);
EXPECT_EQ(TEST_VALUE, string_array_variant.as_string_array());
EXPECT_THROW(string_array_variant.as_bool(), rclcpp::ParameterTypeException);
EXPECT_THROW(string_array_variant.as_int(), rclcpp::ParameterTypeException);
EXPECT_THROW(string_array_variant.as_double(), rclcpp::ParameterTypeException);
EXPECT_THROW(string_array_variant.as_string(), rclcpp::ParameterTypeException);
EXPECT_THROW(string_array_variant.as_byte_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(string_array_variant.as_bool_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(string_array_variant.as_integer_array(), rclcpp::ParameterTypeException);
EXPECT_THROW(string_array_variant.as_double_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("[R, O, S2]", string_array_variant.value_to_string());
rcl_interfaces::msg::Parameter string_array_param = string_array_variant.to_parameter_msg();
EXPECT_EQ("string_array_param", string_array_param.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_STRING_ARRAY,
string_array_param.value.type);
EXPECT_EQ(TEST_VALUE, string_array_param.value.string_array_value);
// From parameter message
rclcpp::Parameter from_msg =
rclcpp::Parameter::from_parameter_msg(string_array_param);
EXPECT_EQ("string_array_param", from_msg.get_name());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_STRING_ARRAY, from_msg.get_type());
EXPECT_EQ("string_array", from_msg.get_type_name());
EXPECT_EQ(TEST_VALUE,
from_msg.get_value<rclcpp::ParameterType::PARAMETER_STRING_ARRAY>());
EXPECT_EQ(TEST_VALUE, from_msg.get_value_message().string_array_value);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_STRING_ARRAY,
from_msg.get_value_message().type);
}

View File

@@ -0,0 +1,354 @@
// Copyright 2018 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 <rcl_yaml_param_parser/parser.h>
#include <rcutils/strdup.h>
#include <cstdio>
#include <string>
#include <vector>
#include "rclcpp/parameter_map.hpp"
rcl_params_t *
make_params(std::vector<std::string> node_names)
{
rcl_allocator_t alloc = rcl_get_default_allocator();
rcl_params_t * c_params = rcl_yaml_node_struct_init(alloc);
c_params->num_nodes = node_names.size();
c_params->allocator = alloc;
if (c_params->num_nodes) {
// Copy node names
for (size_t n = 0; n < node_names.size(); ++n) {
c_params->node_names[n] = static_cast<char *>(alloc.allocate(
sizeof(char) * (node_names[n].size() + 1), alloc.state));
std::snprintf(c_params->node_names[n], node_names[n].size() + 1, "%s", node_names[n].c_str());
}
// zero init node params
for (size_t n = 0; n < node_names.size(); ++n) {
c_params->params[n].parameter_names = NULL;
c_params->params[n].parameter_values = NULL;
c_params->params[n].num_params = 0;
}
}
return c_params;
}
void
make_node_params(rcl_params_t * c_params, size_t node_idx, std::vector<std::string> param_names)
{
rcl_allocator_t alloc = c_params->allocator;
ASSERT_LT(node_idx, c_params->num_nodes);
ASSERT_GT(param_names.size(), 0u);
rcl_node_params_s * c_node_params = &(c_params->params[node_idx]);
c_node_params->num_params = param_names.size();
// Copy parameter names
c_node_params->parameter_names = static_cast<char **>(
alloc.allocate(sizeof(char *) * param_names.size(), alloc.state));
for (size_t p = 0; p < param_names.size(); ++p) {
const std::string & param_name = param_names[p];
c_node_params->parameter_names[p] = static_cast<char *>(alloc.allocate(
sizeof(char) * (param_name.size() + 1), alloc.state));
std::snprintf(
c_node_params->parameter_names[p], param_name.size() + 1, "%s", param_name.c_str());
}
// zero init parameter value
c_node_params->parameter_values = static_cast<rcl_variant_t *>(alloc.allocate(
sizeof(rcl_variant_t) * param_names.size(), alloc.state));
for (size_t p = 0; p < param_names.size(); ++p) {
c_node_params->parameter_values[p].bool_value = NULL;
c_node_params->parameter_values[p].integer_value = NULL;
c_node_params->parameter_values[p].double_value = NULL;
c_node_params->parameter_values[p].string_value = NULL;
c_node_params->parameter_values[p].byte_array_value = NULL;
c_node_params->parameter_values[p].bool_array_value = NULL;
c_node_params->parameter_values[p].integer_array_value = NULL;
c_node_params->parameter_values[p].double_array_value = NULL;
c_node_params->parameter_values[p].string_array_value = NULL;
}
}
TEST(Test_parameter_map_from, null_c_parameter)
{
EXPECT_THROW(rclcpp::parameter_map_from(NULL), rclcpp::exceptions::InvalidParametersException);
}
TEST(Test_parameter_map_from, null_node_names)
{
rcl_params_t * c_params = make_params({});
c_params->num_nodes = 1;
EXPECT_THROW(
rclcpp::parameter_map_from(c_params), rclcpp::exceptions::InvalidParametersException);
c_params->num_nodes = 0;
rcl_yaml_node_struct_fini(c_params);
}
TEST(Test_parameter_map_from, null_node_params)
{
rcl_params_t * c_params = make_params({"foo"});
std::snprintf(c_params->node_names[0], 3 + 1, "foo");
auto allocated_params = c_params->params;
c_params->params = NULL;
EXPECT_THROW(
rclcpp::parameter_map_from(c_params), rclcpp::exceptions::InvalidParametersException);
c_params->params = allocated_params;
rcl_yaml_node_struct_fini(c_params);
}
TEST(Test_parameter_map_from, null_node_name_in_node_names)
{
rcl_params_t * c_params = make_params({"foo"});
auto allocated_name = c_params->node_names[0];
c_params->node_names[0] = NULL;
EXPECT_THROW(
rclcpp::parameter_map_from(c_params), rclcpp::exceptions::InvalidParametersException);
c_params->node_names[0] = allocated_name;
rcl_yaml_node_struct_fini(c_params);
}
TEST(Test_parameter_map_from, null_node_param_value)
{
rcl_params_t * c_params = make_params({"foo"});
make_node_params(c_params, 0, {"bar"});
EXPECT_THROW(
rclcpp::parameter_map_from(c_params), rclcpp::exceptions::InvalidParameterValueException);
rcl_yaml_node_struct_fini(c_params);
}
TEST(Test_parameter_map_from, null_node_param_name)
{
rcl_params_t * c_params = make_params({"foo"});
make_node_params(c_params, 0, {"bar"});
auto allocated_name = c_params->params[0].parameter_names[0];
c_params->params[0].parameter_names[0] = NULL;
EXPECT_THROW(
rclcpp::parameter_map_from(c_params), rclcpp::exceptions::InvalidParametersException);
c_params->params[0].parameter_names[0] = allocated_name;
rcl_yaml_node_struct_fini(c_params);
}
TEST(Test_parameter_map_from, bool_param_value)
{
rcl_params_t * c_params = make_params({"foo"});
make_node_params(c_params, 0, {"true_bool", "false_bool"});
bool true_bool = true;
bool false_bool = false;
c_params->params[0].parameter_values[0].bool_value = &true_bool;
c_params->params[0].parameter_values[1].bool_value = &false_bool;
rclcpp::ParameterMap map = rclcpp::parameter_map_from(c_params);
const std::vector<rclcpp::Parameter> & params = map.at("/foo");
EXPECT_STREQ("true_bool", params.at(0).get_name().c_str());
EXPECT_TRUE(params.at(0).get_value<bool>());
EXPECT_STREQ("false_bool", params.at(1).get_name().c_str());
EXPECT_FALSE(params.at(1).get_value<bool>());
c_params->params[0].parameter_values[0].bool_value = NULL;
c_params->params[0].parameter_values[1].bool_value = NULL;
rcl_yaml_node_struct_fini(c_params);
}
TEST(Test_parameter_map_from, integer_param_value)
{
rcl_params_t * c_params = make_params({"bar"});
make_node_params(c_params, 0, {"positive.int", "negative.int"});
int64_t positive_int = 42;
int64_t negative_int = -12345;
c_params->params[0].parameter_values[0].integer_value = &positive_int;
c_params->params[0].parameter_values[1].integer_value = &negative_int;
rclcpp::ParameterMap map = rclcpp::parameter_map_from(c_params);
const std::vector<rclcpp::Parameter> & params = map.at("/bar");
EXPECT_STREQ("positive.int", params.at(0).get_name().c_str());
EXPECT_EQ(42, params.at(0).get_value<int64_t>());
EXPECT_STREQ("negative.int", params.at(1).get_name().c_str());
EXPECT_EQ(-12345, params.at(1).get_value<int64_t>());
c_params->params[0].parameter_values[0].integer_value = NULL;
c_params->params[0].parameter_values[1].integer_value = NULL;
rcl_yaml_node_struct_fini(c_params);
}
TEST(Test_parameter_map_from, double_param_value)
{
rcl_params_t * c_params = make_params({"foo/bar"});
make_node_params(c_params, 0, {"positive.double", "negative.double"});
double positive_double = 3.14;
double negative_double = -2.718;
c_params->params[0].parameter_values[0].double_value = &positive_double;
c_params->params[0].parameter_values[1].double_value = &negative_double;
rclcpp::ParameterMap map = rclcpp::parameter_map_from(c_params);
const std::vector<rclcpp::Parameter> & params = map.at("/foo/bar");
EXPECT_STREQ("positive.double", params.at(0).get_name().c_str());
EXPECT_DOUBLE_EQ(3.14, params.at(0).get_value<double>());
EXPECT_STREQ("negative.double", params.at(1).get_name().c_str());
EXPECT_DOUBLE_EQ(-2.718, params.at(1).get_value<double>());
c_params->params[0].parameter_values[0].double_value = NULL;
c_params->params[0].parameter_values[1].double_value = NULL;
rcl_yaml_node_struct_fini(c_params);
}
TEST(Test_parameter_map_from, string_param_value)
{
rcl_params_t * c_params = make_params({"/foo/bar"});
make_node_params(c_params, 0, {"string_param"});
std::string hello_world = "hello world";
char * c_hello_world = new char[hello_world.length() + 1];
std::snprintf(c_hello_world, hello_world.size() + 1, "%s", hello_world.c_str());
c_params->params[0].parameter_values[0].string_value = c_hello_world;
rclcpp::ParameterMap map = rclcpp::parameter_map_from(c_params);
const std::vector<rclcpp::Parameter> & params = map.at("/foo/bar");
EXPECT_STREQ("string_param", params.at(0).get_name().c_str());
EXPECT_STREQ(hello_world.c_str(), params.at(0).get_value<std::string>().c_str());
c_params->params[0].parameter_values[0].string_value = NULL;
delete[] c_hello_world;
rcl_yaml_node_struct_fini(c_params);
}
#define MAKE_ARRAY_VALUE(VAR, TYPE, V1, V2) \
do { \
VAR.values = new TYPE[2]; \
VAR.size = 2; \
VAR.values[0] = V1; \
VAR.values[1] = V2; \
} while (false)
#define FREE_ARRAY_VALUE(VAR) \
do { \
delete[] VAR.values; \
} while (false)
TEST(Test_parameter_map_from, byte_array_param_value)
{
rcl_params_t * c_params = make_params({"/foobar"});
make_node_params(c_params, 0, {"byte_array_param"});
rcl_byte_array_t c_byte_array;
MAKE_ARRAY_VALUE(c_byte_array, uint8_t, 0xf0, 0xaa);
c_params->params[0].parameter_values[0].byte_array_value = &c_byte_array;
rclcpp::ParameterMap map = rclcpp::parameter_map_from(c_params);
const std::vector<rclcpp::Parameter> & params = map.at("/foobar");
EXPECT_STREQ("byte_array_param", params.at(0).get_name().c_str());
std::vector<uint8_t> byte_array = params.at(0).get_value<std::vector<uint8_t>>();
ASSERT_EQ(2u, byte_array.size());
EXPECT_EQ(0xf0, byte_array.at(0));
EXPECT_EQ(0xaa, byte_array.at(1));
c_params->params[0].parameter_values[0].byte_array_value = NULL;
FREE_ARRAY_VALUE(c_byte_array);
rcl_yaml_node_struct_fini(c_params);
}
TEST(Test_parameter_map_from, bool_array_param_value)
{
rcl_params_t * c_params = make_params({"foo/bar/baz"});
make_node_params(c_params, 0, {"bool_array_param"});
rcl_bool_array_t c_bool_array;
MAKE_ARRAY_VALUE(c_bool_array, bool, true, false);
c_params->params[0].parameter_values[0].bool_array_value = &c_bool_array;
rclcpp::ParameterMap map = rclcpp::parameter_map_from(c_params);
const std::vector<rclcpp::Parameter> & params = map.at("/foo/bar/baz");
EXPECT_STREQ("bool_array_param", params.at(0).get_name().c_str());
std::vector<bool> bool_array = params.at(0).get_value<std::vector<bool>>();
ASSERT_EQ(2u, bool_array.size());
EXPECT_TRUE(bool_array.at(0));
EXPECT_FALSE(bool_array.at(1));
c_params->params[0].parameter_values[0].bool_array_value = NULL;
FREE_ARRAY_VALUE(c_bool_array);
rcl_yaml_node_struct_fini(c_params);
}
TEST(Test_parameter_map_from, integer_array_param_value)
{
rcl_params_t * c_params = make_params({"foo"});
make_node_params(c_params, 0, {"integer_array_param"});
rcl_int64_array_t c_integer_array;
MAKE_ARRAY_VALUE(c_integer_array, int64_t, 42, -12345);
c_params->params[0].parameter_values[0].integer_array_value = &c_integer_array;
rclcpp::ParameterMap map = rclcpp::parameter_map_from(c_params);
const std::vector<rclcpp::Parameter> & params = map.at("/foo");
EXPECT_STREQ("integer_array_param", params.at(0).get_name().c_str());
std::vector<int64_t> integer_array = params.at(0).get_value<std::vector<int64_t>>();
ASSERT_EQ(2u, integer_array.size());
EXPECT_EQ(42, integer_array.at(0));
EXPECT_EQ(-12345, integer_array.at(1));
c_params->params[0].parameter_values[0].integer_array_value = NULL;
FREE_ARRAY_VALUE(c_integer_array);
rcl_yaml_node_struct_fini(c_params);
}
TEST(Test_parameter_map_from, double_array_param_value)
{
rcl_params_t * c_params = make_params({"foo"});
make_node_params(c_params, 0, {"double_array_param"});
rcl_double_array_t c_double_array;
MAKE_ARRAY_VALUE(c_double_array, double, 3.14, -2.718);
c_params->params[0].parameter_values[0].double_array_value = &c_double_array;
rclcpp::ParameterMap map = rclcpp::parameter_map_from(c_params);
const std::vector<rclcpp::Parameter> & params = map.at("/foo");
EXPECT_STREQ("double_array_param", params.at(0).get_name().c_str());
std::vector<double> double_array = params.at(0).get_value<std::vector<double>>();
ASSERT_EQ(2u, double_array.size());
EXPECT_DOUBLE_EQ(3.14, double_array.at(0));
EXPECT_DOUBLE_EQ(-2.718, double_array.at(1));
c_params->params[0].parameter_values[0].double_array_value = NULL;
FREE_ARRAY_VALUE(c_double_array);
rcl_yaml_node_struct_fini(c_params);
}
TEST(Test_parameter_map_from, string_array_param_value)
{
rcl_params_t * c_params = make_params({"foo"});
make_node_params(c_params, 0, {"string_array_param"});
rcutils_string_array_t c_string_array = rcutils_get_zero_initialized_string_array();
ASSERT_EQ(RCUTILS_RET_OK, rcutils_string_array_init(&c_string_array, 2, &(c_params->allocator)));
c_string_array.data[0] = rcutils_strdup("Hello", c_params->allocator);
c_string_array.data[1] = rcutils_strdup("World", c_params->allocator);
c_params->params[0].parameter_values[0].string_array_value = &c_string_array;
rclcpp::ParameterMap map = rclcpp::parameter_map_from(c_params);
const std::vector<rclcpp::Parameter> & params = map.at("/foo");
EXPECT_STREQ("string_array_param", params.at(0).get_name().c_str());
std::vector<std::string> string_array = params.at(0).get_value<std::vector<std::string>>();
ASSERT_EQ(2u, string_array.size());
EXPECT_STREQ("Hello", string_array.at(0).c_str());
EXPECT_STREQ("World", string_array.at(1).c_str());
EXPECT_EQ(RCUTILS_RET_OK, rcutils_string_array_fini(&c_string_array));
c_params->params[0].parameter_values[0].string_array_value = NULL;
rcl_yaml_node_struct_fini(c_params);
}

View File

@@ -0,0 +1,67 @@
// Copyright 2018 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/rclcpp.hpp"
#include "rcl/types.h"
#include "test_msgs/msg/empty.hpp"
TEST(TestSerializedMessageAllocator, default_allocator) {
using DummyMessageT = float;
auto mem_strategy =
rclcpp::message_memory_strategy::MessageMemoryStrategy<DummyMessageT>::create_default();
auto msg0 = mem_strategy->borrow_serialized_message();
ASSERT_EQ(msg0->buffer_capacity, 0u);
mem_strategy->return_serialized_message(msg0);
auto msg100 = mem_strategy->borrow_serialized_message(100);
ASSERT_EQ(msg100->buffer_capacity, 100u);
mem_strategy->return_serialized_message(msg100);
auto msg200 = mem_strategy->borrow_serialized_message();
auto ret = rmw_serialized_message_resize(msg200.get(), 200);
ASSERT_EQ(RCL_RET_OK, ret);
EXPECT_EQ(0u, msg200->buffer_length);
EXPECT_EQ(200u, msg200->buffer_capacity);
mem_strategy->return_serialized_message(msg200);
auto msg1000 = mem_strategy->borrow_serialized_message(1000);
ASSERT_EQ(msg1000->buffer_capacity, 1000u);
ret = rmw_serialized_message_resize(msg1000.get(), 2000);
ASSERT_EQ(RCL_RET_OK, ret);
EXPECT_EQ(2000u, msg1000->buffer_capacity);
mem_strategy->return_serialized_message(msg1000);
}
TEST(TestSerializedMessageAllocator, borrow_from_subscription) {
rclcpp::init(0, NULL);
auto node = std::make_shared<rclcpp::Node>("test_serialized_message_allocator_node");
std::shared_ptr<rclcpp::SubscriptionBase> sub =
node->create_subscription<test_msgs::msg::Empty>("~/dummy_topic", [](
std::shared_ptr<test_msgs::msg::Empty> test_msg) {(void) test_msg;});
auto msg0 = sub->create_serialized_message();
EXPECT_EQ(0u, msg0->buffer_capacity);
sub->return_serialized_message(msg0);
rclcpp::shutdown();
}

View File

@@ -24,6 +24,12 @@
class TestSubscription : public ::testing::Test
{
public:
void OnMessage(const rcl_interfaces::msg::IntraProcessMessage::SharedPtr msg)
{
(void)msg;
}
protected:
static void SetUpTestCase()
{
@@ -32,7 +38,7 @@ protected:
void SetUp()
{
node = std::make_shared<rclcpp::Node>("my_node", "/ns");
node = std::make_shared<rclcpp::Node>("test_subscription", "/ns");
}
void TearDown()
@@ -43,6 +49,45 @@ protected:
rclcpp::Node::SharedPtr node;
};
class SubscriptionClassNodeInheritance : public rclcpp::Node
{
public:
SubscriptionClassNodeInheritance()
: Node("subscription_class_node_inheritance")
{
}
void OnMessage(const rcl_interfaces::msg::IntraProcessMessage::SharedPtr msg)
{
(void)msg;
}
void CreateSubscription()
{
auto callback = std::bind(
&SubscriptionClassNodeInheritance::OnMessage, this, std::placeholders::_1);
using rcl_interfaces::msg::IntraProcessMessage;
auto sub = this->create_subscription<IntraProcessMessage>("topic", callback);
}
};
class SubscriptionClass
{
public:
void OnMessage(const rcl_interfaces::msg::IntraProcessMessage::SharedPtr msg)
{
(void)msg;
}
void CreateSubscription()
{
auto node = std::make_shared<rclcpp::Node>("test_subscription_member_callback", "/ns");
auto callback = std::bind(&SubscriptionClass::OnMessage, this, std::placeholders::_1);
using rcl_interfaces::msg::IntraProcessMessage;
auto sub = node->create_subscription<IntraProcessMessage>("topic", callback);
}
};
/*
Testing subscription construction and destruction.
*/
@@ -61,3 +106,27 @@ TEST_F(TestSubscription, construction_and_destruction) {
}, rclcpp::exceptions::InvalidTopicNameError);
}
}
/*
Testing subscriptions using std::bind.
*/
TEST_F(TestSubscription, callback_bind) {
using rcl_interfaces::msg::IntraProcessMessage;
{
// Member callback for plain class
SubscriptionClass subscriptionObject;
subscriptionObject.CreateSubscription();
}
{
// Member callback for class inheriting from rclcpp::Node
SubscriptionClassNodeInheritance subscriptionObject;
subscriptionObject.CreateSubscription();
}
{
// Member callback for class inheriting from testing::Test
// Regression test for https://github.com/ros2/rclcpp/issues/479 where the TEST_F GTest macro
// was interfering with rclcpp's `function_traits`.
auto callback = std::bind(&TestSubscription::OnMessage, this, std::placeholders::_1);
auto sub = node->create_subscription<IntraProcessMessage>("topic", callback);
}
}

View File

@@ -0,0 +1,178 @@
// 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 <functional>
#include <memory>
#include <string>
#include "rcl/types.h"
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/subscription_traits.hpp"
#include "test_msgs/msg/empty.hpp"
void serialized_callback_copy(rcl_serialized_message_t unused)
{
(void) unused;
}
void serialized_callback_shared_ptr(std::shared_ptr<rcl_serialized_message_t> unused)
{
(void) unused;
}
void not_serialized_callback(char * unused)
{
(void) unused;
}
void not_serialized_shared_ptr_callback(std::shared_ptr<char> unused)
{
(void) unused;
}
void not_serialized_unique_ptr_callback(
test_msgs::msg::Empty::UniquePtrWithDeleter<rclcpp::allocator::Deleter<std::allocator<void>,
test_msgs::msg::Empty>> unused)
{
(void) unused;
}
TEST(TestSubscriptionTraits, is_serialized_callback) {
// Test regular functions
auto cb1 = &serialized_callback_copy;
static_assert(
rclcpp::subscription_traits::is_serialized_callback<decltype(cb1)>::value == true,
"rcl_serialized_message_t in a first argument callback makes it a serialized callback");
auto cb2 = &serialized_callback_shared_ptr;
static_assert(
rclcpp::subscription_traits::is_serialized_callback<decltype(cb2)>::value == true,
"std::shared_ptr<rcl_serialized_message_t> in a callback makes it a serialized callback");
auto cb3 = &not_serialized_callback;
static_assert(
rclcpp::subscription_traits::is_serialized_callback<decltype(cb3)>::value == false,
"passing a char * is not a serialized callback");
auto cb4 = &not_serialized_shared_ptr_callback;
static_assert(
rclcpp::subscription_traits::is_serialized_callback<decltype(cb4)>::value == false,
"passing a std::shared_tr<char> is not a serialized callback");
auto cb5 = [](rcl_serialized_message_t unused) -> void
{
(void) unused;
};
static_assert(
rclcpp::subscription_traits::is_serialized_callback<decltype(cb5)>::value == true,
"rcl_serialized_message_t in a first argument callback makes it a serialized callback");
using MessageT = test_msgs::msg::Empty;
using MessageTAllocator = std::allocator<void>;
using MessageTDeallocator = rclcpp::allocator::Deleter<MessageTAllocator, MessageT>;
auto cb6 = [](MessageT::UniquePtrWithDeleter<MessageTDeallocator> unique_msg_ptr) -> void
{
(void) unique_msg_ptr;
};
static_assert(
rclcpp::subscription_traits::is_serialized_callback<decltype(cb6)>::value == false,
"passing a std::unique_ptr of test_msgs::msg::Empty is not a serialized callback");
auto cb7 = &not_serialized_unique_ptr_callback;
static_assert(
rclcpp::subscription_traits::is_serialized_callback<decltype(cb7)>::value == false,
"passing a fancy unique_ptr of test_msgs::msg::Empty is not a serialized callback");
}
TEST(TestSubscriptionTraits, callback_messages) {
static_assert(
std::is_same<
std::shared_ptr<char>,
rclcpp::function_traits::function_traits<
decltype(not_serialized_shared_ptr_callback)
>::template argument_type<0>
>::value, "wrong!");
static_assert(
std::is_same<
char,
rclcpp::subscription_traits::extract_message_type<
rclcpp::function_traits::function_traits<
decltype(not_serialized_shared_ptr_callback)
>::template argument_type<0>
>::type
>::value, "wrong!");
auto cb1 = &serialized_callback_copy;
static_assert(
std::is_same<
rcl_serialized_message_t,
rclcpp::subscription_traits::has_message_type<decltype(cb1)>::type>::value,
"serialized callback message type is rcl_serialized_message_t");
auto cb2 = &serialized_callback_shared_ptr;
static_assert(
std::is_same<
rcl_serialized_message_t,
rclcpp::subscription_traits::has_message_type<decltype(cb2)>::type>::value,
"serialized callback message type is rcl_serialized_message_t");
auto cb3 = &not_serialized_callback;
static_assert(
std::is_same<
char *,
rclcpp::subscription_traits::has_message_type<decltype(cb3)>::type>::value,
"not serialized callback message type is char");
auto cb4 = &not_serialized_shared_ptr_callback;
static_assert(
std::is_same<
char,
rclcpp::subscription_traits::has_message_type<decltype(cb4)>::type>::value,
"not serialized shared_ptr callback message type is std::shared_ptr<char>");
auto cb5 = [](rcl_serialized_message_t unused) -> void
{
(void) unused;
};
static_assert(
std::is_same<
rcl_serialized_message_t,
rclcpp::subscription_traits::has_message_type<decltype(cb5)>::type>::value,
"serialized callback message type is rcl_serialized_message_t");
using MessageT = test_msgs::msg::Empty;
using MessageTAllocator = std::allocator<MessageT>;
using MessageTDeallocator = rclcpp::allocator::Deleter<MessageTAllocator, MessageT>;
auto cb6 = [](std::unique_ptr<MessageT, MessageTDeallocator> unique_msg_ptr) -> void
{
(void) unique_msg_ptr;
};
static_assert(
std::is_same<
test_msgs::msg::Empty,
rclcpp::subscription_traits::has_message_type<decltype(cb6)>::type>::value,
"passing a std::unique_ptr of test_msgs::msg::Empty has message type Empty");
auto cb7 = &not_serialized_unique_ptr_callback;
static_assert(
std::is_same<
test_msgs::msg::Empty,
rclcpp::subscription_traits::has_message_type<decltype(cb7)>::type>::value,
"passing a fancy std::unique_ptr of test_msgs::msg::Empty has message type Empty");
}

View File

@@ -23,6 +23,18 @@
#include "rclcpp/clock.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/time.hpp"
#include "rclcpp/utilities.hpp"
namespace
{
bool logical_eq(const bool a, const bool b)
{
return (a && b) || ((!a) && !(b));
}
} // namespace
class TestTime : public ::testing::Test
{
@@ -51,7 +63,7 @@ TEST(TestTime, time_sources) {
EXPECT_NE(0, ros_now.sec);
EXPECT_NE(0u, ros_now.nanosec);
rclcpp::Clock system_clock(RCL_ROS_TIME);
rclcpp::Clock system_clock(RCL_SYSTEM_TIME);
Time system_now = system_clock.now();
EXPECT_NE(0, system_now.sec);
EXPECT_NE(0u, system_now.nanosec);
@@ -63,7 +75,7 @@ TEST(TestTime, time_sources) {
}
TEST(TestTime, conversions) {
rclcpp::Clock system_clock(RCL_ROS_TIME);
rclcpp::Clock system_clock(RCL_SYSTEM_TIME);
rclcpp::Time now = system_clock.now();
builtin_interfaces::msg::Time now_msg = now;
@@ -77,9 +89,9 @@ TEST(TestTime, conversions) {
rclcpp::Time time = msg;
EXPECT_EQ(
RCL_S_TO_NS(static_cast<uint64_t>(msg.sec)) + static_cast<uint64_t>(msg.nanosec),
RCL_S_TO_NS(static_cast<int64_t>(msg.sec)) + static_cast<int64_t>(msg.nanosec),
time.nanoseconds());
EXPECT_EQ(static_cast<uint64_t>(msg.sec), RCL_NS_TO_S(time.nanoseconds()));
EXPECT_EQ(static_cast<int64_t>(msg.sec), RCL_NS_TO_S(time.nanoseconds()));
builtin_interfaces::msg::Time negative_time_msg;
negative_time_msg.sec = -1;
@@ -123,7 +135,7 @@ TEST(TestTime, operators) {
EXPECT_ANY_THROW((void)(system_time > steady_time));
EXPECT_ANY_THROW((void)(system_time - steady_time));
rclcpp::Clock system_clock(RCL_ROS_TIME);
rclcpp::Clock system_clock(RCL_SYSTEM_TIME);
rclcpp::Clock steady_clock(RCL_STEADY_TIME);
rclcpp::Time now = system_clock.now();
@@ -148,11 +160,93 @@ TEST(TestTime, operators) {
}
}
TEST(TestTime, overflow_detectors) {
/////////////////////////////////////////////////////////////////////////////
// Test logical_eq call first:
EXPECT_TRUE(logical_eq(false, false));
EXPECT_FALSE(logical_eq(false, true));
EXPECT_FALSE(logical_eq(true, false));
EXPECT_TRUE(logical_eq(true, true));
/////////////////////////////////////////////////////////////////////////////
// Exhaustive test of all int8_t values
using test_type_t = int8_t;
// big_type_t encompasses test_type_t:
// big_type_t::min < test_type_t::min
// big_type_t::max > test_type_t::max
using big_type_t = int16_t;
const big_type_t min_val = std::numeric_limits<test_type_t>::min();
const big_type_t max_val = std::numeric_limits<test_type_t>::max();
// 256 * 256 = 64K total loops, should be pretty fast on everything
for (big_type_t y = min_val; y <= max_val; ++y) {
for (big_type_t x = min_val; x <= max_val; ++x) {
const big_type_t sum = x + y;
const big_type_t diff = x - y;
const bool add_will_overflow =
rclcpp::add_will_overflow(test_type_t(x), test_type_t(y));
const bool add_did_overflow = sum > max_val;
EXPECT_TRUE(logical_eq(add_will_overflow, add_did_overflow));
const bool add_will_underflow =
rclcpp::add_will_underflow(test_type_t(x), test_type_t(y));
const bool add_did_underflow = sum < min_val;
EXPECT_TRUE(logical_eq(add_will_underflow, add_did_underflow));
const bool sub_will_overflow =
rclcpp::sub_will_overflow(test_type_t(x), test_type_t(y));
const bool sub_did_overflow = diff > max_val;
EXPECT_TRUE(logical_eq(sub_will_overflow, sub_did_overflow));
const bool sub_will_underflow =
rclcpp::sub_will_underflow(test_type_t(x), test_type_t(y));
const bool sub_did_underflow = diff < min_val;
EXPECT_TRUE(logical_eq(sub_will_underflow, sub_did_underflow));
}
}
// Few selected tests for int64_t
EXPECT_TRUE(rclcpp::add_will_overflow<int64_t>(INT64_MAX, 1));
EXPECT_FALSE(rclcpp::add_will_overflow<int64_t>(INT64_MAX, -1));
EXPECT_TRUE(rclcpp::add_will_underflow<int64_t>(INT64_MIN, -1));
EXPECT_FALSE(rclcpp::add_will_underflow<int64_t>(INT64_MIN, 1));
EXPECT_FALSE(rclcpp::sub_will_overflow<int64_t>(INT64_MAX, 1));
EXPECT_TRUE(rclcpp::sub_will_overflow<int64_t>(INT64_MAX, -1));
EXPECT_FALSE(rclcpp::sub_will_underflow<int64_t>(INT64_MIN, -1));
EXPECT_TRUE(rclcpp::sub_will_underflow<int64_t>(INT64_MIN, 1));
}
TEST(TestTime, overflows) {
rclcpp::Time max_time(std::numeric_limits<rcl_time_point_value_t>::max());
rclcpp::Time min_time(std::numeric_limits<rcl_time_point_value_t>::min());
rclcpp::Duration one(1);
rclcpp::Duration two(2);
// Cross min/max
EXPECT_THROW(max_time + one, std::overflow_error);
EXPECT_THROW(min_time - one, std::underflow_error);
EXPECT_THROW(max_time - min_time, std::overflow_error);
EXPECT_THROW(min_time - max_time, std::underflow_error);
EXPECT_NO_THROW(max_time - max_time);
EXPECT_NO_THROW(min_time - min_time);
// Cross zero in both directions
rclcpp::Time one_time(1);
EXPECT_NO_THROW(one_time - two);
rclcpp::Time minus_one_time(-1);
EXPECT_NO_THROW(minus_one_time + two);
EXPECT_NO_THROW(one_time - minus_one_time);
EXPECT_NO_THROW(minus_one_time - one_time);
rclcpp::Time two_time(2);
EXPECT_NO_THROW(one_time - two_time);
}
TEST(TestTime, seconds) {
EXPECT_DOUBLE_EQ(0.0, rclcpp::Time(0, 0).seconds());
EXPECT_DOUBLE_EQ(4.5, rclcpp::Time(4, 500000000).seconds());
EXPECT_DOUBLE_EQ(2.5, rclcpp::Time(0, 2500000000).seconds());
}

View File

@@ -50,6 +50,46 @@ protected:
rclcpp::Node::SharedPtr node;
};
void trigger_clock_changes(
rclcpp::Node::SharedPtr node)
{
auto clock_pub = node->create_publisher<rosgraph_msgs::msg::Clock>("clock",
rmw_qos_profile_default);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
rclcpp::WallRate loop_rate(50);
for (int i = 0; i < 5; ++i) {
if (!rclcpp::ok()) {
break; // Break for ctrl-c
}
auto msg = std::make_shared<rosgraph_msgs::msg::Clock>();
msg->clock.sec = i;
msg->clock.nanosec = 1000;
clock_pub->publish(msg);
executor.spin_once(1000000ns);
loop_rate.sleep();
}
}
void set_use_sim_time_parameter(rclcpp::Node::SharedPtr node, rclcpp::ParameterValue value)
{
auto parameters_client = std::make_shared<rclcpp::SyncParametersClient>(node);
using namespace std::chrono_literals;
EXPECT_TRUE(parameters_client->wait_for_service(2s));
auto set_parameters_results = parameters_client->set_parameters({
rclcpp::Parameter("use_sim_time", value)
});
for (auto & result : set_parameters_results) {
EXPECT_TRUE(result.successful);
}
// SyncParametersClient returns when parameters have been set on the node_parameters interface,
// but it doesn't mean the on_parameter_event subscription in TimeSource has been called.
// Spin some to handle that subscription.
rclcpp::spin_some(node);
}
TEST_F(TestTimeSource, detachUnattached) {
rclcpp::TimeSource ts;
@@ -93,30 +133,24 @@ TEST_F(TestTimeSource, clock) {
ts.attachClock(ros_clock);
EXPECT_FALSE(ros_clock->ros_time_is_active());
auto clock_pub = node->create_publisher<builtin_interfaces::msg::Time>("clock",
rmw_qos_profile_default);
rclcpp::WallRate loop_rate(50);
for (int i = 0; i < 5; ++i) {
if (!rclcpp::ok()) {
break; // Break for ctrl-c
}
auto msg = std::make_shared<builtin_interfaces::msg::Time>();
msg->sec = i;
msg->nanosec = 1000;
clock_pub->publish(msg);
// std::cout << "Publishing: '" << msg->sec << ".000000" << msg->nanosec << "'" << std::endl;
rclcpp::spin_some(node);
loop_rate.sleep();
}
auto t_low = rclcpp::Time(1, 0, RCL_ROS_TIME);
auto t_high = rclcpp::Time(10, 100000, RCL_ROS_TIME);
trigger_clock_changes(node);
// Now that we've recieved a message it should be active with parameter unset
// Even now that we've recieved a message, ROS time should still not be active since the
// parameter has not been explicitly set.
EXPECT_FALSE(ros_clock->ros_time_is_active());
// Activate ROS time.
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true));
EXPECT_TRUE(ros_clock->ros_time_is_active());
trigger_clock_changes(node);
auto t_out = ros_clock->now();
EXPECT_NE(0UL, t_out.nanoseconds());
// Time from clock should now reflect what was published on the /clock topic.
auto t_low = rclcpp::Time(1, 0, RCL_ROS_TIME);
auto t_high = rclcpp::Time(10, 100000, RCL_ROS_TIME);
EXPECT_NE(0L, t_out.nanoseconds());
EXPECT_LT(t_low.nanoseconds(), t_out.nanoseconds());
EXPECT_GT(t_high.nanoseconds(), t_out.nanoseconds());
}
@@ -124,27 +158,30 @@ TEST_F(TestTimeSource, clock) {
class CallbackObject
{
public:
CallbackObject()
: last_precallback_id_(0),
last_postcallback_id_(0)
{}
int last_precallback_id_;
void pre_callback(int id) {last_precallback_id_ = id;}
int pre_callback_calls_ = 0;
int last_precallback_id_ = 0;
void pre_callback(int id)
{
last_precallback_id_ = id;
++pre_callback_calls_;
}
int last_postcallback_id_;
rclcpp::TimeJump last_timejump_;
void post_callback(const rclcpp::TimeJump & jump, int id)
int post_callback_calls_ = 0;
int last_postcallback_id_ = 0;
rcl_time_jump_t last_timejump_;
void post_callback(const rcl_time_jump_t & jump, int id)
{
last_postcallback_id_ = id; last_timejump_ = jump;
++post_callback_calls_;
}
};
TEST_F(TestTimeSource, callbacks) {
CallbackObject cbo;
rclcpp::JumpThreshold jump_threshold;
jump_threshold.min_forward_ = 0;
jump_threshold.min_backward_ = 0;
jump_threshold.on_clock_change_ = true;
rcl_jump_threshold_t jump_threshold;
jump_threshold.min_forward.nanoseconds = 0;
jump_threshold.min_backward.nanoseconds = 0;
jump_threshold.on_clock_change = true;
rclcpp::TimeSource ts(node);
auto ros_clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
@@ -163,37 +200,29 @@ TEST_F(TestTimeSource, callbacks) {
ts.attachClock(ros_clock);
EXPECT_FALSE(ros_clock->ros_time_is_active());
auto clock_pub = node->create_publisher<builtin_interfaces::msg::Time>("clock",
rmw_qos_profile_default);
rclcpp::WallRate loop_rate(50);
for (int i = 0; i < 5; ++i) {
if (!rclcpp::ok()) {
break; // Break for ctrl-c
}
auto msg = std::make_shared<builtin_interfaces::msg::Time>();
msg->sec = i;
msg->nanosec = 1000;
clock_pub->publish(msg);
// std::cout << "Publishing: '" << msg->sec << ".000000" << msg->nanosec << "'" << std::endl;
rclcpp::spin_some(node);
loop_rate.sleep();
}
trigger_clock_changes(node);
auto t_low = rclcpp::Time(1, 0, RCL_ROS_TIME);
auto t_high = rclcpp::Time(10, 100000, RCL_ROS_TIME);
EXPECT_EQ(1, cbo.last_precallback_id_);
EXPECT_EQ(1, cbo.last_postcallback_id_);
// Callbacks will not be triggered since ROS time is not active.
EXPECT_EQ(0, cbo.last_precallback_id_);
EXPECT_EQ(0, cbo.last_postcallback_id_);
// Now that we've recieved a message it should be active with parameter unset
// Activate ROS time.
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true));
EXPECT_TRUE(ros_clock->ros_time_is_active());
trigger_clock_changes(node);
auto t_out = ros_clock->now();
EXPECT_NE(0UL, t_out.nanoseconds());
EXPECT_NE(0L, t_out.nanoseconds());
EXPECT_LT(t_low.nanoseconds(), t_out.nanoseconds());
EXPECT_GT(t_high.nanoseconds(), t_out.nanoseconds());
// Callbacks will now have been triggered since ROS time is active.
EXPECT_EQ(1, cbo.last_precallback_id_);
EXPECT_EQ(1, cbo.last_postcallback_id_);
// Change callbacks
rclcpp::JumpHandler::SharedPtr callback_handler2 = ros_clock->create_jump_callback(
@@ -201,63 +230,47 @@ TEST_F(TestTimeSource, callbacks) {
std::bind(&CallbackObject::post_callback, &cbo, std::placeholders::_1, 2),
jump_threshold);
for (int i = 0; i < 5; ++i) {
if (!rclcpp::ok()) {
break; // Break for ctrl-c
}
auto msg = std::make_shared<builtin_interfaces::msg::Time>();
msg->sec = i;
msg->nanosec = 2000;
clock_pub->publish(msg);
// std::cout << "Publishing: '" << msg->sec << ".000000" << msg->nanosec << "'" << std::endl;
rclcpp::spin_some(node);
loop_rate.sleep();
}
trigger_clock_changes(node);
EXPECT_EQ(2, cbo.last_precallback_id_);
EXPECT_EQ(2, cbo.last_postcallback_id_);
// Now that we've recieved a message it should be active with parameter unset
EXPECT_TRUE(ros_clock->ros_time_is_active());
t_out = ros_clock->now();
EXPECT_NE(0UL, t_out.nanoseconds());
EXPECT_NE(0L, t_out.nanoseconds());
EXPECT_LT(t_low.nanoseconds(), t_out.nanoseconds());
EXPECT_GT(t_high.nanoseconds(), t_out.nanoseconds());
}
void trigger_clock_changes(
rclcpp::Node::SharedPtr node)
{
auto clock_pub = node->create_publisher<builtin_interfaces::msg::Time>("clock",
rmw_qos_profile_default);
// Register a callback handler with only pre_callback
rclcpp::JumpHandler::SharedPtr callback_handler3 = ros_clock->create_jump_callback(
std::bind(&CallbackObject::pre_callback, &cbo, 3),
std::function<void(rcl_time_jump_t)>(),
jump_threshold);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
trigger_clock_changes(node);
EXPECT_EQ(3, cbo.last_precallback_id_);
EXPECT_EQ(2, cbo.last_postcallback_id_);
rclcpp::WallRate loop_rate(50);
for (int i = 0; i < 5; ++i) {
if (!rclcpp::ok()) {
break; // Break for ctrl-c
}
auto msg = std::make_shared<builtin_interfaces::msg::Time>();
msg->sec = i;
msg->nanosec = 1000;
clock_pub->publish(msg);
// std::cout << "Publishing: '" << msg->sec << ".000000" << msg->nanosec << "'" << std::endl;
executor.spin_once(1000000ns);
loop_rate.sleep();
}
// Register a callback handler with only post_callback
rclcpp::JumpHandler::SharedPtr callback_handler4 = ros_clock->create_jump_callback(
std::function<void()>(),
std::bind(&CallbackObject::post_callback, &cbo, std::placeholders::_1, 4),
jump_threshold);
trigger_clock_changes(node);
EXPECT_EQ(3, cbo.last_precallback_id_);
EXPECT_EQ(4, cbo.last_postcallback_id_);
}
TEST_F(TestTimeSource, callback_handler_erasure) {
CallbackObject cbo;
rclcpp::JumpThreshold jump_threshold;
jump_threshold.min_forward_ = 0;
jump_threshold.min_backward_ = 0;
jump_threshold.on_clock_change_ = true;
rcl_jump_threshold_t jump_threshold;
jump_threshold.min_forward.nanoseconds = 0;
jump_threshold.min_backward.nanoseconds = 0;
jump_threshold.on_clock_change = true;
rclcpp::TimeSource ts(node);
auto ros_clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
@@ -276,27 +289,26 @@ TEST_F(TestTimeSource, callback_handler_erasure) {
std::bind(&CallbackObject::post_callback, &cbo, std::placeholders::_1, 1),
jump_threshold);
// Callbacks will not be triggered since ROS time is not active.
EXPECT_EQ(0, cbo.last_precallback_id_);
EXPECT_EQ(0, cbo.last_postcallback_id_);
EXPECT_FALSE(ros_clock->ros_time_is_active());
// Activate ROS time.
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true));
EXPECT_TRUE(ros_clock->ros_time_is_active());
trigger_clock_changes(node);
auto t_low = rclcpp::Time(1, 0, RCL_ROS_TIME);
auto t_high = rclcpp::Time(10, 100000, RCL_ROS_TIME);
// Callbacks will now have been triggered since ROS time is active.
EXPECT_EQ(1, cbo.last_precallback_id_);
EXPECT_EQ(1, cbo.last_postcallback_id_);
// Now that we've recieved a message it should be active with parameter unset
EXPECT_TRUE(ros_clock->ros_time_is_active());
auto t_out = ros_clock->now();
EXPECT_NE(0UL, t_out.nanoseconds());
EXPECT_NE(0L, t_out.nanoseconds());
EXPECT_LT(t_low.nanoseconds(), t_out.nanoseconds());
EXPECT_GT(t_high.nanoseconds(), t_out.nanoseconds());
@@ -311,16 +323,14 @@ TEST_F(TestTimeSource, callback_handler_erasure) {
trigger_clock_changes(node);
EXPECT_EQ(2, cbo.last_precallback_id_);
EXPECT_EQ(2, cbo.last_postcallback_id_);
// Now that we've recieved a message it should be active with parameter unset
EXPECT_TRUE(ros_clock->ros_time_is_active());
t_out = ros_clock->now();
EXPECT_NE(0UL, t_out.nanoseconds());
EXPECT_NE(0L, t_out.nanoseconds());
EXPECT_LT(t_low.nanoseconds(), t_out.nanoseconds());
EXPECT_GT(t_high.nanoseconds(), t_out.nanoseconds());
}
@@ -334,42 +344,56 @@ TEST_F(TestTimeSource, parameter_activation) {
ts.attachClock(ros_clock);
EXPECT_FALSE(ros_clock->ros_time_is_active());
auto parameter_service = std::make_shared<rclcpp::ParameterService>(node);
auto parameters_client = std::make_shared<rclcpp::SyncParametersClient>(node);
using namespace std::chrono_literals;
EXPECT_TRUE(parameters_client->wait_for_service(2s));
auto set_parameters_results = parameters_client->set_parameters({
rclcpp::parameter::ParameterVariant("use_sim_time", true)
});
for (auto & result : set_parameters_results) {
EXPECT_TRUE(result.successful);
}
rclcpp::spin_some(node);
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true));
EXPECT_TRUE(ros_clock->ros_time_is_active());
set_parameters_results = parameters_client->set_parameters({
rclcpp::parameter::ParameterVariant("use_sim_time", rclcpp::parameter::PARAMETER_NOT_SET)
});
for (auto & result : set_parameters_results) {
EXPECT_TRUE(result.successful);
}
set_use_sim_time_parameter(
node, rclcpp::ParameterValue(rclcpp::ParameterType::PARAMETER_NOT_SET));
EXPECT_TRUE(ros_clock->ros_time_is_active());
set_parameters_results = parameters_client->set_parameters({
rclcpp::parameter::ParameterVariant("use_sim_time", false)
});
for (auto & result : set_parameters_results) {
EXPECT_TRUE(result.successful);
}
set_use_sim_time_parameter(node, rclcpp::ParameterValue(false));
EXPECT_FALSE(ros_clock->ros_time_is_active());
set_parameters_results = parameters_client->set_parameters({
rclcpp::parameter::ParameterVariant("use_sim_time", rclcpp::parameter::PARAMETER_NOT_SET)
});
for (auto & result : set_parameters_results) {
EXPECT_TRUE(result.successful);
}
set_use_sim_time_parameter(
node, rclcpp::ParameterValue(rclcpp::ParameterType::PARAMETER_NOT_SET));
EXPECT_FALSE(ros_clock->ros_time_is_active());
// If the use_sim_time parameter is not explicitly set to True, this clock's use of sim time
// should not be affected by the presence of a clock publisher.
trigger_clock_changes(node);
EXPECT_FALSE(ros_clock->ros_time_is_active());
set_use_sim_time_parameter(node, rclcpp::ParameterValue(false));
EXPECT_FALSE(ros_clock->ros_time_is_active());
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true));
EXPECT_TRUE(ros_clock->ros_time_is_active());
}
TEST_F(TestTimeSource, no_pre_jump_callback) {
CallbackObject cbo;
rcl_jump_threshold_t jump_threshold;
jump_threshold.min_forward.nanoseconds = 0;
jump_threshold.min_backward.nanoseconds = 0;
jump_threshold.on_clock_change = true;
rclcpp::TimeSource ts(node);
auto ros_clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
// Register a callback for time jumps
rclcpp::JumpHandler::SharedPtr callback_handler = ros_clock->create_jump_callback(
nullptr,
std::bind(&CallbackObject::post_callback, &cbo, std::placeholders::_1, 1),
jump_threshold);
ASSERT_EQ(0, cbo.last_precallback_id_);
ASSERT_EQ(0, cbo.last_postcallback_id_);
ts.attachClock(ros_clock);
// Activate ROS time
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true));
ASSERT_TRUE(ros_clock->ros_time_is_active());
EXPECT_EQ(0, cbo.last_precallback_id_);
EXPECT_EQ(0, cbo.pre_callback_calls_);
EXPECT_EQ(1, cbo.last_postcallback_id_);
EXPECT_EQ(1, cbo.post_callback_calls_);
}

View File

@@ -0,0 +1,53 @@
// 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/utilities.hpp"
TEST(TestUtilities, remove_ros_arguments) {
const char * const argv[] = {"process_name", "-d", "__ns:=/foo/bar",
"__ns:=/fiz/buz", "--foo=bar", "--baz"};
int argc = sizeof(argv) / sizeof(const char *);
auto args = rclcpp::remove_ros_arguments(argc, argv);
ASSERT_EQ(4u, args.size());
ASSERT_EQ(std::string{"process_name"}, args[0]);
ASSERT_EQ(std::string{"-d"}, args[1]);
ASSERT_EQ(std::string{"--foo=bar"}, args[2]);
ASSERT_EQ(std::string{"--baz"}, args[3]);
}
TEST(TestUtilities, remove_ros_arguments_null) {
// In the case of a C executable, we would expect to get
// argc=1 and argv = ["process_name"], so this is an invalid input.
ASSERT_THROW({
rclcpp::remove_ros_arguments(0, nullptr);
}, rclcpp::exceptions::RCLErrorBase);
}
TEST(TestUtilities, init_with_args) {
const char * const argv[] = {"process_name"};
int argc = sizeof(argv) / sizeof(const char *);
auto other_args = rclcpp::init_and_remove_ros_arguments(argc, argv);
ASSERT_EQ(1u, other_args.size());
ASSERT_EQ(std::string{"process_name"}, other_args[0]);
EXPECT_TRUE(rclcpp::is_initialized());
}

View File

@@ -0,0 +1,30 @@
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
0.6.0
-----------
* Updated to use new error handling API from rcutils (`#577 <https://github.com/ros2/rclcpp/issues/577>`_)
* Deleted TRANSITION_SHUTDOWN (`#576 <https://github.com/ros2/rclcpp/issues/576>`_)
* Added a warning when publishing if publisher is not active (`#574 <https://github.com/ros2/rclcpp/issues/574>`_)
* Added SMART_PTRS_DEF to LifecyclePublisher (`#569 <https://github.com/ros2/rclcpp/issues/569>`_)
* Added service for transition graph (`#555 <https://github.com/ros2/rclcpp/issues/555>`_)
* Added semicolons to all RCLCPP and RCUTILS macros. (`#565 <https://github.com/ros2/rclcpp/issues/565>`_)
* Fixed and improved documentation (`#546 <https://github.com/ros2/rclcpp/issues/546>`_)
* Removed unneeded dependency on std_msgs (`#513 <https://github.com/ros2/rclcpp/issues/513>`_)
* Removed use of uninitialized CMake var (`#511 <https://github.com/ros2/rclcpp/issues/511>`_)
* Added get_node_names API from node. (`#508 <https://github.com/ros2/rclcpp/issues/508>`_)
* Fixed rosidl dependencies (`#507 <https://github.com/ros2/rclcpp/issues/507>`_)
* Contributors: Chris Lalancette, Dirk Thomas, Francisco Martín Rico, Karsten Knese, Mikael Arguedas, Sriram Raghunathan, William Woodall, cho3
0.5.0 (2018-06-25)
------------------
* Added functions that allow you to publish serialized messages and received serialized messages in your subscription callback. (`#388 <https://github.com/ros2/rclcpp/issues/388>`_)
* Added ability to initialize parameter values in a node with an argument to the Node constructor. (`#486 <https://github.com/ros2/rclcpp/issues/486>`_)
* Nodes now autostart the ROS parameter services which let you get, set, and list parameters in a node. (`#478 <https://github.com/ros2/rclcpp/issues/478>`_)
* Fixed a bug that occurred when mixing ``std::shared_ptr`` and ``std::bind``. (`#470 <https://github.com/ros2/rclcpp/issues/470>`_)
* Added ability to pass command line arguments to the Node constructor. (`#461 <https://github.com/ros2/rclcpp/issues/461>`_)
* Changed library export order for static linking. (`#446 <https://github.com/ros2/rclcpp/issues/446>`_)
* Now depends on ``ament_cmake_ros``. (`#444 <https://github.com/ros2/rclcpp/issues/444>`_)
* Updaed code to use logging macros rather than ``fprintf()``. (`#439 <https://github.com/ros2/rclcpp/issues/439>`_)
* Contributors: Dirk Thomas, Guillaume Autran, Karsten Knese, Michael Carroll, Mikael Arguedas, Shane Loretz, dhood

View File

@@ -10,26 +10,28 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_ros REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rcl_lifecycle REQUIRED)
find_package(std_msgs REQUIRED)
find_package(rosidl_typesupport_cpp REQUIRED)
find_package(lifecycle_msgs REQUIRED)
include_directories(include)
### 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
)
# specific order: dependents before dependencies
ament_target_dependencies(rclcpp_lifecycle
"lifecycle_msgs"
"rclcpp"
"rcl_lifecycle"
"rclcpp")
"lifecycle_msgs"
"rosidl_typesupport_cpp"
)
# Causes the visibility macros to use dllexport rather than dllimport,
# which is appropriate when building the dll but not consuming it.
@@ -50,7 +52,6 @@ if(BUILD_TESTING)
target_include_directories(test_lifecycle_node PUBLIC
${rcl_lifecycle_INCLUDE_DIRS}
${rclcpp_INCLUDE_DIRS}
${rclcpp_lifecycle_INCLUDE_DIRS}
)
target_link_libraries(test_lifecycle_node ${PROJECT_NAME})
endif()
@@ -59,7 +60,6 @@ if(BUILD_TESTING)
target_include_directories(test_state_machine_info PUBLIC
${rcl_lifecycle_INCLUDE_DIRS}
${rclcpp_INCLUDE_DIRS}
${rclcpp_lifecycle_INCLUDE_DIRS}
)
target_link_libraries(test_state_machine_info ${PROJECT_NAME})
endif()
@@ -68,7 +68,6 @@ if(BUILD_TESTING)
target_include_directories(test_register_custom_callbacks PUBLIC
${rcl_lifecycle_INCLUDE_DIRS}
${rclcpp_INCLUDE_DIRS}
${rclcpp_lifecycle_INCLUDE_DIRS}
)
target_link_libraries(test_register_custom_callbacks ${PROJECT_NAME})
endif()
@@ -77,7 +76,6 @@ if(BUILD_TESTING)
target_include_directories(test_callback_exceptions PUBLIC
${rcl_lifecycle_INCLUDE_DIRS}
${rclcpp_INCLUDE_DIRS}
${rclcpp_lifecycle_INCLUDE_DIRS}
)
target_link_libraries(test_callback_exceptions ${PROJECT_NAME})
endif()
@@ -86,7 +84,6 @@ if(BUILD_TESTING)
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()
@@ -95,17 +92,17 @@ if(BUILD_TESTING)
target_include_directories(test_transition_wrapper PUBLIC
${rcl_lifecycle_INCLUDE_DIRS}
${rclcpp_INCLUDE_DIRS}
${rclcpp_lifecycle_INCLUDE_DIRS}
)
target_link_libraries(test_transition_wrapper ${PROJECT_NAME})
endif()
endif()
# specific order: dependents before dependencies
ament_export_include_directories(include)
ament_export_libraries(${PROJECT_NAME})
ament_export_dependencies(rclcpp)
ament_export_dependencies(rcl_lifecycle)
ament_export_dependencies(lifecycle_msgs)
ament_export_include_directories(include)
ament_export_libraries(${PROJECT_NAME})
ament_package()
install(DIRECTORY include/

View File

@@ -73,7 +73,7 @@ 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] 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.
*/
@@ -86,17 +86,27 @@ public:
/// Create a node based on the node name and a rclcpp::Context.
/**
* \param[in] node_name Name of the node.
* \param[in] node_name Namespace 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] arguments Command line arguments that should apply only to this node.
* \param[in] initial_parameters a list of initial values for parameters on the node.
* This can be used to provide remapping rules that only affect one instance.
* \param[in] use_global_arguments False to prevent node using arguments passed to the 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.
* \param[in] start_parameter_services True to setup ROS interfaces for accessing parameters
* in the node.
*/
RCLCPP_LIFECYCLE_PUBLIC
LifecycleNode(
const std::string & node_name,
const std::string & namespace_,
rclcpp::Context::SharedPtr context,
bool use_intra_process_comms = false);
const std::vector<std::string> & arguments,
const std::vector<rclcpp::Parameter> & initial_parameters,
bool use_global_arguments = true,
bool use_intra_process_comms = false,
bool start_parameter_services = true);
RCLCPP_LIFECYCLE_PUBLIC
virtual ~LifecycleNode();
@@ -133,6 +143,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 allocator to use during publishing activities.
* \return Shared pointer to the created publisher.
*/
template<typename MessageT, typename Alloc = std::allocator<void>>
@@ -144,7 +155,8 @@ public:
/// Create and return a LifecyclePublisher.
/**
* \param[in] topic_name The topic for this publisher to publish on.
* \param[in] qos_history_depth The depth of the publisher message queue.
* \param[in] qos_profile The QoS settings for this publisher.
* \param[in] allocator allocator to use during publishing activities.
* \return Shared pointer to the created publisher.
*/
template<typename MessageT, typename Alloc = std::allocator<void>>
@@ -180,7 +192,8 @@ public:
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_default,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr,
bool ignore_local_publications = false,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>::SharedPtr
msg_mem_strat = nullptr,
std::shared_ptr<Alloc> allocator = nullptr);
@@ -192,6 +205,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 allocator to be used during handling of subscription callbacks.
* \return Shared pointer to the created subscription.
*/
/* TODO(jacquelinekay):
@@ -246,25 +260,25 @@ public:
RCLCPP_LIFECYCLE_PUBLIC
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
set_parameters(const std::vector<rclcpp::Parameter> & parameters);
RCLCPP_LIFECYCLE_PUBLIC
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
set_parameters_atomically(const std::vector<rclcpp::Parameter> & parameters);
RCLCPP_LIFECYCLE_PUBLIC
std::vector<rclcpp::parameter::ParameterVariant>
std::vector<rclcpp::Parameter>
get_parameters(const std::vector<std::string> & names) const;
RCLCPP_LIFECYCLE_PUBLIC
rclcpp::parameter::ParameterVariant
rclcpp::Parameter
get_parameter(const std::string & name) const;
RCLCPP_LIFECYCLE_PUBLIC
bool
get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const;
rclcpp::Parameter & parameter) const;
template<typename ParameterT>
bool
@@ -284,13 +298,17 @@ 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 function which is expected to atomically set parameters.
* \note Repeated invocations of this function will overwrite previous callbacks
*/
template<typename CallbackT>
void
register_param_change_callback(CallbackT && callback);
RCLCPP_LIFECYCLE_PUBLIC
std::vector<std::string>
get_node_names() const;
RCLCPP_LIFECYCLE_PUBLIC
std::map<std::string, std::vector<std::string>>
get_topic_names_and_types(bool no_demangle = false) const;
@@ -398,7 +416,7 @@ public:
RCLCPP_LIFECYCLE_PUBLIC
const State &
trigger_transition(
const Transition & transition, rcl_lifecycle_transition_key_t & cb_return_code);
const Transition & transition, LifecycleNodeInterface::CallbackReturn & cb_return_code);
RCLCPP_LIFECYCLE_PUBLIC
const State &
@@ -407,7 +425,7 @@ public:
RCLCPP_LIFECYCLE_PUBLIC
const State &
trigger_transition(
uint8_t transition_id, rcl_lifecycle_transition_key_t & cb_return_code);
uint8_t transition_id, LifecycleNodeInterface::CallbackReturn & cb_return_code);
RCLCPP_LIFECYCLE_PUBLIC
const State &
@@ -415,7 +433,7 @@ public:
RCLCPP_LIFECYCLE_PUBLIC
const State &
configure(rcl_lifecycle_transition_key_t & cb_return_code);
configure(LifecycleNodeInterface::CallbackReturn & cb_return_code);
RCLCPP_LIFECYCLE_PUBLIC
const State &
@@ -423,7 +441,7 @@ public:
RCLCPP_LIFECYCLE_PUBLIC
const State &
cleanup(rcl_lifecycle_transition_key_t & cb_return_code);
cleanup(LifecycleNodeInterface::CallbackReturn & cb_return_code);
RCLCPP_LIFECYCLE_PUBLIC
const State &
@@ -431,7 +449,7 @@ public:
RCLCPP_LIFECYCLE_PUBLIC
const State &
activate(rcl_lifecycle_transition_key_t & cb_return_code);
activate(LifecycleNodeInterface::CallbackReturn & cb_return_code);
RCLCPP_LIFECYCLE_PUBLIC
const State &
@@ -439,7 +457,7 @@ public:
RCLCPP_LIFECYCLE_PUBLIC
const State &
deactivate(rcl_lifecycle_transition_key_t & cb_return_code);
deactivate(LifecycleNodeInterface::CallbackReturn & cb_return_code);
RCLCPP_LIFECYCLE_PUBLIC
const State &
@@ -447,31 +465,31 @@ public:
RCLCPP_LIFECYCLE_PUBLIC
const State &
shutdown(rcl_lifecycle_transition_key_t & cb_return_code);
shutdown(LifecycleNodeInterface::CallbackReturn & cb_return_code);
RCLCPP_LIFECYCLE_PUBLIC
bool
register_on_configure(std::function<rcl_lifecycle_transition_key_t(const State &)> fcn);
register_on_configure(std::function<LifecycleNodeInterface::CallbackReturn(const State &)> fcn);
RCLCPP_LIFECYCLE_PUBLIC
bool
register_on_cleanup(std::function<rcl_lifecycle_transition_key_t(const State &)> fcn);
register_on_cleanup(std::function<LifecycleNodeInterface::CallbackReturn(const State &)> fcn);
RCLCPP_LIFECYCLE_PUBLIC
bool
register_on_shutdown(std::function<rcl_lifecycle_transition_key_t(const State &)> fcn);
register_on_shutdown(std::function<LifecycleNodeInterface::CallbackReturn(const State &)> fcn);
RCLCPP_LIFECYCLE_PUBLIC
bool
register_on_activate(std::function<rcl_lifecycle_transition_key_t(const State &)> fcn);
register_on_activate(std::function<LifecycleNodeInterface::CallbackReturn(const State &)> fcn);
RCLCPP_LIFECYCLE_PUBLIC
bool
register_on_deactivate(std::function<rcl_lifecycle_transition_key_t(const State &)> fcn);
register_on_deactivate(std::function<LifecycleNodeInterface::CallbackReturn(const State &)> fcn);
RCLCPP_LIFECYCLE_PUBLIC
bool
register_on_error(std::function<rcl_lifecycle_transition_key_t(const State &)> fcn);
register_on_error(std::function<LifecycleNodeInterface::CallbackReturn(const State &)> fcn);
protected:
RCLCPP_LIFECYCLE_PUBLIC

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