Compare commits

...

84 Commits

Author SHA1 Message Date
Steven! Ragnarök
403aac9662 0.7.16 2021-05-21 07:49:17 -07:00
Jacob Perron
e8cf066d7d Fix documented example in create_publisher (#1558) (#1560)
Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2021-02-24 14:15:48 -08:00
Ivan Santiago Paunovic
c4c39a2069 Fix NodeOptions copy constructor (#1376) (#1466)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-12-17 15:23:02 -03:00
Steven! Ragnarök
b3ecbd5ff7 0.7.15 2020-11-24 15:48:53 -08:00
Seulbae Kim
052936980e Fix typo in action client logger name (#1414)
Backport of commit 6ef2384 from #937.

Signed-off-by: Seulbae Kim <squizz617@gmail.com>
2020-10-28 12:59:50 -03:00
Michel Hidalgo
6e133c2ad7 Fix implementation of NodeOptions::use_global_arguments() (#1176) (#1206)
`this->node_options_` might still be `nullptr` for a default initialized NodeOptions instance.
`use_global_arguments()` must return `this->use_global_arguments_`, in analogy to `NodeOptions::enable_rosout()`.

Signed-off-by: Johannes Meyer <johannes@intermodalics.eu>

Co-authored-by: Johannes Meyer <johannes@intermodalics.eu>
2020-10-05 16:04:34 -03:00
Michel Hidalgo
1f0f8f6176 [dashing backport] Fix conversion of negative durations to messages (#1188) (#1207)
* Fix conversion of negative durations to messages (#1188)

* Fix conversion from negative Duration or Time to the respective message type and throw in Duration::to_rmw_time() if the duration is negative.
rmw_time_t cannot represent negative durations.

Constructors and assignment operators can be just defaulted.

Other changes are mainly cosmetical, to make conversions between signed
and unsigned types and between 32-bit and 64-bit types more explicit.

Signed-off-by: Johannes Meyer <johannes@intermodalics.eu>

* Add -Wconversion compiler option and fix implicit conversions that might alter the value

Signed-off-by: Johannes Meyer <johannes@intermodalics.eu>

* Fix usage of fixture class in some unit tests by using gtest macro TEST_F() instead of TEST().

Signed-off-by: Johannes Meyer <johannes@intermodalics.eu>

* Add compiler option -Wno-sign-conversion to fix build with Clang on macOS

Signed-off-by: Johannes Meyer <johannes@intermodalics.eu>

* Drop tests for future API.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

Co-authored-by: Johannes Meyer <johannes@intermodalics.eu>
2020-10-05 16:04:00 -03:00
Michel Hidalgo
ab2484295a [dashing backport] Type conversions fixes (#901) (#1209)
* Type conversions fixes (#901)

* Fix type conversions

Signed-off-by: Monika Idzik <monika.idzik@apex.ai>

* Add static_casts

Signed-off-by: Monika Idzik <monika.idzik@apex.ai>

* Address PR comments

Signed-off-by: Monika Idzik <monika.idzik@apex.ai>

* Remove one time use variable

Signed-off-by: Monika Idzik <monika.idzik@apex.ai>

* Please cpplint

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

Co-authored-by: Monika Idzik <monika.idzik@apex.ai>
2020-09-17 20:19:12 -03:00
Ivan Santiago Paunovic
c214ddc57b add operator!= for duration (#1236) (#1277)
Signed-off-by: Jannik Abbenseth <jannik.abbenseth@ipa.fraunhofer.de>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jannik Abbenseth <ipa-jba@users.noreply.github.com>
2020-08-18 10:34:08 -03:00
Ivan Santiago Paunovic
d27ca8ee85 add operator!= for duration (#1236) (#1277)
Signed-off-by: Jannik Abbenseth <jannik.abbenseth@ipa.fraunhofer.de>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jannik Abbenseth <ipa-jba@users.noreply.github.com>
2020-08-18 10:28:23 -03:00
Steven! Ragnarök
342f225053 0.7.14 2020-07-11 00:04:56 -04:00
Alejandro Hernández Cordero
3c198de5b2 [Backport Dashing]Fixed doxygen warnings (#1163) (#1208)
Signed-off-by: ahcorde <ahcorde@gmail.com>
2020-07-10 16:54:25 -04:00
Ivan Santiago Paunovic
8906c9eb7f Check if context is valid when looping in spin_some (#1167)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-07-02 18:26:00 -03:00
DongheeYe
9e98aa70d3 Fix spin_until_future_complete: check spinning value (#1023)
Signed-off-by: Donghee Ye <donghee.ye@samsung.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-07-02 18:26:00 -03:00
Alejandro Hernández Cordero
6471043a0b Added rclcpp_components Doxyfile (#1091) (#1201)
* Added rclcpp components Doxyfile

Signed-off-by: ahcorde <ahcorde@gmail.com>

* Added feedback

Signed-off-by: ahcorde <ahcorde@gmail.com>
2020-06-26 19:20:32 +02:00
tomoya
f8bba370dc [dashing] backport #1182 (#1195)
Signed-off-by: Tomoya.Fujita <Tomoya.Fujita@sony.com>
2020-06-25 13:04:38 -04:00
tomoya
d5040ae304 Fix lock-order-inversion (potential deadlock) (#1135) (#1138)
Signed-off-by: Tomoya.Fujita <Tomoya.Fujita@sony.com>
2020-06-01 11:20:33 -03:00
Steven! Ragnarök
3e5b6a47ea 0.7.13 2020-03-12 17:37:15 -04:00
Jacob Perron
8c3fbce10d Don't specify calling convention in std::_Binder template (#952) (#1015)
Fix for a build error on 32-bit Windows. Member functions use the
__thiscall convention by default which is incompatible with __cdecl.

Signed-off-by: Sean Kelly <sean@seankelly.dev>
2020-03-12 14:23:08 -07:00
Jacob Perron
fb6ab9ef70 Remove absolute path from installed CMake code (#948) (#949)
Otherwise, rclcpp_components_register_node() fails if used from a fat archive.

Related to https://github.com/ros2/ros2/issues/606.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2020-01-08 10:19:12 -08:00
Shane Loretz
7629f5b6d9 0.7.12
Signed-off-by: Shane Loretz <sloretz@openrobotics.org>
2019-12-05 14:16:28 -05:00
Ivan Santiago Paunovic
7cb2ececcb issue-919 Fixed "memory leak" in action clients (#920) (#934)
Whenever a call is made to `rclcpp_action::Client::wait_for_action_server`
a weak pointer to an Event object gets added to the graph_event_ vector
of the NodeGraph interface. This vector will be cleared on a node graph
change event, but if no such event occurs the weak pointer will be stuck
in the vector.  Furthermore, if client code issues repeated calls to
`wait_for_action_server` the vector will keep growing.

The fix moves the Event object creation right after the early return from
`wait_for_action_server` so that the Event object is not created in the
case that the server is known to be present and therefore there is no
need to wait for a node graph change event to occur.

Signed-off-by: Adrian Stere <astere@clearpath.ai>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2019-12-05 14:09:07 -05:00
Shane Loretz
0f4fc08a93 [Dashing] backport rclcpp_components_register_node (#935)
* Cmake infrastructure for creating components (#784)

*cmake macro to create components for libraries with multiple nodes

Signed-off-by: Siddharth Kucheria <kucheria@usc.edu>
Signed-off-by: Shane Loretz <sloretz@openrobotics.org>

* fix for multiple nodes not being recognized (#790)

Signed-off-by: Siddharth Kucheria <kucheria@usc.edu>
Signed-off-by: Shane Loretz <sloretz@openrobotics.org>

* fix linter issue (#795)

Signed-off-by: Siddharth Kucheria <kucheria@usc.edu>
Signed-off-by: Shane Loretz <sloretz@openrobotics.org>
2019-12-05 12:48:13 -05:00
Jacob Perron
82f6dfa6de [rclcpp_action] Do not throw exception in action client if take fails (#888) (#891)
As documented in rcl_action, a return code of RCL_RET_ACTION_CLIENT_TAKE_FAILED does not mean that
an error occurred.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-11-18 14:44:06 -08:00
Ivan Santiago Paunovic
fadd923aa0 0.7.11 2019-10-11 11:08:51 -03:00
ivanpauno
f954ce5145 Fix get_node_interfaces functions taking a pointer (#821) (#870)
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-09-26 14:18:39 -03:00
ivanpauno
895145cfc9 Fix hang with timers in MultiThreadedExecutor (#835) (#836) (#869)
Signed-off-by: Todd Malsbary <todd.malsbary@intel.com>
2019-09-25 15:52:22 -03:00
Tully Foote
3cdb3cb1bf 0.7.10 2019-09-23 12:41:47 -07:00
Tully Foote
5d5d3ce09d update changelog 2019-09-23 12:40:48 -07:00
Zachary Michaels
839ad201ac reset error message before setting a new one, embed the original one (#854) (#866)
Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
Signed-off-by: Zachary Michaels <zmichaels11@gmail.com>
2019-09-23 12:38:26 -07:00
Michael Carroll
f3b0aa170c 0.7.9
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2019-09-20 16:14:01 -05:00
Zachary Michaels
6e76503d9a add mutex in add/remove_node and wait_for_work to protect concurrent use/change of memory_strategy_ (#837) (#857)
Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
Signed-off-by: Zachary Michaels <zmichaels11@gmail.com>
2019-09-20 14:08:34 -07:00
William Woodall
b4629bf889 0.7.8 2019-09-06 10:37:56 -07:00
William Woodall
3f31ad0f55 changelogs
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-09-06 10:37:28 -07:00
Jacob Perron
25eeffdfcc Fixe error messages not printing to terminal (#777) (#847)
* Uncommented catch exception code to print error

Signed-off-by: Yathartha Tuladhar <yathartha3@gmail.com>

* put back the e

Signed-off-by: Yathartha Tuladhar <yathartha3@gmail.com>

* uncommented error printing in exception handling

Signed-off-by: Yathartha Tuladhar <yathartha3@gmail.com>

* Removed "/n" characters.
Update lifecycle_node_interface_impl.hpp

Signed-off-by: Yathartha Tuladhar <yathartha3@gmail.com>

* Fix CI issue and remove TODO

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-09-05 11:59:18 -07:00
Karsten Knese
3293603396 0.7.7 2019-07-31 15:46:10 -07:00
Karsten Knese
50ec1e568a refine changelog text
Signed-off-by: Karsten Knese <karsten@openrobotics.org>
2019-07-31 15:38:03 -07:00
Karsten Knese
607e290732 generate changelog
Signed-off-by: Karsten Knese <karsten@openrobotics.org>
2019-07-31 15:11:58 -07:00
Scott K Logan
47ce42bc3f Use params from node '/**' from parameter YAML file (#805)
The short-term goal of this change is to enable the creation of a
parameter YAML file which is applied to each node, regardless of node
name or namespace.

Future work is to support all wildcard syntax in node names in
parameter YAML files.

This is a backport of #762 for Dashing.

Signed-off-by: Scott K Logan <logans@cottsay.net>
2019-07-31 14:05:01 -07:00
Scott K Logan
d2f052ee3d Fix a comparison with a sign mismatch (#804)
This is a backport of #771 for Dashing.

Signed-off-by: Scott K Logan <logans@cottsay.net>
2019-07-31 11:05:00 -07:00
Gonzo
b86127d721 changed on_parameter_event qos profile to rmw_qos_profile_parameter_events (#774) (#798)
Signed-off-by: alberto <alberto.soragna@gmail.com>
2019-07-30 15:06:44 -03:00
Michel Hidalgo
5cb6a6eeb5 Make TimeSource ignore use_sim_time events coming from other nodes. (#799) (#803)
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2019-07-30 12:44:39 -03:00
Karsten Knese
f41e33c2a7 use default parameter descriptor in parameters interface (#765) (#794)
* use default parameter descriptor in parameters interface

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* use default parameter for value

Signed-off-by: Karsten Knese <karsten@openrobotics.org>
2019-07-29 11:24:19 -07:00
Esteve Fernandez
10c34ee9e5 Added support for const member functions (#763)
* Added support for const member functions

Signed-off-by: Esteve Fernandez <esteve@apache.org>

* Added signature for Windows

Signed-off-by: Esteve Fernandez <esteve@apache.org>

* Fix style

Signed-off-by: Esteve Fernandez <esteve@apache.org>

* Attempt at fixing function_traits for macOS

Signed-off-by: Esteve Fernandez <esteve@apache.org>
2019-07-29 10:44:27 -07:00
Dirk Thomas
6d3cbd39d1 Add default value to options in LifecycleNode construnctor. Update API documentation. (#775) (#801)
Signed-off-by: Esteve Fernandez <esteve@apache.org>
2019-07-26 15:39:05 -07:00
Chris Lalancette
bfee90ab7a 0.7.6 2019-06-12 20:13:10 +00:00
Chris Lalancette
1927f7e40e Changelog.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2019-06-12 20:12:58 +00:00
ivanpauno
f73d80e79c Ignore parameters overrides in set parameter methods when allowing undeclared parameters (#756)
* Ignore parameters overrides in set parameter methods when allowing undeclared parameters

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Address reviewer comment

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-06-12 16:02:01 -04:00
Shane Loretz
e2e35570d5 Add rclcpp::create_timer() (#757)
* Add rclcpp::create_timer()

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>

* Friendly overload with node-like object

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>

* forward<CallbackT>

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>

* Make sure test with NodeWrapper compiles

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
2019-06-12 16:02:01 -04:00
Alberto Soragna
5c395d6651 checking origin of intra-process msg before taking them (#753)
Signed-off-by: alberto <alberto.soragna@gmail.com>
2019-06-12 16:02:01 -04:00
Jacob Perron
24080a458d 0.7.5 2019-05-30 17:31:20 -07:00
ivanpauno
8ff51833ad Avoid 'Intra process message no longer being stored when trying to ha… (#749)
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-05-30 15:54:02 -03:00
Michael Carroll
347f8d0b1d 0.7.4
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2019-05-29 18:53:14 -05:00
William Woodall
ecc95009f1 Rename parameter options (#745)
* rename initial_parameters in NodeOptions to parameter_overrides

Signed-off-by: William Woodall <william@osrfoundation.org>

* rename automatically_declare_initial_parameters to automatically_declare_parameters_from_overrides

Signed-off-by: William Woodall <william@osrfoundation.org>

* some additional renames I missed

Signed-off-by: William Woodall <william@osrfoundation.org>

* add test for setting after declaring with parameter overrides

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixup NodeOptions docs

Signed-off-by: William Woodall <william+github@osrfoundation.org>

Co-Authored-By: chapulina <louise@openrobotics.org>

* clarify relationship between allow_undeclared_parameters and parameter_overrides

Signed-off-by: William Woodall <william@osrfoundation.org>
2019-05-29 12:12:42 -07:00
roderick-koehle
d2b2f9124e Bionic use of strerror_r (#742)
Since API 23 Android Bionic uses the GNU convention for strerror_r.
Following bionic/libc line 96,
  https://android.googlesource.com/platform/bionic.git/+/refs/heads/master/libc/include/string.h

Signed-off-by: roderick-koehle <50633232+roderick-koehle@users.noreply.github.com>
2019-05-29 11:15:50 -07:00
ivanpauno
ca01555936 Enforce parameter ranges (#735)
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-05-29 12:03:06 -03:00
Alberto Soragna
0723a0a6fc removed not used parameter client (#740)
* removed not used parameter client

Signed-off-by: alberto <alberto.soragna@gmail.com>

* moved parameter include directives to time source cpp file

Signed-off-by: alberto <alberto.soragna@gmail.com>
2019-05-28 14:52:32 -07:00
Dirk Thomas
8553fbea7f ensure removal of guard conditions of expired nodes from memory strategy (#741)
* change memory strategy API from vector of nodes to list of nodes

Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>

* store guard_condition of node in executor and ensure that it is removed from the memory strategy

Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>

* add unit test

Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
2019-05-24 15:31:31 -07:00
Jacob Perron
131a11bba5 Guard against calling null goal response callback (#738)
Also make sure to invoke the goal response callback before the result callback.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-05-23 21:00:58 -07:00
Jacob Perron
29308dc8bc Fix typo in log warning message (#737)
Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-05-23 16:10:36 -07:00
Dirk Thomas
06275105fc don't use global arguments for components loaded into the manager (#736)
Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
2019-05-23 13:06:23 -07:00
ivanpauno
30df5cdf36 Throw nice errors when creating a publisher with intraprocess communication and incompatible qos policy (#729)
* Throw nice errors when creating a publisher with intraprocess communication and history keep all or history depth 0.

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-05-22 15:38:10 -03:00
William Woodall
1a662d46fb 0.7.3 2019-05-20 16:12:29 -07:00
William Woodall
4467ce9913 changelogs
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-05-20 16:12:05 -07:00
Emerson Knapp
005131dba8 I realize why the original was spelled wrong, volatile is a c++ keyword. Modify to not require learning a misspelling, and note why it can't have that name (#725)
Signed-off-by: Emerson Knapp <eknapp@amazon.com>
2019-05-17 18:18:58 -07:00
Emerson Knapp
05c19028f4 volitile -> volatile (#724)
Signed-off-by: Emerson Knapp <eknapp@amazon.com>
2019-05-17 15:37:38 -07:00
Dirk Thomas
a8f4d391f2 fix clang warning (#723)
Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
2019-05-17 09:26:16 -07:00
Alberto Soragna
0682ceb3e1 Created on_parameter_event static function (#688)
* created static functions

Signed-off-by: alsora <alberto.soragna@gmail.com>
Signed-off-by: alberto soragna <alberto.soragna@gmail.com>

* updated on_parameter_event to new subscriber api

Signed-off-by: alberto soragna <alberto.soragna@gmail.com>

* Update parameter_client.hpp

Reorderd typenames in template

Signed-off-by: alberto soragna <alberto.soragna@gmail.com>

* updated API also for Synchronous client and fixed linter errors

Signed-off-by: alberto soragna <alberto.soragna@gmail.com>

* added empty line at the end of files

Signed-off-by: alberto soragna <alberto.soragna@gmail.com>

* fixed linter error

Signed-off-by: alsora <alberto.soragna@gmail.com>

* added parameter client tests

Signed-off-by: alsora <alberto.soragna@gmail.com>

* added missing includes in unit test

Signed-off-by: alsora <alberto.soragna@gmail.com>
2019-05-16 13:45:10 -07:00
Jacob Perron
2152e0574b Guard against ParameterNotDeclaredException in parameter service callbacks (#718)
Fixes #705.

If the set_parameters() call fails, it's nice to be able to return a partial result.
Since there is no convenient method to obtain a partial result, we call set_parameters()
once for each parameter.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-05-15 16:10:53 -07:00
Michael Jeronimo
1081e75079 Add missing template functionality to lifecycle_node. (#707)
* Add missing template functionality to lifecycle_node.

Recent changes to the node_parameters interface was accompanied by additions to the
node.hpp header and implementation files. However, these additions were not also made
to the corresponding lifecycle node files. This PR includes the changes required for
the lifecycle node.

Going forward, I suggest that any code like this that supplements the basic node interfaces
should either be factored out into a separate header that both node and lifecycle_node
include, or that the supplemental code simply be included in the appropriate node_interface
file directly, if possible. That way we can avoid code duplication and its symptoms which
is node and lifecycle_node getting out of sync (which they have several times).

Signed-off-by: Michael Jeronimo <michael.jeronimo@intel.com>

* consolidate documentation to just be in rclcpp/node.hpp

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix visibility macros

Signed-off-by: William Woodall <william@osrfoundation.org>

* deprecation methods that were also deprecated in rclcpp::Node

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixup variable name

Signed-off-by: William Woodall <william@osrfoundation.org>

* add missing template method implementations

Signed-off-by: William Woodall <william@osrfoundation.org>

* add more methods that were not ported to lifecycle node originally

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix cpplint

Signed-off-by: William Woodall <william@osrfoundation.org>
2019-05-15 15:15:40 -07:00
Prajakta Gokhale
b17bbf31b3 Fix heap-use-after-free and memory leaks reported from test_node.cpp (#719)
Fix AddressSanitizer errors reported by test_node.cpp unit test.

Signed-off-by: Prajakta Gokhale <prajaktg@amazon.com>
2019-05-14 21:08:33 -07:00
William Woodall
ef41059a75 0.7.2 2019-05-08 17:27:05 -07:00
William Woodall
cfeb6a6360 changelogs
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-05-08 17:25:59 -07:00
William Woodall
c769b1b030 change API to encourage users to specify history depth always (#713)
* improve interoperability with rclcpp::Duration and std::chrono

Signed-off-by: William Woodall <william@osrfoundation.org>

* add to_rmw_time to Duration

Signed-off-by: William Woodall <william@osrfoundation.org>

* add new QoS class to rclcpp

Signed-off-by: William Woodall <william@osrfoundation.org>

* changes to NodeBase, NodeTopics, etc in preparation for changes to pub/sub

Signed-off-by: William Woodall <william@osrfoundation.org>

* refactor publisher creation to use new QoS class

Signed-off-by: William Woodall <william@osrfoundation.org>

* refactor subscription creation to use new QoS class

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixing fallout from changes to pub/sub creation

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixed Windows error: no appropriate default constructor available

why? who knows

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixed Windows error: could not deduce template argument for 'PublisherT'

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix missing vftable linker error on Windows

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix more cases of no suitable default constructor errors...

Signed-off-by: William Woodall <william@osrfoundation.org>

* prevent msvc from trying to interpret some cases as functions

Signed-off-by: William Woodall <william@osrfoundation.org>

* uncrustify

Signed-off-by: William Woodall <william@osrfoundation.org>

* cpplint

Signed-off-by: William Woodall <william@osrfoundation.org>

* add C++ version of default action qos

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixing lifecycle subscription signatures

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix allocators (we actually use this already in the pub/sub factory)

Signed-off-by: William Woodall <william@osrfoundation.org>

* suppress cppcheck on false positive syntax error

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix more cppcheck syntax error false positives

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix case where sub-type of QoS is used

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixup get_node_topics_interface.hpp according to reviews and tests

Signed-off-by: William Woodall <william@osrfoundation.org>

* additional fixes based on local testing and CI

Signed-off-by: William Woodall <william@osrfoundation.org>

* another trick to avoid 'no appropriate default constructor available'

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix compiler error with clang on macOS

Signed-off-by: William Woodall <william@osrfoundation.org>

* disable build failure tests until we can get Jenkins to ignore their output

Signed-off-by: William Woodall <william@osrfoundation.org>

* suppress more cppcheck false positives

Signed-off-by: William Woodall <william@osrfoundation.org>

* add missing visibility macros to default QoS profile classes

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix another case of 'no appropriate default constructor available'

Signed-off-by: William Woodall <william@osrfoundation.org>

* unfortunately this actaully fixes a build error on Windows...

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix typos

Signed-off-by: William Woodall <william@osrfoundation.org>
2019-05-08 14:24:40 -07:00
ivanpauno
385cccc2cc Deprecate shared ptr publish (#709)
* Deprecate publish methods with shared_ptr signature

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Corrected with PR comments. Deprecated similar methods in lifecycle publisher

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Removed reference in unique_ptr publish call

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Corrected with PR comments. Corrected warning problem in lifecycle_publisher

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Deprecate publish call taking a raw ptr. Stop deprecating publish methods in LifecyclePublisher.

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Pleased linter

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Corrected mac warning

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Corrected serialized publish methods

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Avoid windows warning

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Not deprecate on windows

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-05-06 13:32:50 -07:00
M. M
d399fef9c6 Implement callbacks for liveliness and deadline QoS events (#695)
* implement deadline and liveliness qos callbacks

Signed-off-by: Miaofei <miaofei@amazon.com>

* fix windows build

Signed-off-by: Miaofei <miaofei@amazon.com>

* address feedback from pull request

Signed-off-by: Miaofei <miaofei@amazon.com>

* update formatting to be compatible with ros2 coding style and ament_uncrustify

Signed-off-by: Miaofei <miaofei@amazon.com>

* make QOSEventHandlerBase::add_to_wait_set() throw

Signed-off-by: Miaofei <miaofei@amazon.com>

* mark throw_from_rcl_error as [[noreturn]]

Signed-off-by: Miaofei <miaofei@amazon.com>

* fix windows compilation error

Signed-off-by: Miaofei <miaofei@amazon.com>

* Ignore uncrustify for single [[noreturn]] syntax instance

Signed-off-by: Emerson Knapp <eknapp@amazon.com>
2019-05-03 10:16:39 -07:00
Jacob Perron
ecf35114b6 Add return code to CancelGoal service response (#710)
* Populate return code of CancelGoal service response

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

* Throw if there is an error processing a cancel goal request

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

* Make cancel callback signature consistent across cancel methods and add tests

Refactored the callback signature for canceling one goal. Now it is the same as the other cancel methods.
This makes it easier to communicate the error code to the user.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

* Address review

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-05-02 15:25:15 -07:00
Alberto Soragna
7ed130cf7a check for nullptr before publishing event (#714)
Signed-off-by: alberto <asoragna@irobot.com>
2019-05-02 10:24:44 -07:00
Michael Carroll
59d59b0c18 API updates for rmw preallocation work (#711)
* API updates for rmw preallocation work.

Signed-off-by: Michael Carroll <michael@openrobotics.org>

* Adjust for allocation in serialized message method.

Signed-off-by: Michael Carroll <michael@openrobotics.org>

* Fix extra take call.

Signed-off-by: Michael Carroll <michael@openrobotics.org>
2019-05-02 11:32:35 -05:00
jhdcs
a8a0788f81 [WIP / Re-Opened] Add functions to return formatted Node Name-Namespace strings (#698)
* Add functions to return formatted Node Name-Namespace strings

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Changed get_node_names to return fully qualified names, removed namespace method
Signed-off-by: Oswin So <oswinso@gmail.com>

* Removed unnecessary capture-by-reference

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Added first draft of tests

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Fixed bug creating phantom empty name/namespaces

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Re-ordered includes

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Swap checks to see if name is in set

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Fixed style errors from uncrustify

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Swapped to unordered_set

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Re-ordered includes again

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* nitpick: minimize vertical whitespace

see: https://google.github.io/styleguide/cppguide.html#Vertical_Whitespace

Signed-off-by: William Woodall <william@osrfoundation.org>

* Add API documentation for added get_node_names function

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Revert to last known semi-working point

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Modified expected test results

A fully-qualified name is "namespace"/"name". If namespace is set to be "/" (as they are in these tests), we would expect a qualified name of "//name"
Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Have get_node_names determine if central slash needed or not

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Corrected tests to not accept double slashes

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Undo changes to .gitignore

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Change qualified string construction to better handle invalid slashes

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Removed debugging statements

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Simplified slash-checking logic

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>
2019-05-01 17:56:05 -07:00
Tully Foote
e9101b49cd Add rcl_node_get_fully_qualified_name (ros2/rcl#255) (#712)
Signed-off-by: RARvolt <rarvolt@gmail.com>
2019-04-30 18:13:16 -07:00
Dima Dorezyuk
078d5ff662 Fixup clock (#696)
* Fix uninitialized bool in clock.cpp

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Fixup includes of clock.hpp/cpp

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Add documentation for exceptions to clock.hpp

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Adjust function signature of getters of clock.hpp/cpp

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Remove raw pointers Clock::create_jump_callback

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Remove unnecessary rclcpp namespace reference from clock.cpp

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Change exception to bad_alloc on JumpHandler allocation failure

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Fix missing nullptr check in Clock::on_time_jump

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Add JumpHandler::callback types

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Add warning for lifetime of Clock and JumpHandler

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Incorporate review

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Incorporate review

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>
2019-04-30 17:22:26 -07:00
Michael Jeronimo
dc05a2e755 Add assignment of parameter-related fields in node options constructor (#708)
* Add assignment of missing parameter-related fields in node options copy constructor.

The allow_undeclared_parameters and automatically_declare_initial_parameters fields of
the node options class were not assigned in the assignment operator, resulting in
an incorrect copy of the node options object, which also indirectly affects the
copy constructor.

Signed-off-by: Michael Jeronimo <michael.jeronimo@intel.com>

* Run linters
2019-04-30 13:47:20 -07:00
ivanpauno
98f610c114 New IntraProcessManager capable of storing shared_ptr<const T> (#690)
* Changed mapped_ring_buffer class to store both shared_ptr or unique_ptr

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Changed the IPM store and take methods

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Changed publish methods to take advantage of the new IPM

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Change how subscriptions handle intraprocess messages

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Modified publish method signatures

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Renamed 'publisher.cpp' and 'subscription.cpp' to 'publisher_base.cpp' and 'subscription_base.cpp'

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Updated lifecycle_publisher publish methods

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-04-30 16:05:53 -03:00
132 changed files with 6612 additions and 1828 deletions

View File

@@ -2,6 +2,123 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
0.7.16 (2021-05-21)
-------------------
* Fix documented example in create_publisher. (`#1558 <https://github.com/ros2/rclcpp/issues/1558>`_) (`#1560 <https://github.com/ros2/rclcpp/issues/1560>`_)
* Fix NodeOptions copy constructor. (`#1376 <https://github.com/ros2/rclcpp/issues/1376>`_) (`#1466 <https://github.com/ros2/rclcpp/issues/1466>`_)
* Contributors: Ivan Santiago Paunovic, Jacob Perron
0.7.15 (2020-11-24)
-------------------
* Fix implementation of NodeOptions::use_global_arguments(). (`#1176 <https://github.com/ros2/rclcpp/issues/1176>`_) (`#1206 <https://github.com/ros2/rclcpp/issues/1206>`_)
* Fix conversion of negative durations to messages. (`#1188 <https://github.com/ros2/rclcpp/issues/1188>`_) (`#1207 <https://github.com/ros2/rclcpp/issues/1207>`_)
* Type conversions fixes. (`#901 <https://github.com/ros2/rclcpp/issues/901>`_) (`#1209 <https://github.com/ros2/rclcpp/issues/1209>`_)
* Add operator!= for duration (`#1236 <https://github.com/ros2/rclcpp/issues/1236>`_) (`#1277 <https://github.com/ros2/rclcpp/issues/1277>`_)
* Contributors: Ivan Santiago Paunovic, Michel Hidalgo, Monika Idzik
0.7.14 (2020-07-10)
-------------------
* Fixed doxygen warnings. (`#1208 <https://github.com/ros2/rclcpp/issues/1208>`_)
* Check if context is valid when looping in spin_some. (`#1167 <https://github.com/ros2/rclcpp/issues/1167>`_)
* Fix spin_until_future_complete: check spinning value. (`#1023 <https://github.com/ros2/rclcpp/issues/1023>`_)
* Fix exception on rcl_clock_init. (`#1195 <https://github.com/ros2/rclcpp/issues/1195>`_)
* Fix lock-order-inversion (potential deadlock). (`#1138 <https://github.com/ros2/rclcpp/issues/1138>`_)
* Contributors: Alejandro Hernández Cordero, DongheeYe, Ivan Santiago Paunovic, tomoya
0.7.13 (2020-03-12)
-------------------
* Don't specify calling convention in std::_Binder template. (`#1015 <https://github.com/ros2/rclcpp/issues/1015>`_)
* Contributors: Jacob Perron, Sean Kelly
0.7.12 (2019-12-05)
-------------------
0.7.11 (2019-10-11)
-------------------
* Fix `get_node_*_interface` functions taking a pointer (`#870 <https://github.com/ros2/rclcpp/pull/870>`_).
* Fix hang with timers in `MultiThreadedExecutor` (`#869 <https://github.com/ros2/rclcpp/pull/869>`_).
* Contributors: Todd Malsbary, ivanpauno
0.7.10 (2019-09-23)
-------------------
0.7.9 (2019-09-20)
------------------
* add mutex in add/remove_node and wait_for_work to protect concurrent use/change of memory_strategy\_ (`#837 <https://github.com/ros2/rclcpp/issues/837>`_) (`#857 <https://github.com/ros2/rclcpp/issues/857>`_)
* Contributors: Zachary Michaels
0.7.8 (2019-09-06)
------------------
0.7.7 (2019-07-31)
------------------
* Enabled the creation of a parameter YAML file which is applied to each node. (`#805 <https://github.com/ros2/rclcpp/issues/805>`_)
* Fixed a signed/unsigned integer comparison compiler warning. (`#804 <https://github.com/ros2/rclcpp/issues/804>`_)
* Changed the QoS profile used when subscribing to parameter events to match the publishing side, i.e. ``rmw_qos_profile_parameter_events``. (`#798 <https://github.com/ros2/rclcpp/issues/798>`_)
* Changed the logic in TimeSource to ignore use_sim_time parameter events coming from other nodes. (`#803 <https://github.com/ros2/rclcpp/issues/803>`_)
* Added missing default values in the NodeParametersInterface. (`#794 <https://github.com/ros2/rclcpp/issues/794>`_)
* Added support for const member callback functions. (`#763 <https://github.com/ros2/rclcpp/issues/763>`_)
* Contributors: Esteve Fernandez, Gonzo, Karsten Knese, Michel Hidalgo, Scott K Logan
0.7.6 (2019-06-12)
------------------
* Ignore parameters overrides in set parameter methods when allowing undeclared parameters (`#756 <https://github.com/ros2/rclcpp/issues/756>`_)
* Add rclcpp::create_timer() (`#757 <https://github.com/ros2/rclcpp/issues/757>`_)
* checking origin of intra-process msg before taking them (`#753 <https://github.com/ros2/rclcpp/issues/753>`_)
* Contributors: Alberto Soragna, Shane Loretz, ivanpauno
0.7.5 (2019-05-30)
------------------
* Avoid 'Intra process message no longer being stored when trying to handle it' warning (`#749 <https://github.com/ros2/rclcpp/issues/749>`_)
* Contributors: ivanpauno
0.7.4 (2019-05-29)
------------------
* Rename parameter options (`#745 <https://github.com/ros2/rclcpp/issues/745>`_)
* Bionic use of strerror_r (`#742 <https://github.com/ros2/rclcpp/issues/742>`_)
* Enforce parameter ranges (`#735 <https://github.com/ros2/rclcpp/issues/735>`_)
* removed not used parameter client (`#740 <https://github.com/ros2/rclcpp/issues/740>`_)
* ensure removal of guard conditions of expired nodes from memory strategy (`#741 <https://github.com/ros2/rclcpp/issues/741>`_)
* Fix typo in log warning message (`#737 <https://github.com/ros2/rclcpp/issues/737>`_)
* Throw nice errors when creating a publisher with intraprocess communication and incompatible qos policy (`#729 <https://github.com/ros2/rclcpp/issues/729>`_)
* Contributors: Alberto Soragna, Dirk Thomas, Jacob Perron, William Woodall, ivanpauno, roderick-koehle
0.7.3 (2019-05-20)
------------------
* Fixed misspelling, volitile -> volatile (`#724 <https://github.com/ros2/rclcpp/issues/724>`_), and then fixed that since it is a C++ keyword to be ``durability_volatile`` (`#725 <https://github.com/ros2/rclcpp/issues/725>`_)
* Fixed a clang warning (`#723 <https://github.com/ros2/rclcpp/issues/723>`_)
* Added ``on_parameter_event`` static method to the ``AsyncParametersClient`` (`#688 <https://github.com/ros2/rclcpp/issues/688>`_)
* Added a guard against ``ParameterNotDeclaredException`` throwing from within the parameter service callbacks. (`#718 <https://github.com/ros2/rclcpp/issues/718>`_)
* Added missing template functionality to lifecycle_node. (`#707 <https://github.com/ros2/rclcpp/issues/707>`_)
* Fixed heap-use-after-free and memory leaks reported from ``test_node.cpp`` (`#719 <https://github.com/ros2/rclcpp/issues/719>`_)
* Contributors: Alberto Soragna, Dirk Thomas, Emerson Knapp, Jacob Perron, Michael Jeronimo, Prajakta Gokhale
0.7.2 (2019-05-08)
------------------
* Added new way to specify QoS settings for publishers and subscriptions. (`#713 <https://github.com/ros2/rclcpp/issues/713>`_)
* The new way requires that you specify a history depth when creating a publisher or subscription.
* In the past it was possible to create one without specifying any history depth, but these signatures have been deprecated.
* Deprecated ``shared_ptr`` and raw pointer versions of ``Publisher<T>::publish()``. (`#709 <https://github.com/ros2/rclcpp/issues/709>`_)
* Implemented API to set callbacks for liveliness and deadline QoS events for publishers and subscriptions. (`#695 <https://github.com/ros2/rclcpp/issues/695>`_)
* Fixed a segmentation fault when publishing a parameter event when they ought to be disabled. (`#714 <https://github.com/ros2/rclcpp/issues/714>`_)
* Changes required for upcoming pre-allocation API. (`#711 <https://github.com/ros2/rclcpp/issues/711>`_)
* Changed ``Node::get_node_names()`` to return the full node names rather than just the base name. (`#698 <https://github.com/ros2/rclcpp/issues/698>`_)
* Remove logic made redundant by the `ros2/rcl#255 <https://github.com/ros2/rcl/issues/255>`_ pull request. (`#712 <https://github.com/ros2/rclcpp/issues/712>`_)
* Various improvements for ``rclcpp::Clock``. (`#696 <https://github.com/ros2/rclcpp/issues/696>`_)
* Fixed uninitialized bool in ``clock.cpp``.
* Fixed up includes of ``clock.hpp/cpp``.
* Added documentation for exceptions to ``clock.hpp``.
* Adjusted function signature of getters of ``clock.hpp/cpp``.
* Removed raw pointers to ``Clock::create_jump_callback``.
* Removed unnecessary ``rclcpp`` namespace reference from ``clock.cpp``.
* Changed exception to ``bad_alloc`` on ``JumpHandler`` allocation failure.
* Fixed missing ``nullptr`` check in ``Clock::on_time_jump``.
* Added ``JumpHandler::callback`` types.
* Added warning for lifetime of Clock and JumpHandler
* Fixed bug left over from the `pull request #495 <https://github.com/ros2/rclcpp/pull/495>`_. (`#708 <https://github.com/ros2/rclcpp/issues/708>`_)
* Changed the ``IntraProcessManager`` to be capable of storing ``shared_ptr<const T>`` in addition to ``unique_ptr<T>``. (`#690 <https://github.com/ros2/rclcpp/issues/690>`_)
* Contributors: Alberto Soragna, Dima Dorezyuk, M. M, Michael Carroll, Michael Jeronimo, Tully Foote, William Woodall, ivanpauno, jhdcs
0.7.1 (2019-04-26)
------------------
* Added read only parameters. (`#495 <https://github.com/ros2/rclcpp/issues/495>`_)

View File

@@ -19,7 +19,11 @@ if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
# About -Wno-sign-conversion: With Clang, -Wconversion implies -Wsign-conversion. There are a number of
# implicit sign conversions in rclcpp and gtest.cc, see https://ci.ros2.org/job/ci_osx/9265/.
# Hence disabling -Wsign-conversion for now until all those have eventually been fixed.
# (from https://github.com/ros2/rclcpp/pull/1188#issuecomment-650229140)
add_compile_options(-Wall -Wextra -Wconversion -Wno-sign-conversion -Wpedantic)
endif()
include_directories(include)
@@ -64,10 +68,12 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/parameter_events_filter.cpp
src/rclcpp/parameter_map.cpp
src/rclcpp/parameter_service.cpp
src/rclcpp/publisher.cpp
src/rclcpp/publisher_base.cpp
src/rclcpp/qos.cpp
src/rclcpp/qos_event.cpp
src/rclcpp/service.cpp
src/rclcpp/signal_handler.cpp
src/rclcpp/subscription.cpp
src/rclcpp/subscription_base.cpp
src/rclcpp/time.cpp
src/rclcpp/time_source.cpp
src/rclcpp/timer.cpp
@@ -141,6 +147,8 @@ if(BUILD_TESTING)
find_package(rmw_implementation_cmake REQUIRED)
include(cmake/rclcpp_add_build_failure_test.cmake)
ament_add_gtest(test_client test/test_client.cpp)
if(TARGET test_client)
ament_target_dependencies(test_client
@@ -151,6 +159,18 @@ if(BUILD_TESTING)
)
target_link_libraries(test_client ${PROJECT_NAME})
endif()
ament_add_gtest(test_create_timer test/test_create_timer.cpp)
if(TARGET test_create_timer)
ament_target_dependencies(test_create_timer
"rcl_interfaces"
"rmw"
"rcl"
"rosidl_generator_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_create_timer ${PROJECT_NAME})
target_include_directories(test_create_timer PRIVATE test/)
endif()
ament_add_gtest(test_expand_topic_or_service_name test/test_expand_topic_or_service_name.cpp)
if(TARGET test_expand_topic_or_service_name)
ament_target_dependencies(test_expand_topic_or_service_name
@@ -200,6 +220,34 @@ if(BUILD_TESTING)
)
target_link_libraries(test_node ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_interfaces__get_node_interfaces
test/node_interfaces/test_get_node_interfaces.cpp)
if(TARGET test_node_interfaces__get_node_interfaces)
target_link_libraries(test_node_interfaces__get_node_interfaces ${PROJECT_NAME})
endif()
# TODO(wjwwood): reenable these build failure tests when I can get Jenkins to ignore their output
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ref_rclcpp_node
# test/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ref_rclcpp_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ref_rclcpp_node
# ${PROJECT_NAME})
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ptr_rclcpp_node
# test/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ptr_rclcpp_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ptr_rclcpp_node
# ${PROJECT_NAME})
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ref_wrapped_node
# test/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ref_wrapped_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ref_rclcpp_node
# ${PROJECT_NAME})
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ptr_wrapped_node
# test/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ptr_wrapped_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ptr_rclcpp_node
# ${PROJECT_NAME})
ament_add_gtest(test_node_global_args test/test_node_global_args.cpp)
if(TARGET test_node_global_args)
ament_target_dependencies(test_node_global_args
@@ -210,6 +258,13 @@ if(BUILD_TESTING)
)
target_link_libraries(test_node_global_args ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter_client test/test_parameter_client.cpp)
if(TARGET test_parameter_client)
ament_target_dependencies(test_parameter_client
"rcl_interfaces"
)
target_link_libraries(test_parameter_client ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter_events_filter test/test_parameter_events_filter.cpp)
if(TARGET test_parameter_events_filter)
ament_target_dependencies(test_parameter_events_filter
@@ -244,15 +299,6 @@ if(BUILD_TESTING)
)
target_link_libraries(test_publisher ${PROJECT_NAME})
endif()
ament_add_gtest(test_pub_sub_option_interface test/test_pub_sub_option_interface.cpp)
if(TARGET test_pub_sub_option_interface)
ament_target_dependencies(test_pub_sub_option_interface
test_msgs
)
target_link_libraries(test_pub_sub_option_interface
${PROJECT_NAME}
)
endif()
ament_add_gtest(test_publisher_subscription_count_api test/test_publisher_subscription_count_api.cpp)
if(TARGET test_publisher_subscription_count_api)
ament_target_dependencies(test_publisher_subscription_count_api

View File

@@ -0,0 +1,56 @@
# Copyright 2019 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Register a test which tries to compile a file and expects it to fail to build.
#
# This will create two targets, one by the given target name and a test target
# which has the same name prefixed with `test_`.
# For example, if target is `should_not_compile__use_const_argument` then there
# will be an executable target called `should_not_compile__use_const_argument`
# and a test target called `test_should_not_compile__use_const_argument`.
#
# :param target: the name of the target to be created
# :type target: string
# :param ARGN: the list of source files to be used to create the test executable
# :type ARGN: list of strings
#
macro(rclcpp_add_build_failure_test target)
if(${ARGC} EQUAL 0)
message(
FATAL_ERROR
"rclcpp_add_build_failure_test() requires a target name and "
"at least one source file")
endif()
add_executable(${target} ${ARGN})
set_target_properties(${target}
PROPERTIES
EXCLUDE_FROM_ALL TRUE
EXCLUDE_FROM_DEFAULT_BUILD TRUE)
add_test(
NAME test_${target}
COMMAND
${CMAKE_COMMAND}
--build .
--target ${target}
--config $<CONFIGURATION>
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_tests_properties(test_${target}
PROPERTIES
WILL_FAIL TRUE
LABELS "build_failure"
)
endmacro()

View File

@@ -65,7 +65,8 @@ void * retyped_reallocate(void * untyped_pointer, size_t size, void * untyped_al
// Convert a std::allocator_traits-formatted Allocator into an rcl allocator
template<
typename T, typename Alloc,
typename T,
typename Alloc,
typename std::enable_if<!std::is_same<Alloc, std::allocator<void>>::value>::type * = nullptr>
rcl_allocator_t get_rcl_allocator(Alloc & allocator)
{
@@ -83,7 +84,8 @@ rcl_allocator_t get_rcl_allocator(Alloc & allocator)
// TODO(jacquelinekay) Workaround for an incomplete implementation of std::allocator<void>
template<
typename T, typename Alloc,
typename T,
typename Alloc,
typename std::enable_if<std::is_same<Alloc, std::allocator<void>>::value>::type * = nullptr>
rcl_allocator_t get_rcl_allocator(Alloc & allocator)
{

View File

@@ -36,6 +36,7 @@ class AnySubscriptionCallback
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
using SharedPtrCallback = std::function<void (const std::shared_ptr<MessageT>)>;
@@ -154,7 +155,6 @@ public:
void dispatch(
std::shared_ptr<MessageT> message, const rmw_message_info_t & message_info)
{
(void)message_info;
if (shared_ptr_callback_) {
shared_ptr_callback_(message);
} else if (shared_ptr_with_info_callback_) {
@@ -177,30 +177,50 @@ public:
}
void dispatch_intra_process(
MessageUniquePtr & message, const rmw_message_info_t & message_info)
ConstMessageSharedPtr message, const rmw_message_info_t & message_info)
{
if (const_shared_ptr_callback_) {
const_shared_ptr_callback_(message);
} else if (const_shared_ptr_with_info_callback_) {
const_shared_ptr_with_info_callback_(message, message_info);
} else {
if (unique_ptr_callback_ || unique_ptr_with_info_callback_ ||
shared_ptr_callback_ || shared_ptr_with_info_callback_)
{
throw std::runtime_error("unexpected dispatch_intra_process const shared "
"message call with no const shared_ptr callback");
} else {
throw std::runtime_error("unexpected message without any callback set");
}
}
}
void dispatch_intra_process(
MessageUniquePtr message, const rmw_message_info_t & message_info)
{
(void)message_info;
if (shared_ptr_callback_) {
typename std::shared_ptr<MessageT> shared_message = std::move(message);
shared_ptr_callback_(shared_message);
} else if (shared_ptr_with_info_callback_) {
typename std::shared_ptr<MessageT> shared_message = std::move(message);
shared_ptr_with_info_callback_(shared_message, message_info);
} else if (const_shared_ptr_callback_) {
typename std::shared_ptr<MessageT const> const_shared_message = std::move(message);
const_shared_ptr_callback_(const_shared_message);
} else if (const_shared_ptr_with_info_callback_) {
typename std::shared_ptr<MessageT const> const_shared_message = std::move(message);
const_shared_ptr_with_info_callback_(const_shared_message, message_info);
} else if (unique_ptr_callback_) {
unique_ptr_callback_(std::move(message));
} else if (unique_ptr_with_info_callback_) {
unique_ptr_with_info_callback_(std::move(message), message_info);
} else if (const_shared_ptr_callback_ || const_shared_ptr_with_info_callback_) {
throw std::runtime_error("unexpected dispatch_intra_process unique message call"
" with const shared_ptr callback");
} else {
throw std::runtime_error("unexpected message without any callback set");
}
}
bool use_take_shared_method()
{
return const_shared_ptr_callback_ || const_shared_ptr_with_info_callback_;
}
private:
std::shared_ptr<MessageAlloc> message_allocator_;
MessageDeleter message_deleter_;

View File

@@ -21,6 +21,7 @@
#include <vector>
#include "rclcpp/client.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/timer.hpp"
@@ -92,6 +93,10 @@ public:
protected:
RCLCPP_DISABLE_COPY(CallbackGroup)
RCLCPP_PUBLIC
void
add_publisher(const rclcpp::PublisherBase::SharedPtr publisher_ptr);
RCLCPP_PUBLIC
void
add_subscription(const rclcpp::SubscriptionBase::SharedPtr subscription_ptr);

View File

@@ -16,9 +16,6 @@
#define RCLCPP__CLOCK_HPP_
#include <functional>
#include <memory>
#include <mutex>
#include <vector>
#include "rclcpp/macros.hpp"
#include "rclcpp/time.hpp"
@@ -35,13 +32,17 @@ class JumpHandler
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(JumpHandler)
using pre_callback_t = std::function<void ()>;
using post_callback_t = std::function<void (const rcl_time_jump_t &)>;
JumpHandler(
std::function<void()> pre_callback,
std::function<void(const rcl_time_jump_t &)> post_callback,
pre_callback_t pre_callback,
post_callback_t post_callback,
const rcl_jump_threshold_t & threshold);
std::function<void()> pre_callback;
std::function<void(const rcl_time_jump_t &)> post_callback;
pre_callback_t pre_callback;
post_callback_t post_callback;
rcl_jump_threshold_t notice_threshold;
};
@@ -50,38 +51,74 @@ class Clock
public:
RCLCPP_SMART_PTR_DEFINITIONS(Clock)
/// Default c'tor
/**
* Initializes the clock instance with the given clock_type.
*
* \param clock_type type of the clock.
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
*/
RCLCPP_PUBLIC
explicit Clock(rcl_clock_type_t clock_type = RCL_SYSTEM_TIME);
RCLCPP_PUBLIC
~Clock();
/**
* Returns current time from the time source specified by clock_type.
*
* \return current time.
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
*/
RCLCPP_PUBLIC
Time
now();
/**
* Returns the clock of the type `RCL_ROS_TIME` is active.
*
* \return true if the clock is active
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw if
* the current clock does not have the clock_type `RCL_ROS_TIME`.
*/
RCLCPP_PUBLIC
bool
ros_time_is_active();
RCLCPP_PUBLIC
rcl_clock_t *
get_clock_handle();
get_clock_handle() noexcept;
RCLCPP_PUBLIC
rcl_clock_type_t
get_clock_type();
get_clock_type() const noexcept;
// Add a callback to invoke if the jump threshold is exceeded.
/**
* These callback functions must remain valid as long as the
* returned shared pointer is valid.
*
* Function will register callbacks to the callback queue. On time jump all
* callbacks will be executed whose threshold is greater then the time jump;
* The logic will first call selected pre_callbacks and then all selected
* post_callbacks.
*
* Function is only applicable if the clock_type is `RCL_ROS_TIME`
*
* \param pre_callback Must be non-throwing
* \param post_callback Must be non-throwing.
* \param threshold Callbacks will be triggered if the time jump is greater
* then the threshold.
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
* \throws std::bad_alloc if the allocation of the JumpHandler fails.
* \warning the instance of the clock must remain valid as long as any created
* JumpHandler.
*/
RCLCPP_PUBLIC
JumpHandler::SharedPtr
create_jump_callback(
std::function<void()> pre_callback,
std::function<void(const rcl_time_jump_t &)> post_callback,
JumpHandler::pre_callback_t pre_callback,
JumpHandler::post_callback_t post_callback,
const rcl_jump_threshold_t & threshold);
private:

View File

@@ -18,19 +18,30 @@
#include <memory>
#include <string>
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/node_options.hpp"
#include "rclcpp/publisher_factory.hpp"
#include "rclcpp/publisher_options.hpp"
#include "rclcpp/qos.hpp"
#include "rmw/qos_profiles.h"
namespace rclcpp
{
template<typename MessageT, typename AllocatorT, typename PublisherT>
template<
typename MessageT,
typename AllocatorT = std::allocator<void>,
typename PublisherT = ::rclcpp::Publisher<MessageT, AllocatorT>>
// cppcheck-suppress syntaxError // bug in cppcheck 1.82 for [[deprecated]] on templated function
[[deprecated("use alternative rclcpp::create_publisher() signatures")]]
std::shared_ptr<PublisherT>
create_publisher(
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
const std::string & topic_name,
const rmw_qos_profile_t & qos_profile,
const PublisherEventCallbacks & event_callbacks,
rclcpp::callback_group::CallbackGroup::SharedPtr group,
bool use_intra_process_comms,
std::shared_ptr<AllocatorT> allocator)
{
@@ -39,10 +50,69 @@ create_publisher(
auto pub = node_topics->create_publisher(
topic_name,
rclcpp::create_publisher_factory<MessageT, AllocatorT, PublisherT>(allocator),
rclcpp::create_publisher_factory<MessageT, AllocatorT, PublisherT>(event_callbacks, allocator),
publisher_options,
use_intra_process_comms);
node_topics->add_publisher(pub);
node_topics->add_publisher(pub, group);
return std::dynamic_pointer_cast<PublisherT>(pub);
}
/// Create and return a publisher of the given MessageT type.
/**
* The NodeT type only needs to have a method called get_node_topics_interface()
* which returns a shared_ptr to a NodeTopicsInterface.
*/
template<
typename MessageT,
typename AllocatorT = std::allocator<void>,
typename PublisherT = ::rclcpp::Publisher<MessageT, AllocatorT>,
typename NodeT>
std::shared_ptr<PublisherT>
create_publisher(
NodeT & node,
const std::string & topic_name,
const rclcpp::QoS & qos,
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (
rclcpp::PublisherOptionsWithAllocator<AllocatorT>()
))
{
using rclcpp::node_interfaces::get_node_topics_interface;
auto node_topics = get_node_topics_interface(node);
std::shared_ptr<AllocatorT> allocator = options.allocator;
if (!allocator) {
allocator = std::make_shared<AllocatorT>();
}
bool use_intra_process;
switch (options.use_intra_process_comm) {
case IntraProcessSetting::Enable:
use_intra_process = true;
break;
case IntraProcessSetting::Disable:
use_intra_process = false;
break;
case IntraProcessSetting::NodeDefault:
use_intra_process = node_topics->get_node_base_interface()->get_use_intra_process_default();
break;
default:
throw std::runtime_error("Unrecognized IntraProcessSetting value");
break;
}
// TODO(wjwwood): convert all of the interfaces to use QoS and PublisherOptionsBase
auto pub = node_topics->create_publisher(
topic_name,
rclcpp::create_publisher_factory<MessageT, AllocatorT, PublisherT>(
options.event_callbacks,
allocator
),
options.template to_rcl_publisher_options<MessageT>(qos),
use_intra_process
);
node_topics->add_publisher(pub, options.callback_group);
return std::dynamic_pointer_cast<PublisherT>(pub);
}

View File

@@ -19,8 +19,11 @@
#include <string>
#include <utility>
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/subscription_factory.hpp"
#include "rclcpp/subscription_options.hpp"
#include "rclcpp/qos.hpp"
#include "rmw/qos_profiles.h"
namespace rclcpp
@@ -32,12 +35,15 @@ template<
typename AllocatorT,
typename CallbackMessageT,
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>>
// cppcheck-suppress syntaxError // bug in cppcheck 1.82 for [[deprecated]] on templated function
[[deprecated("use alternative rclcpp::create_subscription() signatures")]]
typename std::shared_ptr<SubscriptionT>
create_subscription(
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
const std::string & topic_name,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile,
const SubscriptionEventCallbacks & event_callbacks,
rclcpp::callback_group::CallbackGroup::SharedPtr group,
bool ignore_local_publications,
bool use_intra_process_comms,
@@ -52,7 +58,10 @@ create_subscription(
auto factory = rclcpp::create_subscription_factory
<MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT>(
std::forward<CallbackT>(callback), msg_mem_strat, allocator);
std::forward<CallbackT>(callback),
event_callbacks,
msg_mem_strat,
allocator);
auto sub = node_topics->create_subscription(
topic_name,
@@ -63,6 +72,75 @@ create_subscription(
return std::dynamic_pointer_cast<SubscriptionT>(sub);
}
/// Create and return a subscription of the given MessageT type.
/**
* The NodeT type only needs to have a method called get_node_topics_interface()
* which returns a shared_ptr to a NodeTopicsInterface, or be a
* NodeTopicsInterface pointer itself.
*/
template<
typename MessageT,
typename CallbackT,
typename AllocatorT = std::allocator<void>,
typename CallbackMessageT =
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
typename NodeT>
typename std::shared_ptr<SubscriptionT>
create_subscription(
NodeT && node,
const std::string & topic_name,
const rclcpp::QoS & qos,
CallbackT && callback,
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
),
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT, AllocatorT>::SharedPtr
msg_mem_strat = nullptr)
{
using rclcpp::node_interfaces::get_node_topics_interface;
auto node_topics = get_node_topics_interface(std::forward<NodeT>(node));
if (!msg_mem_strat) {
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
msg_mem_strat = MessageMemoryStrategy<CallbackMessageT, AllocatorT>::create_default();
}
std::shared_ptr<AllocatorT> allocator = options.allocator;
if (!allocator) {
allocator = std::make_shared<AllocatorT>();
}
auto factory = rclcpp::create_subscription_factory
<MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT>(
std::forward<CallbackT>(callback), options.event_callbacks, msg_mem_strat, allocator);
bool use_intra_process;
switch (options.use_intra_process_comm) {
case IntraProcessSetting::Enable:
use_intra_process = true;
break;
case IntraProcessSetting::Disable:
use_intra_process = false;
break;
case IntraProcessSetting::NodeDefault:
use_intra_process = node_topics->get_node_base_interface()->get_use_intra_process_default();
break;
default:
throw std::runtime_error("Unrecognized IntraProcessSetting value");
break;
}
// TODO(wjwwood): convert all of the interfaces to use QoS and SubscriptionOptionsBase
auto sub = node_topics->create_subscription(
topic_name,
factory,
options.template to_rcl_subscription_options<MessageT>(qos),
use_intra_process);
node_topics->add_subscription(sub, options.callback_group);
return std::dynamic_pointer_cast<SubscriptionT>(sub);
}
} // namespace rclcpp
#endif // RCLCPP__CREATE_SUBSCRIPTION_HPP_

View File

@@ -0,0 +1,73 @@
// Copyright 2019 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_TIMER_HPP_
#define RCLCPP__CREATE_TIMER_HPP_
#include <memory>
#include <string>
#include <utility>
#include "rclcpp/duration.hpp"
# include "rclcpp/node_interfaces/get_node_base_interface.hpp"
# include "rclcpp/node_interfaces/get_node_timers_interface.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
namespace rclcpp
{
/// Create a timer with a given clock
/// \internal
template<typename CallbackT>
typename rclcpp::TimerBase::SharedPtr
create_timer(
node_interfaces::NodeBaseInterface * node_base,
node_interfaces::NodeTimersInterface * node_timers,
rclcpp::Clock::SharedPtr clock,
rclcpp::Duration period,
CallbackT && callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr)
{
auto timer = rclcpp::GenericTimer<CallbackT>::make_shared(
clock,
period.to_chrono<std::chrono::nanoseconds>(),
std::forward<CallbackT>(callback),
node_base->get_context());
node_timers->add_timer(timer, group);
return timer;
}
/// Create a timer with a given clock
template<typename NodeT, typename CallbackT>
typename rclcpp::TimerBase::SharedPtr
create_timer(
NodeT node,
rclcpp::Clock::SharedPtr clock,
rclcpp::Duration period,
CallbackT && callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr)
{
return create_timer(
rclcpp::node_interfaces::get_node_base_interface(node),
rclcpp::node_interfaces::get_node_timers_interface(node),
clock,
period,
std::forward<CallbackT>(callback),
group);
}
} // namespace rclcpp
#endif // RCLCPP__CREATE_TIMER_HPP_

View File

@@ -23,91 +23,90 @@
namespace rclcpp
{
class Duration
class RCLCPP_PUBLIC Duration
{
public:
RCLCPP_PUBLIC
Duration(int32_t seconds, uint32_t nanoseconds);
RCLCPP_PUBLIC
explicit Duration(
rcl_duration_value_t nanoseconds);
explicit Duration(rcl_duration_value_t nanoseconds);
RCLCPP_PUBLIC
explicit Duration(
std::chrono::nanoseconds nanoseconds);
explicit Duration(std::chrono::nanoseconds nanoseconds);
RCLCPP_PUBLIC
Duration(
const builtin_interfaces::msg::Duration & duration_msg);
// intentionally not using explicit to create a conversion constructor
template<class Rep, class Period>
// cppcheck-suppress noExplicitConstructor
Duration(const std::chrono::duration<Rep, Period> & duration) // NOLINT(runtime/explicit)
: Duration(std::chrono::duration_cast<std::chrono::nanoseconds>(duration))
{}
// cppcheck-suppress noExplicitConstructor
Duration(const builtin_interfaces::msg::Duration & duration_msg); // NOLINT(runtime/explicit)
RCLCPP_PUBLIC
explicit Duration(const rcl_duration_t & duration);
RCLCPP_PUBLIC
Duration(const Duration & rhs);
RCLCPP_PUBLIC
virtual ~Duration();
virtual ~Duration() = default;
RCLCPP_PUBLIC
operator builtin_interfaces::msg::Duration() const;
RCLCPP_PUBLIC
// cppcheck-suppress operatorEq // this is a false positive from cppcheck
Duration &
operator=(const Duration & rhs);
RCLCPP_PUBLIC
Duration &
operator=(const builtin_interfaces::msg::Duration & Duration_msg);
operator=(const builtin_interfaces::msg::Duration & duration_msg);
RCLCPP_PUBLIC
bool
operator==(const rclcpp::Duration & rhs) const;
RCLCPP_PUBLIC
bool
operator!=(const rclcpp::Duration & rhs) const;
bool
operator<(const rclcpp::Duration & rhs) const;
RCLCPP_PUBLIC
bool
operator<=(const rclcpp::Duration & rhs) const;
RCLCPP_PUBLIC
bool
operator>=(const rclcpp::Duration & rhs) const;
RCLCPP_PUBLIC
bool
operator>(const rclcpp::Duration & rhs) const;
RCLCPP_PUBLIC
Duration
operator+(const rclcpp::Duration & rhs) const;
RCLCPP_PUBLIC
Duration
operator-(const rclcpp::Duration & rhs) const;
RCLCPP_PUBLIC
static Duration
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;
template<class DurationT>
DurationT
to_chrono() const
{
return std::chrono::duration_cast<DurationT>(std::chrono::nanoseconds(this->nanoseconds()));
}
rmw_time_t
to_rmw_time() const;
private:
rcl_duration_t rcl_duration_;
};

View File

@@ -110,13 +110,15 @@ public:
* \throws std::runtime_error if the rcl_get_error_state returns 0
* \throws RCLErrorBase some child class exception based on ret
*/
/* *INDENT-OFF* */ // Uncrustify cannot yet understand [[noreturn]] properly
RCLCPP_PUBLIC
void
throw_from_rcl_error(
throw_from_rcl_error [[noreturn]] (
rcl_ret_t ret,
const std::string & prefix = "",
const rcl_error_state_t * error_state = nullptr,
void (* reset_error)() = rcl_reset_error);
/* *INDENT-ON* */
class RCLErrorBase
{

View File

@@ -22,6 +22,7 @@
#include <iostream>
#include <list>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
@@ -34,6 +35,7 @@
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/scope_exit.hpp"
namespace rclcpp
{
@@ -242,9 +244,14 @@ public:
}
std::chrono::nanoseconds timeout_left = timeout_ns;
while (rclcpp::ok(this->context_)) {
if (spinning.exchange(true)) {
throw std::runtime_error("spin_until_future_complete() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
while (rclcpp::ok(this->context_) && spinning.load()) {
// Do one item of work.
spin_once(timeout_left);
spin_once_impl(timeout_left);
// Check if the future is set, return SUCCESS if it is.
status = future.wait_for(std::chrono::seconds(0));
if (status == std::future_status::ready) {
@@ -355,6 +362,9 @@ protected:
/// Wait set for managing entities that the rmw layer waits on.
rcl_wait_set_t wait_set_ = rcl_get_zero_initialized_wait_set();
// Mutex to protect the subsequent memory_strategy_.
std::mutex memory_strategy_mutex_;
/// The memory strategy: an interface for handling user-defined memory allocation strategies.
memory_strategy::MemoryStrategy::SharedPtr memory_strategy_;
@@ -364,7 +374,12 @@ protected:
private:
RCLCPP_DISABLE_COPY(Executor)
std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> weak_nodes_;
RCLCPP_PUBLIC
void
spin_once_impl(std::chrono::nanoseconds timeout);
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> weak_nodes_;
std::list<const rcl_guard_condition_t *> guard_conditions_;
};
} // namespace executor

View File

@@ -48,6 +48,7 @@ public:
* \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
* \param timeout maximum time to wait
*/
RCLCPP_PUBLIC
MultiThreadedExecutor(

View File

@@ -88,8 +88,24 @@ struct function_traits<std::_Bind<ReturnTypeT(ClassT::*(FArgs ...))(Args ...)>>
struct function_traits<std::_Bind<std::_Mem_fn<ReturnTypeT (ClassT::*)(Args ...)>(FArgs ...)>>
#elif defined _MSC_VER // MS Visual Studio
struct function_traits<
std::_Binder<std::_Unforced, ReturnTypeT(__cdecl ClassT::*)(Args ...), FArgs ...>
>
std::_Binder<std::_Unforced, ReturnTypeT (ClassT::*)(Args ...), FArgs ...>>
#else
#error "Unsupported C++ compiler / standard library"
#endif
: function_traits<ReturnTypeT(Args ...)>
{};
// std::bind for object const methods
template<typename ClassT, typename ReturnTypeT, typename ... Args, typename ... FArgs>
#if defined _LIBCPP_VERSION // libc++ (Clang)
struct function_traits<std::__bind<ReturnTypeT (ClassT::*)(Args ...) const, FArgs ...>>
#elif defined _GLIBCXX_RELEASE // glibc++ (GNU C++ >= 7.1)
struct function_traits<std::_Bind<ReturnTypeT(ClassT::*(FArgs ...))(Args ...) const>>
#elif defined __GLIBCXX__ // glibc++ (GNU C++)
struct function_traits<std::_Bind<std::_Mem_fn<ReturnTypeT (ClassT::*)(Args ...) const>(FArgs ...)>>
#elif defined _MSC_VER // MS Visual Studio
struct function_traits<
std::_Binder<std::_Unforced, ReturnTypeT (ClassT::*)(Args ...) const, FArgs ...>>
#else
#error "Unsupported C++ compiler / standard library"
#endif
@@ -103,7 +119,7 @@ struct function_traits<std::__bind<ReturnTypeT( &)(Args ...), FArgs ...>>
#elif defined __GLIBCXX__ // glibc++ (GNU C++)
struct function_traits<std::_Bind<ReturnTypeT(*(FArgs ...))(Args ...)>>
#elif defined _MSC_VER // MS Visual Studio
struct function_traits<std::_Binder<std::_Unforced, ReturnTypeT(__cdecl &)(Args ...), FArgs ...>>
struct function_traits<std::_Binder<std::_Unforced, ReturnTypeT( &)(Args ...), FArgs ...>>
#else
#error "Unsupported C++ compiler / standard library"
#endif

View File

@@ -25,14 +25,15 @@
#include <memory>
#include <mutex>
#include <unordered_map>
#include <utility>
#include <set>
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/intra_process_manager_impl.hpp"
#include "rclcpp/mapped_ring_buffer.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/publisher_base.hpp"
#include "rclcpp/subscription_base.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
@@ -184,21 +185,11 @@ public:
* \param buffer_size if 0 (default) a size is calculated based on the QoS.
* \return an unsigned 64-bit integer which is the publisher's unique id.
*/
template<typename MessageT, typename Alloc>
RCLCPP_PUBLIC
uint64_t
add_publisher(
typename Publisher<MessageT, Alloc>::SharedPtr publisher,
size_t buffer_size = 0)
{
auto id = IntraProcessManager::get_next_unique_id();
size_t size = buffer_size > 0 ? buffer_size : publisher->get_queue_size();
auto mrb = mapped_ring_buffer::MappedRingBuffer<
MessageT,
typename Publisher<MessageT, Alloc>::MessageAlloc
>::make_shared(size, publisher->get_allocator());
impl_->add_publisher(id, publisher, mrb, size);
return id;
}
rclcpp::PublisherBase::SharedPtr publisher,
size_t buffer_size = 0);
/// Unregister a publisher using the publisher's unique id.
/**
@@ -242,12 +233,11 @@ public:
* \return the message sequence number.
*/
template<
typename MessageT, typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>>
typename MessageT, typename Alloc = std::allocator<void>>
uint64_t
store_intra_process_message(
uint64_t intra_process_publisher_id,
std::unique_ptr<MessageT, Deleter> & message)
std::shared_ptr<const MessageT> message)
{
using MRBMessageAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<MessageT>;
using TypedMRB = typename mapped_ring_buffer::MappedRingBuffer<MessageT, MRBMessageAlloc>;
@@ -270,6 +260,35 @@ public:
return message_seq;
}
template<
typename MessageT, typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>>
uint64_t
store_intra_process_message(
uint64_t intra_process_publisher_id,
std::unique_ptr<MessageT, Deleter> message)
{
using MRBMessageAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<MessageT>;
using TypedMRB = typename mapped_ring_buffer::MappedRingBuffer<MessageT, MRBMessageAlloc>;
uint64_t message_seq = 0;
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer = impl_->get_publisher_info_for_id(
intra_process_publisher_id, message_seq);
typename TypedMRB::SharedPtr typed_buffer = std::static_pointer_cast<TypedMRB>(buffer);
if (!typed_buffer) {
throw std::runtime_error("Typecast failed due to incorrect message type");
}
// Insert the message into the ring buffer using the message_seq to identify it.
bool did_replace = typed_buffer->push_and_replace(message_seq, std::move(message));
// TODO(wjwwood): do something when a message was displaced. log debug?
(void)did_replace; // Avoid unused variable warning.
impl_->store_intra_process_message(intra_process_publisher_id, message_seq);
// Return the message sequence which is sent to the subscription.
return message_seq;
}
/// Take an intra process message.
/**
* The intra_process_publisher_id and message_sequence_number parameters
@@ -334,10 +353,45 @@ public:
// Return a copy or the unique_ptr (ownership) depending on how many subscriptions are left.
if (target_subs_size) {
// There are more subscriptions to serve, return a copy.
typed_buffer->get_copy_at_key(message_sequence_number, message);
typed_buffer->get(message_sequence_number, message);
} else {
// This is the last one to be returned, transfer ownership.
typed_buffer->pop_at_key(message_sequence_number, message);
typed_buffer->pop(message_sequence_number, message);
}
}
template<
typename MessageT, typename Alloc = std::allocator<void>>
void
take_intra_process_message(
uint64_t intra_process_publisher_id,
uint64_t message_sequence_number,
uint64_t requesting_subscriptions_intra_process_id,
std::shared_ptr<const MessageT> & message)
{
using MRBMessageAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<MessageT>;
using TypedMRB = mapped_ring_buffer::MappedRingBuffer<MessageT, MRBMessageAlloc>;
message = nullptr;
size_t target_subs_size = 0;
std::lock_guard<std::mutex> lock(take_mutex_);
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer = impl_->take_intra_process_message(
intra_process_publisher_id,
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;
}
// Return a copy or the unique_ptr (ownership) depending on how many subscriptions are left.
if (target_subs_size) {
// There are more subscriptions to serve, return a copy.
typed_buffer->get(message_sequence_number, message);
} else {
// This is the last one to be returned, transfer ownership.
typed_buffer->pop(message_sequence_number, message);
}
}

View File

@@ -34,8 +34,8 @@
#include "rclcpp/macros.hpp"
#include "rclcpp/mapped_ring_buffer.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/publisher_base.hpp"
#include "rclcpp/subscription_base.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp

View File

@@ -20,6 +20,7 @@
#include <cstdint>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <utility>
#include <vector>
@@ -38,7 +39,7 @@ public:
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBufferBase)
};
/// Ring buffer container of unique_ptr's of T, which can be accessed by a key.
/// Ring buffer container of shared_ptr's or unique_ptr's of T, which can be accessed by a key.
/**
* T must be a CopyConstructable and CopyAssignable.
* This class can be used in a container by using the base class MappedRingBufferBase.
@@ -64,6 +65,7 @@ public:
using ElemAlloc = typename ElemAllocTraits::allocator_type;
using ElemDeleter = allocator::Deleter<ElemAlloc, T>;
using ConstElemSharedPtr = std::shared_ptr<const T>;
using ElemUniquePtr = std::unique_ptr<T, ElemDeleter>;
/// Constructor.
@@ -101,57 +103,33 @@ public:
* \param value if the key is found, the value is stored in this parameter
*/
void
get_copy_at_key(uint64_t key, ElemUniquePtr & value)
get(uint64_t key, ElemUniquePtr & value)
{
std::lock_guard<std::mutex> lock(data_mutex_);
auto it = get_iterator_of_key(key);
value = nullptr;
if (it != elements_.end() && it->in_use) {
auto ptr = ElemAllocTraits::allocate(*allocator_.get(), 1);
ElemAllocTraits::construct(*allocator_.get(), ptr, *it->value);
value = ElemUniquePtr(ptr);
if (it->unique_value) {
ElemDeleter deleter = it->unique_value.get_deleter();
auto ptr = ElemAllocTraits::allocate(*allocator_.get(), 1);
ElemAllocTraits::construct(*allocator_.get(), ptr, *it->unique_value);
value = ElemUniquePtr(ptr, deleter);
} else if (it->shared_value) {
ElemDeleter * deleter = std::get_deleter<ElemDeleter, const T>(it->shared_value);
auto ptr = ElemAllocTraits::allocate(*allocator_.get(), 1);
ElemAllocTraits::construct(*allocator_.get(), ptr, *it->shared_value);
if (deleter) {
value = ElemUniquePtr(ptr, *deleter);
} else {
value = ElemUniquePtr(ptr);
}
} else {
throw std::runtime_error("Unexpected empty MappedRingBuffer element.");
}
}
}
/// Return ownership of the value stored in the ring buffer, leaving a copy.
/**
* The key is matched if an element in the ring bufer has a matching key.
* This method will allocate in order to store a copy.
*
* The key is not guaranteed to be unique, see the class docs for more.
*
* The ownership of the currently stored object is returned, but a copy is
* made and stored in its place.
* This means that multiple calls to this function for a particular element
* will result in returning the copied and stored object not the original.
* This also means that later calls to pop_at_key will not return the
* originally stored object, since it was returned by the first call to this
* method.
*
* The contents of value before the method is called are discarded.
*
* \param key the key associated with the stored value
* \param value if the key is found, the value is stored in this parameter
*/
void
get_ownership_at_key(uint64_t key, ElemUniquePtr & value)
{
std::lock_guard<std::mutex> lock(data_mutex_);
auto it = get_iterator_of_key(key);
value = nullptr;
if (it != elements_.end() && it->in_use) {
// Make a copy.
auto ptr = ElemAllocTraits::allocate(*allocator_.get(), 1);
ElemAllocTraits::construct(*allocator_.get(), ptr, *it->value);
auto copy = ElemUniquePtr(ptr);
// Return the original.
value.swap(it->value);
// Store the copy.
it->value.swap(copy);
}
}
/// Return ownership of the value stored in the ring buffer at the given key.
/// Share ownership of the value stored in the ring buffer at the given key.
/**
* The key is matched if an element in the ring buffer has a matching key.
*
@@ -163,13 +141,90 @@ public:
* \param value if the key is found, the value is stored in this parameter
*/
void
pop_at_key(uint64_t key, ElemUniquePtr & value)
get(uint64_t key, ConstElemSharedPtr & value)
{
std::lock_guard<std::mutex> lock(data_mutex_);
auto it = get_iterator_of_key(key);
value.reset();
if (it != elements_.end() && it->in_use) {
if (!it->shared_value) {
// The stored unique_ptr is upgraded to a shared_ptr here.
// All the remaining get and pop calls done with unique_ptr
// signature will receive a copy.
if (!it->unique_value) {
throw std::runtime_error("Unexpected empty MappedRingBuffer element.");
}
it->shared_value = std::move(it->unique_value);
}
value = it->shared_value;
}
}
/// Give the ownership of the stored value to the caller if possible, or copy and release.
/**
* The key is matched if an element in the ring buffer has a matching key.
* This method may allocate in order to return a copy.
*
* If the stored value is a shared_ptr, it is not possible to downgrade it to a unique_ptr.
* In that case, a copy is returned and the stored value is released.
*
* The key is not guaranteed to be unique, see the class docs for more.
*
* The contents of value before the method is called are discarded.
*
* \param key the key associated with the stored value
* \param value if the key is found, the value is stored in this parameter
*/
void
pop(uint64_t key, ElemUniquePtr & value)
{
std::lock_guard<std::mutex> lock(data_mutex_);
auto it = get_iterator_of_key(key);
value = nullptr;
if (it != elements_.end() && it->in_use) {
value.swap(it->value);
if (it->unique_value) {
value = std::move(it->unique_value);
} else if (it->shared_value) {
auto ptr = ElemAllocTraits::allocate(*allocator_.get(), 1);
ElemAllocTraits::construct(*allocator_.get(), ptr, *it->shared_value);
auto deleter = std::get_deleter<ElemDeleter, const T>(it->shared_value);
if (deleter) {
value = ElemUniquePtr(ptr, *deleter);
} else {
value = ElemUniquePtr(ptr);
}
it->shared_value.reset();
} else {
throw std::runtime_error("Unexpected empty MappedRingBuffer element.");
}
it->in_use = false;
}
}
/// Give the ownership of the stored value to the caller, at the given key.
/**
* The key is matched if an element in the ring buffer has a matching key.
*
* The key is not guaranteed to be unique, see the class docs for more.
*
* The contents of value before the method is called are discarded.
*
* \param key the key associated with the stored value
* \param value if the key is found, the value is stored in this parameter
*/
void
pop(uint64_t key, ConstElemSharedPtr & value)
{
std::lock_guard<std::mutex> lock(data_mutex_);
auto it = get_iterator_of_key(key);
if (it != elements_.end() && it->in_use) {
if (it->shared_value) {
value = std::move(it->shared_value);
} else if (it->unique_value) {
value = std::move(it->unique_value);
} else {
throw std::runtime_error("Unexpected empty MappedRingBuffer element.");
}
it->in_use = false;
}
}
@@ -180,29 +235,44 @@ public:
* It is up to the user to ensure the key is unique.
* This method should not allocate memory.
*
* After insertion, if a pair was replaced, then value will contain ownership
* of that displaced value. Otherwise it will be a nullptr.
* After insertion the value will be a nullptr.
* If a pair were replaced, its smart pointer is reset.
*
* \param key the key associated with the value to be stored
* \param value the value to store, and optionally the value displaced
*/
bool
push_and_replace(uint64_t key, ElemUniquePtr & value)
push_and_replace(uint64_t key, ConstElemSharedPtr value)
{
std::lock_guard<std::mutex> lock(data_mutex_);
bool did_replace = elements_[head_].in_use;
elements_[head_].key = key;
elements_[head_].value.swap(value);
elements_[head_].in_use = true;
Element & element = elements_[head_];
element.key = key;
element.unique_value.reset();
element.shared_value.reset();
element.shared_value = value;
element.in_use = true;
head_ = (head_ + 1) % elements_.size();
return did_replace;
}
/// Insert a key-value pair, displacing an existing pair if necessary.
/**
* See `bool push_and_replace(uint64_t key, const ConstElemSharedPtr & value)`.
*/
bool
push_and_replace(uint64_t key, ElemUniquePtr && value)
push_and_replace(uint64_t key, ElemUniquePtr value)
{
ElemUniquePtr temp = std::move(value);
return push_and_replace(key, temp);
std::lock_guard<std::mutex> lock(data_mutex_);
bool did_replace = elements_[head_].in_use;
Element & element = elements_[head_];
element.key = key;
element.unique_value.reset();
element.shared_value.reset();
element.unique_value = std::move(value);
element.in_use = true;
head_ = (head_ + 1) % elements_.size();
return did_replace;
}
/// Return true if the key is found in the ring buffer, otherwise false.
@@ -216,27 +286,28 @@ public:
private:
RCLCPP_DISABLE_COPY(MappedRingBuffer<T, Alloc>)
struct element
struct Element
{
uint64_t key;
ElemUniquePtr value;
ElemUniquePtr unique_value;
ConstElemSharedPtr shared_value;
bool in_use;
};
using VectorAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<element>;
using VectorAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<Element>;
typename std::vector<element, VectorAlloc>::iterator
typename std::vector<Element, VectorAlloc>::iterator
get_iterator_of_key(uint64_t key)
{
auto it = std::find_if(
elements_.begin(), elements_.end(),
[key](element & e) -> bool {
[key](Element & e) -> bool {
return e.key == key && e.in_use;
});
return it;
}
std::vector<element, VectorAlloc> elements_;
std::vector<Element, VectorAlloc> elements_;
size_t head_;
std::shared_ptr<ElemAlloc> allocator_;
std::mutex data_mutex_;

View File

@@ -15,8 +15,8 @@
#ifndef RCLCPP__MEMORY_STRATEGY_HPP_
#define RCLCPP__MEMORY_STRATEGY_HPP_
#include <list>
#include <memory>
#include <vector>
#include "rcl/allocator.h"
#include "rcl/wait.h"
@@ -42,15 +42,16 @@ class RCLCPP_PUBLIC MemoryStrategy
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(MemoryStrategy)
using WeakNodeVector = std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>;
using WeakNodeList = std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>;
virtual ~MemoryStrategy() = default;
virtual bool collect_entities(const WeakNodeVector & weak_nodes) = 0;
virtual bool collect_entities(const WeakNodeList & weak_nodes) = 0;
virtual size_t number_of_ready_subscriptions() const = 0;
virtual size_t number_of_ready_services() const = 0;
virtual size_t number_of_ready_clients() const = 0;
virtual size_t number_of_ready_events() const = 0;
virtual size_t number_of_ready_timers() const = 0;
virtual size_t number_of_guard_conditions() const = 0;
virtual size_t number_of_waitables() const = 0;
@@ -66,22 +67,22 @@ public:
virtual void
get_next_subscription(
rclcpp::executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes) = 0;
const WeakNodeList & weak_nodes) = 0;
virtual void
get_next_service(
rclcpp::executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes) = 0;
const WeakNodeList & weak_nodes) = 0;
virtual void
get_next_client(
rclcpp::executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes) = 0;
const WeakNodeList & weak_nodes) = 0;
virtual void
get_next_waitable(
rclcpp::executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes) = 0;
const WeakNodeList & weak_nodes) = 0;
virtual rcl_allocator_t
get_allocator() = 0;
@@ -89,42 +90,42 @@ public:
static rclcpp::SubscriptionBase::SharedPtr
get_subscription_by_handle(
std::shared_ptr<const rcl_subscription_t> subscriber_handle,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
static rclcpp::ServiceBase::SharedPtr
get_service_by_handle(
std::shared_ptr<const rcl_service_t> service_handle,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
static rclcpp::ClientBase::SharedPtr
get_client_by_handle(
std::shared_ptr<const rcl_client_t> client_handle,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
static rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_by_group(
rclcpp::callback_group::CallbackGroup::SharedPtr group,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
static rclcpp::callback_group::CallbackGroup::SharedPtr
get_group_by_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
static rclcpp::callback_group::CallbackGroup::SharedPtr
get_group_by_service(
rclcpp::ServiceBase::SharedPtr service,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
static rclcpp::callback_group::CallbackGroup::SharedPtr
get_group_by_client(
rclcpp::ClientBase::SharedPtr client,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
static rclcpp::callback_group::CallbackGroup::SharedPtr
get_group_by_waitable(
rclcpp::Waitable::SharedPtr waitable,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
};
} // namespace memory_strategy

View File

@@ -42,7 +42,6 @@
#include "rclcpp/logger.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/message_memory_strategy.hpp"
#include "rclcpp/node_options.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_clock_interface.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
@@ -53,9 +52,11 @@
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/node_interfaces/node_waitables_interface.hpp"
#include "rclcpp/node_options.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/publisher_options.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/subscription_options.hpp"
@@ -144,8 +145,27 @@ public:
/// Create and return a Publisher.
/**
* The rclcpp::QoS has several convenient constructors, including a
* conversion constructor for size_t, which mimics older API's that
* allows just a string and size_t to create a publisher.
*
* For example, all of these cases will work:
*
* pub = node->create_publisher<MsgT>("chatter", 10); // implicitly KeepLast
* pub = node->create_publisher<MsgT>("chatter", QoS(10)); // implicitly KeepLast
* pub = node->create_publisher<MsgT>("chatter", QoS(KeepLast(10)));
* pub = node->create_publisher<MsgT>("chatter", QoS(KeepAll()));
* pub = node->create_publisher<MsgT>("chatter", QoS(1).best_effort().durability_volatile());
* {
* rclcpp::QoS custom_qos(KeepLast(10), rmw_qos_profile_sensor_data);
* pub = node->create_publisher<MsgT>("chatter", custom_qos);
* }
*
* The publisher options may optionally be passed as the third argument for
* any of the above cases.
*
* \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 The Quality of Service settings for the publisher.
* \param[in] options Additional options for the created Publisher.
* \return Shared pointer to the created publisher.
*/
@@ -156,30 +176,29 @@ public:
std::shared_ptr<PublisherT>
create_publisher(
const std::string & topic_name,
size_t qos_history_depth,
const PublisherOptionsWithAllocator<AllocatorT> &
options = PublisherOptionsWithAllocator<AllocatorT>());
const rclcpp::QoS & qos,
const PublisherOptionsWithAllocator<AllocatorT> & options =
PublisherOptionsWithAllocator<AllocatorT>()
);
/// Create and return a Publisher.
/**
* \param[in] topic_name The topic for this publisher to publish on.
* \param[in] qos_history_depth The depth of the publisher message queue.
* \param[in] allocator Optional custom allocator.
* \param[in] allocator Custom allocator.
* \return Shared pointer to the created publisher.
*/
template<
typename MessageT, typename Alloc = std::allocator<void>,
typename PublisherT = ::rclcpp::Publisher<MessageT, Alloc>>
typename MessageT,
typename AllocatorT = std::allocator<void>,
typename PublisherT = ::rclcpp::Publisher<MessageT, AllocatorT>>
// cppcheck-suppress syntaxError // bug in cppcheck 1.82 for [[deprecated]] on templated function
[[deprecated(
"use the create_publisher(const std::string &, size_t, const PublisherOptions<Alloc> & = "
"PublisherOptions<Alloc>()) signature instead")]]
[[deprecated("use create_publisher(const std::string &, const rclcpp::QoS &, ...) instead")]]
std::shared_ptr<PublisherT>
create_publisher(
const std::string & topic_name,
size_t qos_history_depth,
std::shared_ptr<Alloc> allocator,
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault);
std::shared_ptr<AllocatorT> allocator);
/// Create and return a Publisher.
/**
@@ -189,14 +208,16 @@ public:
* \return Shared pointer to the created publisher.
*/
template<
typename MessageT, typename Alloc = std::allocator<void>,
typename PublisherT = ::rclcpp::Publisher<MessageT, Alloc>>
typename MessageT,
typename AllocatorT = std::allocator<void>,
typename PublisherT = ::rclcpp::Publisher<MessageT, AllocatorT>>
// cppcheck-suppress syntaxError // bug in cppcheck 1.82 for [[deprecated]] on templated function
[[deprecated("use create_publisher(const std::string &, const rclcpp::QoS &, ...) instead")]]
std::shared_ptr<PublisherT>
create_publisher(
const std::string & topic_name,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_default,
std::shared_ptr<Alloc> allocator = nullptr,
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault);
std::shared_ptr<AllocatorT> allocator = nullptr);
/// Create and return a Subscription.
/**
@@ -220,10 +241,10 @@ public:
std::shared_ptr<SubscriptionT>
create_subscription(
const std::string & topic_name,
const rclcpp::QoS & qos,
CallbackT && callback,
size_t qos_history_depth,
const SubscriptionOptionsWithAllocator<AllocatorT> &
options = SubscriptionOptionsWithAllocator<AllocatorT>(),
const SubscriptionOptionsWithAllocator<AllocatorT> & options =
SubscriptionOptionsWithAllocator<AllocatorT>(),
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, AllocatorT
>::SharedPtr
@@ -250,6 +271,10 @@ public:
typename Alloc = std::allocator<void>,
typename SubscriptionT = rclcpp::Subscription<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>>
// cppcheck-suppress syntaxError // bug in cppcheck 1.82 for [[deprecated]] on templated function
[[deprecated(
"use create_subscription(const std::string &, const rclcpp::QoS &, CallbackT, ...) instead"
)]]
std::shared_ptr<SubscriptionT>
create_subscription(
const std::string & topic_name,
@@ -260,8 +285,7 @@ public:
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,
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault);
std::shared_ptr<Alloc> allocator = nullptr);
/// Create and return a Subscription.
/**
@@ -284,21 +308,21 @@ public:
typename Alloc = std::allocator<void>,
typename SubscriptionT = rclcpp::Subscription<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>>
// cppcheck-suppress syntaxError // bug in cppcheck 1.82 for [[deprecated]] on templated function
[[deprecated(
"use the create_subscription(const std::string &, CallbackT &&, size_t, "
"const SubscriptionOptions<Alloc> & = SubscriptionOptions<Alloc>(), ...) signature instead")]]
"use create_subscription(const std::string &, const rclcpp::QoS &, CallbackT, ...) instead"
)]]
std::shared_ptr<SubscriptionT>
create_subscription(
const std::string & topic_name,
CallbackT && callback,
size_t qos_history_depth,
rclcpp::callback_group::CallbackGroup::SharedPtr group,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr,
bool ignore_local_publications = false,
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,
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault);
std::shared_ptr<Alloc> allocator = nullptr);
/// Create a timer.
/**
@@ -313,7 +337,13 @@ public:
CallbackT callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
/* Create and return a Client. */
/// Create and return a Client.
/**
* \param[in] service_name The topic to service on.
* \param[in] qos_profile rmw_qos_profile_t Quality of service profile for client.
* \param[in] group Callback group to call the service.
* \return Shared pointer to the created client.
*/
template<typename ServiceT>
typename rclcpp::Client<ServiceT>::SharedPtr
create_client(
@@ -321,7 +351,14 @@ public:
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_services_default,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
/* Create and return a Service. */
/// Create and return a Service.
/**
* \param[in] service_name The topic to service on.
* \param[in] callback User-defined callback function.
* \param[in] qos_profile rmw_qos_profile_t Quality of service profile for client.
* \param[in] group Callback group to call the service.
* \return Shared pointer to the created service.
*/
template<typename ServiceT, typename CallbackT>
typename rclcpp::Service<ServiceT>::SharedPtr
create_service(
@@ -492,6 +529,7 @@ public:
* rclcpp::NodeOptions::allow_undeclared_parameters set to true.
* If undeclared parameters are allowed, then the parameter is implicitly
* declared with the default parameter meta data before being set.
* Parameter overrides are ignored by set_parameter.
*
* This method will result in any callback registered with
* set_on_parameters_set_callback to be called.
@@ -916,6 +954,11 @@ public:
void
register_param_change_callback(CallbackT && callback);
/// Get the fully-qualified names of all available nodes.
/**
* The fully-qualified name includes the local namespace and name of the node.
* \return A vector of fully-qualified names of nodes.
*/
RCLCPP_PUBLIC
std::vector<std::string>
get_node_names() const;
@@ -1112,14 +1155,27 @@ public:
* with a leading '/'.
*/
RCLCPP_PUBLIC
Node::SharedPtr
rclcpp::Node::SharedPtr
create_sub_node(const std::string & sub_namespace);
/// Return the NodeOptions used when creating this node.
RCLCPP_PUBLIC
const NodeOptions &
const rclcpp::NodeOptions &
get_node_options() const;
/// Manually assert that this Node is alive (for RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_NODE).
/**
* If the rmw Liveliness policy is set to RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_NODE, the creator
* of this node may manually call `assert_liveliness` at some point in time to signal to the rest
* of the system that this Node is still alive.
*
* \return `true` if the liveliness was asserted successfully, otherwise `false`
*/
RCLCPP_PUBLIC
RCUTILS_WARN_UNUSED
bool
assert_liveliness() const;
protected:
/// Construct a sub-node, which will extend the namespace of all entities created with it.
/**
@@ -1151,7 +1207,7 @@ private:
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr node_time_source_;
rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr node_waitables_;
const NodeOptions node_options_;
const rclcpp::NodeOptions node_options_;
const std::string sub_namespace_;
const std::string effective_namespace_;
};

View File

@@ -36,11 +36,12 @@
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rclcpp/contexts/default_context.hpp"
#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/intra_process_manager.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -67,66 +68,42 @@ template<typename MessageT, typename AllocatorT, typename PublisherT>
std::shared_ptr<PublisherT>
Node::create_publisher(
const std::string & topic_name,
size_t qos_history_depth,
const rclcpp::QoS & qos,
const PublisherOptionsWithAllocator<AllocatorT> & options)
{
std::shared_ptr<AllocatorT> allocator = options.allocator;
if (!allocator) {
allocator = std::make_shared<AllocatorT>();
}
rmw_qos_profile_t qos_profile = options.qos_profile;
qos_profile.depth = qos_history_depth;
bool use_intra_process;
switch (options.use_intra_process_comm) {
case IntraProcessSetting::Enable:
use_intra_process = true;
break;
case IntraProcessSetting::Disable:
use_intra_process = false;
break;
case IntraProcessSetting::NodeDefault:
use_intra_process = this->get_node_options().use_intra_process_comms();
break;
default:
throw std::runtime_error("Unrecognized IntraProcessSetting value");
break;
}
return rclcpp::create_publisher<MessageT, AllocatorT, PublisherT>(
this->node_topics_.get(),
*this,
extend_name_with_sub_namespace(topic_name, this->get_sub_namespace()),
qos_profile,
use_intra_process,
allocator);
qos,
options);
}
template<typename MessageT, typename Alloc, typename PublisherT>
template<typename MessageT, typename AllocatorT, typename PublisherT>
std::shared_ptr<PublisherT>
Node::create_publisher(
const std::string & topic_name, size_t qos_history_depth,
std::shared_ptr<Alloc> allocator,
IntraProcessSetting use_intra_process_comm)
const std::string & topic_name,
size_t qos_history_depth,
std::shared_ptr<AllocatorT> allocator)
{
PublisherOptionsWithAllocator<Alloc> pub_options;
PublisherOptionsWithAllocator<AllocatorT> pub_options;
pub_options.allocator = allocator;
pub_options.use_intra_process_comm = use_intra_process_comm;
return this->create_publisher<MessageT, Alloc, PublisherT>(
topic_name, qos_history_depth, pub_options);
return this->create_publisher<MessageT, AllocatorT, PublisherT>(
topic_name, rclcpp::QoS(rclcpp::KeepLast(qos_history_depth)), pub_options);
}
template<typename MessageT, typename Alloc, typename PublisherT>
template<typename MessageT, typename AllocatorT, typename PublisherT>
std::shared_ptr<PublisherT>
Node::create_publisher(
const std::string & topic_name, const rmw_qos_profile_t & qos_profile,
std::shared_ptr<Alloc> allocator, IntraProcessSetting use_intra_process_comm)
const std::string & topic_name,
const rmw_qos_profile_t & qos_profile,
std::shared_ptr<AllocatorT> allocator)
{
PublisherOptionsWithAllocator<Alloc> pub_options;
pub_options.qos_profile = qos_profile;
rclcpp::QoS qos(rclcpp::QoSInitialization::from_rmw(qos_profile));
qos.get_rmw_qos_profile() = qos_profile;
PublisherOptionsWithAllocator<AllocatorT> pub_options;
pub_options.allocator = allocator;
pub_options.use_intra_process_comm = use_intra_process_comm;
return this->create_publisher<MessageT, Alloc, PublisherT>(
topic_name, qos_profile.depth, pub_options);
return this->create_publisher<MessageT, AllocatorT, PublisherT>(topic_name, qos, pub_options);
}
template<
@@ -137,55 +114,20 @@ template<
std::shared_ptr<SubscriptionT>
Node::create_subscription(
const std::string & topic_name,
const rclcpp::QoS & qos,
CallbackT && callback,
size_t qos_history_depth,
const SubscriptionOptionsWithAllocator<AllocatorT> & options,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, AllocatorT>::SharedPtr
msg_mem_strat)
{
using CallbackMessageT = typename rclcpp::subscription_traits::has_message_type<CallbackT>::type;
std::shared_ptr<AllocatorT> allocator = options.allocator;
if (!allocator) {
allocator = std::make_shared<AllocatorT>();
}
rmw_qos_profile_t qos_profile = options.qos_profile;
qos_profile.depth = qos_history_depth;
if (!msg_mem_strat) {
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
msg_mem_strat = MessageMemoryStrategy<CallbackMessageT, AllocatorT>::create_default();
}
bool use_intra_process;
switch (options.use_intra_process_comm) {
case IntraProcessSetting::Enable:
use_intra_process = true;
break;
case IntraProcessSetting::Disable:
use_intra_process = false;
break;
case IntraProcessSetting::NodeDefault:
use_intra_process = this->get_node_options().use_intra_process_comms();
break;
default:
throw std::runtime_error("Unrecognized IntraProcessSetting value");
break;
}
return rclcpp::create_subscription<
MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT>(
this->node_topics_.get(),
return rclcpp::create_subscription<MessageT>(
*this,
extend_name_with_sub_namespace(topic_name, this->get_sub_namespace()),
qos,
std::forward<CallbackT>(callback),
qos_profile,
options.callback_group,
options.ignore_local_publications,
use_intra_process,
msg_mem_strat,
allocator);
options,
msg_mem_strat);
}
template<
@@ -203,18 +145,18 @@ Node::create_subscription(
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,
IntraProcessSetting use_intra_process_comm)
std::shared_ptr<Alloc> allocator)
{
rclcpp::QoS qos(rclcpp::QoSInitialization::from_rmw(qos_profile));
qos.get_rmw_qos_profile() = qos_profile;
SubscriptionOptionsWithAllocator<Alloc> sub_options;
sub_options.qos_profile = qos_profile;
sub_options.callback_group = group;
sub_options.ignore_local_publications = ignore_local_publications;
sub_options.allocator = allocator;
sub_options.use_intra_process_comm = use_intra_process_comm;
return this->create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
topic_name, std::forward<CallbackT>(callback), qos_profile.depth, sub_options, msg_mem_strat);
topic_name, qos, std::forward<CallbackT>(callback), sub_options, msg_mem_strat);
}
template<
@@ -232,17 +174,19 @@ Node::create_subscription(
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,
IntraProcessSetting use_intra_process_comm)
std::shared_ptr<Alloc> allocator)
{
SubscriptionOptionsWithAllocator<Alloc> sub_options;
sub_options.callback_group = group;
sub_options.ignore_local_publications = ignore_local_publications;
sub_options.allocator = allocator;
sub_options.use_intra_process_comm = use_intra_process_comm;
return this->create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
topic_name, std::forward<CallbackT>(callback), qos_history_depth, sub_options, msg_mem_strat);
topic_name,
rclcpp::QoS(rclcpp::KeepLast(qos_history_depth)),
std::forward<CallbackT>(callback),
sub_options,
msg_mem_strat);
}
template<typename DurationRepT, typename DurationT, typename CallbackT>

View File

@@ -0,0 +1,149 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__NODE_INTERFACES__GET_NODE_BASE_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__GET_NODE_BASE_INTERFACE_HPP_
#include <memory>
#include <utility>
#include <type_traits>
#include "rclcpp/node_interfaces/node_base_interface.hpp"
/// This header provides the get_node_base_interface() template function.
/**
* This function is useful for getting the NodeBaseInterface pointer from
* various kinds of Node-like classes.
*
* It's able to get the NodeBaseInterface pointer so long as the class
* has a method called ``get_node_base_interface()`` which returns
* either a pointer (const or not) to a NodeBaseInterface or a
* std::shared_ptr to a NodeBaseInterface.
*/
namespace rclcpp
{
namespace node_interfaces
{
namespace detail
{
// This is a meta-programming checker for if a given Node-like object has a
// getter called get_node_base_interface() which returns various types,
// e.g. const pointer or a shared pointer.
template<typename NodeType, typename ReturnType>
struct has_get_node_base_interface
{
private:
template<typename T>
static constexpr
auto
check(T *)->typename std::is_same<
decltype(std::declval<T>().get_node_base_interface()),
ReturnType
>::type;
template<typename>
static constexpr
std::false_type
check(...);
public:
using type = decltype(check<NodeType>(nullptr));
static constexpr bool value = type::value;
};
// If NodeType is a pointer to NodeBaseInterface already (just normal function overload).
inline
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface_from_pointer(rclcpp::node_interfaces::NodeBaseInterface * pointer)
{
return pointer;
}
// If NodeType has a method called get_node_base_interface() which returns a shared pointer.
template<
typename NodeType,
typename std::enable_if<has_get_node_base_interface<
typename std::remove_pointer<NodeType>::type,
std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface>
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface_from_pointer(NodeType node_pointer)
{
return node_pointer->get_node_base_interface().get();
}
// If NodeType has a method called get_node_base_interface() which returns a pointer.
template<
typename NodeType,
typename std::enable_if<has_get_node_base_interface<
typename std::remove_pointer<NodeType>::type,
rclcpp::node_interfaces::NodeBaseInterface *
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface_from_pointer(NodeType node_pointer)
{
return node_pointer->get_node_base_interface();
}
// Forward shared_ptr's to const node pointer signatures.
template<
typename NodeType,
typename std::enable_if<std::is_same<
NodeType,
typename std::shared_ptr<typename std::remove_pointer<NodeType>::type::element_type> *
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface_from_pointer(NodeType node_shared_pointer)
{
return get_node_base_interface_from_pointer(node_shared_pointer->get());
}
} // namespace detail
/// Get the NodeBaseInterface as a pointer from a pointer to a "Node like" object.
template<
typename NodeType,
typename std::enable_if<std::is_pointer<NodeType>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface(NodeType node_pointer)
{
// Forward pointers to detail implmentation directly.
return detail::get_node_base_interface_from_pointer(node_pointer);
}
/// Get the NodeBaseInterface as a pointer from a "Node like" object.
template<
typename NodeType,
typename std::enable_if<
!std::is_pointer<typename std::remove_reference<NodeType>::type>::value, int
>::type = 0
>
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface(NodeType && node_reference)
{
// Forward references to detail implmentation as a pointer.
return detail::get_node_base_interface_from_pointer(&node_reference);
}
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__GET_NODE_BASE_INTERFACE_HPP_

View File

@@ -0,0 +1,149 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__NODE_INTERFACES__GET_NODE_TIMERS_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__GET_NODE_TIMERS_INTERFACE_HPP_
#include <memory>
#include <utility>
#include <type_traits>
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
/// This header provides the get_node_timers_interface() template function.
/**
* This function is useful for getting the NodeTimersInterface pointer from
* various kinds of Node-like classes.
*
* It's able to get the NodeTimersInterface pointer so long as the class
* has a method called ``get_node_timers_interface()`` which returns
* either a pointer (const or not) to a NodeTimersInterface or a
* std::shared_ptr to a NodeTimersInterface.
*/
namespace rclcpp
{
namespace node_interfaces
{
namespace detail
{
// This is a meta-programming checker for if a given Node-like object has a
// getter called get_node_timers_interface() which returns various types,
// e.g. const pointer or a shared pointer.
template<typename NodeType, typename ReturnType>
struct has_get_node_timers_interface
{
private:
template<typename T>
static constexpr
auto
check(T *)->typename std::is_same<
decltype(std::declval<T>().get_node_timers_interface()),
ReturnType
>::type;
template<typename>
static constexpr
std::false_type
check(...);
public:
using type = decltype(check<NodeType>(nullptr));
static constexpr bool value = type::value;
};
// If NodeType is a pointer to NodeTimersInterface already (just normal function overload).
inline
rclcpp::node_interfaces::NodeTimersInterface *
get_node_timers_interface_from_pointer(rclcpp::node_interfaces::NodeTimersInterface * pointer)
{
return pointer;
}
// If NodeType has a method called get_node_timers_interface() which returns a shared pointer.
template<
typename NodeType,
typename std::enable_if<has_get_node_timers_interface<
typename std::remove_pointer<NodeType>::type,
std::shared_ptr<rclcpp::node_interfaces::NodeTimersInterface>
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTimersInterface *
get_node_timers_interface_from_pointer(NodeType node_pointer)
{
return node_pointer->get_node_timers_interface().get();
}
// If NodeType has a method called get_node_timers_interface() which returns a pointer.
template<
typename NodeType,
typename std::enable_if<has_get_node_timers_interface<
typename std::remove_pointer<NodeType>::type,
rclcpp::node_interfaces::NodeTimersInterface *
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTimersInterface *
get_node_timers_interface_from_pointer(NodeType node_pointer)
{
return node_pointer->get_node_timers_interface();
}
// Forward shared_ptr's to const node pointer signatures.
template<
typename NodeType,
typename std::enable_if<std::is_same<
NodeType,
typename std::shared_ptr<typename std::remove_pointer<NodeType>::type::element_type> *
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTimersInterface *
get_node_timers_interface_from_pointer(NodeType node_shared_pointer)
{
return get_node_timers_interface_from_pointer(node_shared_pointer->get());
}
} // namespace detail
/// Get the NodeTimersInterface as a pointer from a pointer to a "Node like" object.
template<
typename NodeType,
typename std::enable_if<std::is_pointer<NodeType>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTimersInterface *
get_node_timers_interface(NodeType node_pointer)
{
// Forward pointers to detail implmentation directly.
return detail::get_node_timers_interface_from_pointer(node_pointer);
}
/// Get the NodeTimersInterface as a pointer from a "Node like" object.
template<
typename NodeType,
typename std::enable_if<
!std::is_pointer<typename std::remove_reference<NodeType>::type>::value, int
>::type = 0
>
rclcpp::node_interfaces::NodeTimersInterface *
get_node_timers_interface(NodeType && node_reference)
{
// Forward references to detail implmentation as a pointer.
return detail::get_node_timers_interface_from_pointer(&node_reference);
}
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__GET_NODE_TIMERS_INTERFACE_HPP_

View File

@@ -0,0 +1,149 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__NODE_INTERFACES__GET_NODE_TOPICS_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__GET_NODE_TOPICS_INTERFACE_HPP_
#include <memory>
#include <utility>
#include <type_traits>
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
/// This header provides the get_node_topics_interface() template function.
/**
* This function is useful for getting the NodeTopicsInterface pointer from
* various kinds of Node-like classes.
*
* It's able to get the NodeTopicsInterface pointer so long as the class
* has a method called ``get_node_topics_interface()`` which returns
* either a pointer (const or not) to a NodeTopicsInterface or a
* std::shared_ptr to a NodeTopicsInterface.
*/
namespace rclcpp
{
namespace node_interfaces
{
namespace detail
{
// This is a meta-programming checker for if a given Node-like object has a
// getter called get_node_topics_interface() which returns various types,
// e.g. const pointer or a shared pointer.
template<typename NodeType, typename ReturnType>
struct has_get_node_topics_interface
{
private:
template<typename T>
static constexpr
auto
check(T *)->typename std::is_same<
decltype(std::declval<T>().get_node_topics_interface()),
ReturnType
>::type;
template<typename>
static constexpr
std::false_type
check(...);
public:
using type = decltype(check<NodeType>(nullptr));
static constexpr bool value = type::value;
};
// If NodeType is a pointer to NodeTopicsInterface already (just normal function overload).
inline
rclcpp::node_interfaces::NodeTopicsInterface *
get_node_topics_interface_from_pointer(rclcpp::node_interfaces::NodeTopicsInterface * pointer)
{
return pointer;
}
// If NodeType has a method called get_node_topics_interface() which returns a shared pointer.
template<
typename NodeType,
typename std::enable_if<has_get_node_topics_interface<
typename std::remove_pointer<NodeType>::type,
std::shared_ptr<rclcpp::node_interfaces::NodeTopicsInterface>
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTopicsInterface *
get_node_topics_interface_from_pointer(NodeType node_pointer)
{
return node_pointer->get_node_topics_interface().get();
}
// If NodeType has a method called get_node_topics_interface() which returns a pointer.
template<
typename NodeType,
typename std::enable_if<has_get_node_topics_interface<
typename std::remove_pointer<NodeType>::type,
rclcpp::node_interfaces::NodeTopicsInterface *
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTopicsInterface *
get_node_topics_interface_from_pointer(NodeType node_pointer)
{
return node_pointer->get_node_topics_interface();
}
// Forward shared_ptr's to const node pointer signatures.
template<
typename NodeType,
typename std::enable_if<std::is_same<
NodeType,
typename std::shared_ptr<typename std::remove_pointer<NodeType>::type::element_type> *
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTopicsInterface *
get_node_topics_interface_from_pointer(NodeType node_shared_pointer)
{
return get_node_topics_interface_from_pointer(node_shared_pointer->get());
}
} // namespace detail
/// Get the NodeTopicsInterface as a pointer from a pointer to a "Node like" object.
template<
typename NodeType,
typename std::enable_if<std::is_pointer<NodeType>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTopicsInterface *
get_node_topics_interface(NodeType node_pointer)
{
// Forward pointers to detail implmentation directly.
return detail::get_node_topics_interface_from_pointer(node_pointer);
}
/// Get the NodeTopicsInterface as a pointer from a "Node like" object.
template<
typename NodeType,
typename std::enable_if<
!std::is_pointer<typename std::remove_reference<NodeType>::type>::value, int
>::type = 0
>
rclcpp::node_interfaces::NodeTopicsInterface *
get_node_topics_interface(NodeType && node_reference)
{
// Forward references to detail implmentation as a pointer.
return detail::get_node_topics_interface_from_pointer(&node_reference);
}
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__GET_NODE_TOPICS_INTERFACE_HPP_

View File

@@ -19,10 +19,11 @@
#include <string>
#include <vector>
#include "rcl/node.h"
#include "rclcpp/context.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_options.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_options.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
@@ -34,13 +35,15 @@ namespace node_interfaces
class NodeBase : public NodeBaseInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBaseInterface)
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBase)
RCLCPP_PUBLIC
NodeBase(
const std::string & node_name,
const std::string & namespace_,
const NodeOptions & options);
rclcpp::Context::SharedPtr context,
const rcl_node_options_t & rcl_node_options,
bool use_intra_process_default);
RCLCPP_PUBLIC
virtual
@@ -86,6 +89,11 @@ public:
std::shared_ptr<const rcl_node_t>
get_shared_rcl_node_handle() const;
RCLCPP_PUBLIC
virtual
bool
assert_liveliness() const;
RCLCPP_PUBLIC
virtual
rclcpp::callback_group::CallbackGroup::SharedPtr
@@ -121,10 +129,16 @@ public:
std::unique_lock<std::recursive_mutex>
acquire_notify_guard_condition_lock() const;
RCLCPP_PUBLIC
virtual
bool
get_use_intra_process_default() const;
private:
RCLCPP_DISABLE_COPY(NodeBase)
rclcpp::Context::SharedPtr context_;
bool use_intra_process_default_;
std::shared_ptr<rcl_node_t> node_handle_;

View File

@@ -102,6 +102,12 @@ public:
std::shared_ptr<const rcl_node_t>
get_shared_rcl_node_handle() const = 0;
/// Manually assert that this Node is alive (for RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_NODE).
RCLCPP_PUBLIC
virtual
bool
assert_liveliness() const = 0;
/// Create and return a callback group.
RCLCPP_PUBLIC
virtual
@@ -149,6 +155,12 @@ public:
virtual
std::unique_lock<std::recursive_mutex>
acquire_notify_guard_condition_lock() const = 0;
/// Return the default preference for using intra process communication.
RCLCPP_PUBLIC
virtual
bool
get_use_intra_process_default() const = 0;
};
} // namespace node_interfaces

View File

@@ -63,13 +63,13 @@ public:
const node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
const node_interfaces::NodeServicesInterface::SharedPtr node_services,
const node_interfaces::NodeClockInterface::SharedPtr node_clock,
const std::vector<Parameter> & initial_parameters,
bool use_intra_process,
const std::vector<Parameter> & parameter_overrides,
bool start_parameter_services,
bool start_parameter_event_publisher,
const rmw_qos_profile_t & parameter_event_qos_profile,
const rclcpp::QoS & parameter_event_qos,
const rclcpp::PublisherOptionsBase & parameter_event_publisher_options,
bool allow_undeclared_parameters,
bool automatically_declare_initial_parameters);
bool automatically_declare_parameters_from_overrides);
RCLCPP_PUBLIC
virtual
@@ -143,7 +143,7 @@ public:
RCLCPP_PUBLIC
const std::map<std::string, rclcpp::ParameterValue> &
get_initial_parameter_values() const override;
get_parameter_overrides() const override;
private:
RCLCPP_DISABLE_COPY(NodeParameters)
@@ -154,7 +154,7 @@ private:
std::map<std::string, ParameterInfo> parameters_;
std::map<std::string, rclcpp::ParameterValue> initial_parameter_values_;
std::map<std::string, rclcpp::ParameterValue> parameter_overrides_;
bool allow_undeclared_ = false;

View File

@@ -51,8 +51,9 @@ public:
const rclcpp::ParameterValue &
declare_parameter(
const std::string & name,
const rclcpp::ParameterValue & default_value,
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor) = 0;
const rclcpp::ParameterValue & default_value = rclcpp::ParameterValue(),
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
rcl_interfaces::msg::ParameterDescriptor()) = 0;
/// Undeclare a parameter.
/**
@@ -183,7 +184,7 @@ public:
RCLCPP_PUBLIC
virtual
const std::map<std::string, rclcpp::ParameterValue> &
get_initial_parameter_values() const = 0;
get_parameter_overrides() const = 0;
};
} // namespace node_interfaces

View File

@@ -42,44 +42,44 @@ public:
explicit NodeTopics(rclcpp::node_interfaces::NodeBaseInterface * node_base);
RCLCPP_PUBLIC
virtual
~NodeTopics();
~NodeTopics() override;
RCLCPP_PUBLIC
virtual
rclcpp::PublisherBase::SharedPtr
create_publisher(
const std::string & topic_name,
const rclcpp::PublisherFactory & publisher_factory,
rcl_publisher_options_t & publisher_options,
bool use_intra_process);
const rcl_publisher_options_t & publisher_options,
bool use_intra_process) override;
RCLCPP_PUBLIC
virtual
void
add_publisher(
rclcpp::PublisherBase::SharedPtr publisher);
rclcpp::PublisherBase::SharedPtr publisher,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) override;
RCLCPP_PUBLIC
virtual
rclcpp::SubscriptionBase::SharedPtr
create_subscription(
const std::string & topic_name,
const rclcpp::SubscriptionFactory & subscription_factory,
rcl_subscription_options_t & subscription_options,
bool use_intra_process);
const rcl_subscription_options_t & subscription_options,
bool use_intra_process) override;
RCLCPP_PUBLIC
virtual
void
add_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group);
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) override;
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface() const override;
private:
RCLCPP_DISABLE_COPY(NodeTopics)
NodeBaseInterface * node_base_;
rclcpp::node_interfaces::NodeBaseInterface * node_base_;
};
} // namespace node_interfaces

View File

@@ -50,14 +50,15 @@ public:
create_publisher(
const std::string & topic_name,
const rclcpp::PublisherFactory & publisher_factory,
rcl_publisher_options_t & publisher_options,
const rcl_publisher_options_t & publisher_options,
bool use_intra_process) = 0;
RCLCPP_PUBLIC
virtual
void
add_publisher(
rclcpp::PublisherBase::SharedPtr publisher) = 0;
rclcpp::PublisherBase::SharedPtr publisher,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) = 0;
RCLCPP_PUBLIC
virtual
@@ -65,7 +66,7 @@ public:
create_subscription(
const std::string & topic_name,
const rclcpp::SubscriptionFactory & subscription_factory,
rcl_subscription_options_t & subscription_options,
const rcl_subscription_options_t & subscription_options,
bool use_intra_process) = 0;
RCLCPP_PUBLIC
@@ -74,6 +75,11 @@ public:
add_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) = 0;
RCLCPP_PUBLIC
virtual
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface() const = 0;
};
} // namespace node_interfaces

View File

@@ -23,6 +23,8 @@
#include "rclcpp/context.hpp"
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/publisher_options.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
@@ -38,14 +40,16 @@ public:
*
* - context = rclcpp::contexts::default_context::get_global_default_context()
* - arguments = {}
* - initial_parameters = {}
* - parameter_overrides = {}
* - use_global_arguments = true
* - use_intra_process_comms = false
* - start_parameter_services = true
* - start_parameter_event_publisher = true
* - parameter_event_qos_profile = rmw_qos_profile_parameter_events
* - parameter_event_qos = rclcpp::ParameterEventQoS
* - with history setting and depth from rmw_qos_profile_parameter_events
* - parameter_event_publisher_options = rclcpp::PublisherOptionsBase
* - allow_undeclared_parameters = false
* - automatically_declare_initial_parameters = false
* - automatically_declare_parameters_from_overrides = false
* - allocator = rcl_get_default_allocator()
*
* \param[in] allocator allocator to use in construction of NodeOptions.
@@ -103,31 +107,31 @@ public:
NodeOptions &
arguments(const std::vector<std::string> & arguments);
/// Return a reference to the list of initial parameters.
/// Return a reference to the list of parameter overrides.
RCLCPP_PUBLIC
std::vector<rclcpp::Parameter> &
initial_parameters();
parameter_overrides();
RCLCPP_PUBLIC
const std::vector<rclcpp::Parameter> &
initial_parameters() const;
parameter_overrides() const;
/// Set the initial parameters, return this for parameter idiom.
/// Set the parameters overrides, return this for parameter idiom.
/**
* These initial parameter values are used to change the initial value
* These parameter overrides are used to change the initial value
* of declared parameters within the node, overriding hard coded default
* values if necessary.
*/
RCLCPP_PUBLIC
NodeOptions &
initial_parameters(const std::vector<rclcpp::Parameter> & initial_parameters);
parameter_overrides(const std::vector<rclcpp::Parameter> & parameter_overrides);
/// Append a single initial parameter, parameter idiom style.
/// Append a single parameter override, parameter idiom style.
template<typename ParameterT>
NodeOptions &
append_initial_parameter(const std::string & name, const ParameterT & value)
append_parameter_override(const std::string & name, const ParameterT & value)
{
this->initial_parameters().emplace_back(name, rclcpp::ParameterValue(value));
this->parameter_overrides().emplace_back(name, rclcpp::ParameterValue(value));
return *this;
}
@@ -202,18 +206,36 @@ public:
NodeOptions &
start_parameter_event_publisher(bool start_parameter_event_publisher);
/// Return a reference to the parameter_event_qos_profile QoS.
/// Return a reference to the parameter_event_qos QoS.
RCLCPP_PUBLIC
const rmw_qos_profile_t &
parameter_event_qos_profile() const;
const rclcpp::QoS &
parameter_event_qos() const;
/// Set the parameter_event_qos_profile QoS, return this for parameter idiom.
/// Set the parameter_event_qos QoS, return this for parameter idiom.
/**
* The QoS settings to be used for the parameter event publisher, if enabled.
*/
RCLCPP_PUBLIC
NodeOptions &
parameter_event_qos_profile(const rmw_qos_profile_t & parameter_event_qos_profile);
parameter_event_qos(const rclcpp::QoS & parameter_event_qos);
/// Return a reference to the parameter_event_publisher_options.
RCLCPP_PUBLIC
const rclcpp::PublisherOptionsBase &
parameter_event_publisher_options() const;
/// Set the parameter_event_publisher_options, return this for parameter idiom.
/**
* The QoS settings to be used for the parameter event publisher, if enabled.
*
* \todo(wjwwood): make this take/store an instance of
* rclcpp::PublisherOptionsWithAllocator<Allocator>, but to do that requires
* NodeOptions to also be templated based on the Allocator type.
*/
RCLCPP_PUBLIC
NodeOptions &
parameter_event_publisher_options(
const rclcpp::PublisherOptionsBase & parameter_event_publisher_options);
/// Return the allow_undeclared_parameters flag.
RCLCPP_PUBLIC
@@ -225,29 +247,35 @@ public:
* If true, allow any parameter name to be set on the node without first
* being declared.
* Otherwise, setting an undeclared parameter will raise an exception.
*
* This option being true does not affect parameter_overrides, as the first
* set action will implicitly declare the parameter and therefore consider
* any parameter overrides.
*/
RCLCPP_PUBLIC
NodeOptions &
allow_undeclared_parameters(bool allow_undeclared_parameters);
/// Return the automatically_declare_initial_parameters flag.
/// Return the automatically_declare_parameters_from_overrides flag.
RCLCPP_PUBLIC
bool
automatically_declare_initial_parameters() const;
automatically_declare_parameters_from_overrides() const;
/// Set the automatically_declare_initial_parameters, return this.
/// Set the automatically_declare_parameters_from_overrides, return this.
/**
* If true, automatically iterate through the node's initial parameters and
* If true, automatically iterate through the node's parameter overrides and
* implicitly declare any that have not already been declared.
* Otherwise, parameters passed to the node's initial_parameters, and/or the
* global initial parameter values, which are not explicitly declared will
* not appear on the node at all.
* Otherwise, parameters passed to the node's parameter_overrides, and/or the
* global arguments (e.g. parameter overrides from a YAML file), which are
* not explicitly declared will not appear on the node at all, even if
* `allow_undeclared_parameters` is true.
* Already declared parameters will not be re-declared, and parameters
* declared in this way will use the default constructed ParameterDescriptor.
*/
RCLCPP_PUBLIC
NodeOptions &
automatically_declare_initial_parameters(bool automatically_declare_initial_parameters);
automatically_declare_parameters_from_overrides(
bool automatically_declare_parameters_from_overrides);
/// Return the rcl_allocator_t to be used.
RCLCPP_PUBLIC
@@ -280,7 +308,7 @@ private:
std::vector<std::string> arguments_ {};
std::vector<rclcpp::Parameter> initial_parameters_ {};
std::vector<rclcpp::Parameter> parameter_overrides_ {};
bool use_global_arguments_ {true};
@@ -290,11 +318,15 @@ private:
bool start_parameter_event_publisher_ {true};
rmw_qos_profile_t parameter_event_qos_profile_ {rmw_qos_profile_parameter_events};
rclcpp::QoS parameter_event_qos_ = rclcpp::ParameterEventsQoS(
rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events)
);
rclcpp::PublisherOptionsBase parameter_event_publisher_options_ = rclcpp::PublisherOptionsBase();
bool allow_undeclared_parameters_ {false};
bool automatically_declare_initial_parameters_ {false};
bool automatically_declare_parameters_from_overrides_ {false};
rcl_allocator_t allocator_ {rcl_get_default_allocator()};
};

View File

@@ -46,6 +46,15 @@ class AsyncParametersClient
public:
RCLCPP_SMART_PTR_DEFINITIONS(AsyncParametersClient)
/// Create an async parameters client.
/**
* \param[in] node_base_interface The node base interface of the corresponding node.
* \param[in] node_topics_interface Node topic base interface.
* \param[in] node_graph_interface The node graph interface of the corresponding node.
* \param[in] node_services_interface Node service interface.
* \param[in] remote_node_name (optional) name of the remote node
* \param[in] qos_profile (optional) The rmw qos profile to use to subscribe
*/
RCLCPP_PUBLIC
AsyncParametersClient(
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
@@ -55,12 +64,24 @@ public:
const std::string & remote_node_name = "",
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
/// Constructor
/**
* \param[in] node The async paramters client will be added to this node.
* \param[in] remote_node_name (optional) name of the remote node
* \param[in] qos_profile (optional) The rmw qos profile to use to subscribe
*/
RCLCPP_PUBLIC
AsyncParametersClient(
const rclcpp::Node::SharedPtr node,
const std::string & remote_node_name = "",
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
/// Constructor
/**
* \param[in] node The async paramters client will be added to this node.
* \param[in] remote_node_name (optional) name of the remote node
* \param[in] qos_profile (optional) The rmw qos profile to use to subscribe
*/
RCLCPP_PUBLIC
AsyncParametersClient(
rclcpp::Node * node,
@@ -110,28 +131,50 @@ public:
template<
typename CallbackT,
typename Alloc = std::allocator<void>,
typename SubscriptionT =
rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent, Alloc>>
typename AllocatorT = std::allocator<void>>
typename rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
on_parameter_event(CallbackT && callback)
on_parameter_event(
CallbackT && callback,
const rclcpp::QoS & qos = (
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events))
),
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
))
{
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
auto msg_mem_strat =
MessageMemoryStrategy<rcl_interfaces::msg::ParameterEvent, Alloc>::create_default();
return this->on_parameter_event(
this->node_topics_interface_,
callback,
qos,
options);
}
using rcl_interfaces::msg::ParameterEvent;
return rclcpp::create_subscription<
ParameterEvent, CallbackT, Alloc, ParameterEvent, SubscriptionT>(
this->node_topics_interface_.get(),
/**
* The NodeT type only needs to have a method called get_node_topics_interface()
* which returns a shared_ptr to a NodeTopicsInterface, or be a
* NodeTopicsInterface pointer itself.
*/
template<
typename CallbackT,
typename NodeT,
typename AllocatorT = std::allocator<void>>
static typename rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
on_parameter_event(
NodeT && node,
CallbackT && callback,
const rclcpp::QoS & qos = (
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events))
),
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
))
{
return rclcpp::create_subscription<rcl_interfaces::msg::ParameterEvent>(
node,
"parameter_events",
qos,
std::forward<CallbackT>(callback),
rmw_qos_profile_default,
nullptr, // group,
false, // ignore_local_publications,
false, // use_intra_process_comms_,
msg_mem_strat,
std::make_shared<Alloc>());
options);
}
RCLCPP_PUBLIC
@@ -154,7 +197,7 @@ protected:
wait_for_service_nanoseconds(std::chrono::nanoseconds timeout);
private:
const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface_;
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface_;
rclcpp::Client<rcl_interfaces::srv::GetParameters>::SharedPtr get_parameters_client_;
rclcpp::Client<rcl_interfaces::srv::GetParameterTypes>::SharedPtr
get_parameter_types_client_;
@@ -271,7 +314,26 @@ public:
typename rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
on_parameter_event(CallbackT && callback)
{
return async_parameters_client_->on_parameter_event(std::forward<CallbackT>(callback));
return async_parameters_client_->on_parameter_event(
std::forward<CallbackT>(callback));
}
/**
* The NodeT type only needs to have a method called get_node_topics_interface()
* which returns a shared_ptr to a NodeTopicsInterface, or be a
* NodeTopicsInterface pointer itself.
*/
template<
typename CallbackT,
typename NodeT>
static typename rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
on_parameter_event(
NodeT && node,
CallbackT && callback)
{
return AsyncParametersClient::on_parameter_event(
node,
std::forward<CallbackT>(callback));
}
RCLCPP_PUBLIC

View File

@@ -23,6 +23,7 @@
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include "rcl/error_handling.h"
#include "rcl/publisher.h"
@@ -31,167 +32,15 @@
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/publisher_base.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
// Forward declaration is used for friend statement.
namespace node_interfaces
{
class NodeTopicsInterface;
}
namespace intra_process_manager
{
/**
* NOTE(ivanpauno): IntraProcessManager is forward declared here, avoiding a circular inclusion between intra_process_manager.hpp and publisher.hpp.
* SharedPtr and WeakPtr of the IntraProcessManager are defined again here, to avoid a warning for accessing a member of a forward declared class.
*/
class IntraProcessManager;
}
class PublisherBase
{
friend ::rclcpp::node_interfaces::NodeTopicsInterface;
public:
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
/// Default constructor.
/**
* Typically, a publisher is not created through this method, but instead is created through a
* call to `Node::create_publisher`.
* \param[in] node_base A pointer to the NodeBaseInterface for the parent node.
* \param[in] topic The topic that this publisher publishes on.
* \param[in] type_support The type support structure for the type to be published.
* \param[in] publisher_options QoS settings for this publisher.
*/
RCLCPP_PUBLIC
PublisherBase(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rosidl_message_type_support_t & type_support,
const rcl_publisher_options_t & publisher_options);
RCLCPP_PUBLIC
virtual ~PublisherBase();
/// Get the topic that this publisher publishes on.
/** \return The topic name. */
RCLCPP_PUBLIC
const char *
get_topic_name() const;
/// Get the queue size for this publisher.
/** \return The queue size. */
RCLCPP_PUBLIC
size_t
get_queue_size() const;
/// Get the global identifier for this publisher (used in rmw and by DDS).
/** \return The gid. */
RCLCPP_PUBLIC
const rmw_gid_t &
get_gid() const;
/// Get the global identifier for this publisher used by intra-process communication.
/** \return The intra-process gid. */
RCLCPP_PUBLIC
const rmw_gid_t &
get_intra_process_gid() const;
/// Get the rcl publisher handle.
/** \return The rcl publisher handle. */
RCLCPP_PUBLIC
rcl_publisher_t *
get_publisher_handle();
/// Get the rcl publisher handle.
/** \return The rcl publisher handle. */
RCLCPP_PUBLIC
const rcl_publisher_t *
get_publisher_handle() const;
/// Get subscription count
/** \return The number of subscriptions. */
RCLCPP_PUBLIC
size_t
get_subscription_count() const;
/// Get intraprocess subscription count
/** \return The number of intraprocess subscriptions. */
RCLCPP_PUBLIC
size_t
get_intra_process_subscription_count() const;
/// Get the actual QoS settings, after the defaults have been determined.
/**
* The actual configuration applied when using RMW_QOS_POLICY_*_SYSTEM_DEFAULT
* can only be resolved after the creation of the publisher, and it
* depends on the underlying rmw implementation.
* If the underlying setting in use can't be represented in ROS terms,
* it will be set to RMW_QOS_POLICY_*_UNKNOWN.
* May throw runtime_error when an unexpected error occurs.
* \return The actual qos settings.
*/
RCLCPP_PUBLIC
rmw_qos_profile_t
get_actual_qos() const;
/// Compare this publisher to a gid.
/**
* Note that this function calls the next function.
* \param[in] gid Reference to a gid.
* \return True if the publisher's gid matches the input.
*/
RCLCPP_PUBLIC
bool
operator==(const rmw_gid_t & gid) const;
/// Compare this publisher to a pointer gid.
/**
* A wrapper for comparing this publisher's gid to the input using rmw_compare_gids_equal.
* \param[in] gid A pointer to a gid.
* \return True if this publisher's gid matches the input.
*/
RCLCPP_PUBLIC
bool
operator==(const rmw_gid_t * gid) const;
using StoreMessageCallbackT = std::function<uint64_t(uint64_t, void *, const std::type_info &)>;
using IntraProcessManagerSharedPtr =
std::shared_ptr<rclcpp::intra_process_manager::IntraProcessManager>;
/// Implementation utility function used to setup intra process publishing after creation.
RCLCPP_PUBLIC
void
setup_intra_process(
uint64_t intra_process_publisher_id,
StoreMessageCallbackT store_callback,
IntraProcessManagerSharedPtr ipm,
const rcl_publisher_options_t & intra_process_options);
protected:
std::shared_ptr<rcl_node_t> rcl_node_handle_;
rcl_publisher_t publisher_handle_ = rcl_get_zero_initialized_publisher();
rcl_publisher_t intra_process_publisher_handle_ = rcl_get_zero_initialized_publisher();
using IntraProcessManagerWeakPtr =
std::weak_ptr<rclcpp::intra_process_manager::IntraProcessManager>;
bool intra_process_is_enabled_;
IntraProcessManagerWeakPtr weak_ipm_;
uint64_t intra_process_publisher_id_;
StoreMessageCallbackT store_intra_process_message_;
rmw_gid_t rmw_gid_;
rmw_gid_t intra_process_rmw_gid_;
};
/// A publisher publishes messages of any type to a topic.
template<typename MessageT, typename Alloc = std::allocator<void>>
class Publisher : public PublisherBase
@@ -201,6 +50,7 @@ public:
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
using MessageSharedPtr = std::shared_ptr<const MessageT>;
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<MessageT, Alloc>)
@@ -208,6 +58,7 @@ public:
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rcl_publisher_options_t & publisher_options,
const PublisherEventCallbacks & event_callbacks,
const std::shared_ptr<MessageAlloc> & allocator)
: PublisherBase(
node_base,
@@ -217,112 +68,99 @@ public:
message_allocator_(allocator)
{
allocator::set_allocator_for_deleter(&message_deleter_, message_allocator_.get());
if (event_callbacks.deadline_callback) {
this->add_event_handler(event_callbacks.deadline_callback,
RCL_PUBLISHER_OFFERED_DEADLINE_MISSED);
}
if (event_callbacks.liveliness_callback) {
this->add_event_handler(event_callbacks.liveliness_callback,
RCL_PUBLISHER_LIVELINESS_LOST);
}
}
virtual ~Publisher()
{}
mapped_ring_buffer::MappedRingBufferBase::SharedPtr
make_mapped_ring_buffer(size_t size) const override
{
return mapped_ring_buffer::MappedRingBuffer<
MessageT,
typename Publisher<MessageT, Alloc>::MessageAlloc
>::make_shared(size, this->get_allocator());
}
/// Send a message to the topic for this publisher.
/**
* This function is templated on the input message type, MessageT.
* \param[in] msg A shared pointer to the message to send.
*/
virtual void
publish(std::unique_ptr<MessageT, MessageDeleter> & msg)
publish(std::unique_ptr<MessageT, MessageDeleter> msg)
{
bool inter_process_subscriptions_exist =
get_subscription_count() > get_intra_process_subscription_count();
if (!intra_process_is_enabled_ || inter_process_subscriptions_exist) {
if (!intra_process_is_enabled_) {
this->do_inter_process_publish(msg.get());
return;
}
if (store_intra_process_message_) {
// Take the pointer from the unique_msg, release it and pass as a void *
// to the ipm. The ipm should then capture it again as a unique_ptr of
// the correct type.
// TODO(wjwwood):
// investigate how to transfer the custom deleter (if there is one)
// from the incoming unique_ptr through to the ipm's unique_ptr.
// See: http://stackoverflow.com/questions/11002641/dynamic-casting-for-unique-ptr
MessageT * msg_ptr = msg.get();
msg.release();
uint64_t message_seq =
store_intra_process_message_(intra_process_publisher_id_, msg_ptr, typeid(MessageT));
rcl_interfaces::msg::IntraProcessMessage ipm;
ipm.publisher_id = intra_process_publisher_id_;
ipm.message_sequence = message_seq;
auto status = rcl_publish(&intra_process_publisher_handle_, &ipm);
if (RCL_RET_PUBLISHER_INVALID == status) {
rcl_reset_error(); // next call will reset error message if not context
if (rcl_publisher_is_valid_except_context(&intra_process_publisher_handle_)) {
rcl_context_t * context = rcl_publisher_get_context(&intra_process_publisher_handle_);
if (nullptr != context && !rcl_context_is_valid(context)) {
// publisher is invalid due to context being shutdown
return;
}
}
}
if (RCL_RET_OK != status) {
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish intra process message");
}
// If an interprocess subscription exist, then the unique_ptr is promoted
// to a shared_ptr and published.
// This allows doing the intraprocess publish first and then doing the
// interprocess publish, resulting in lower publish-to-subscribe latency.
// It's not possible to do that with an unique_ptr,
// as do_intra_process_publish takes the ownership of the message.
uint64_t message_seq;
bool inter_process_publish_needed =
get_subscription_count() > get_intra_process_subscription_count();
MessageSharedPtr shared_msg;
if (inter_process_publish_needed) {
shared_msg = std::move(msg);
message_seq =
store_intra_process_message(intra_process_publisher_id_, shared_msg);
} else {
// Always destroy the message, even if we don't consume it, for consistency.
msg.reset();
message_seq =
store_intra_process_message(intra_process_publisher_id_, std::move(msg));
}
this->do_intra_process_publish(message_seq);
if (inter_process_publish_needed) {
this->do_inter_process_publish(shared_msg.get());
}
}
// Skip deprecated attribute in windows, as it raise a warning in template specialization.
#if !defined(_WIN32)
[[deprecated(
"publishing an unique_ptr is prefered when using intra process communication."
" If using a shared_ptr, use publish(*msg).")]]
#endif
virtual void
publish(const std::shared_ptr<MessageT> & msg)
publish(const std::shared_ptr<const MessageT> & msg)
{
// Avoid allocating when not using intra process.
if (!store_intra_process_message_) {
// In this case we're not using intra process.
return this->do_inter_process_publish(msg.get());
}
// Otherwise we have to allocate memory in a unique_ptr and pass it along.
// TODO(wjwwood):
// The intra process manager should probably also be able to store
// shared_ptr's and do the "smart" thing based on other intra process
// subscriptions. For now call the other publish().
auto ptr = MessageAllocTraits::allocate(*message_allocator_.get(), 1);
MessageAllocTraits::construct(*message_allocator_.get(), ptr, *msg.get());
MessageUniquePtr unique_msg(ptr, message_deleter_);
return this->publish(unique_msg);
}
virtual void
publish(std::shared_ptr<const MessageT> msg)
{
// Avoid allocating when not using intra process.
if (!store_intra_process_message_) {
// In this case we're not using intra process.
return this->do_inter_process_publish(msg.get());
}
// Otherwise we have to allocate memory in a unique_ptr and pass it along.
// TODO(wjwwood):
// The intra process manager should probably also be able to store
// shared_ptr's and do the "smart" thing based on other intra process
// subscriptions. For now call the other publish().
auto ptr = MessageAllocTraits::allocate(*message_allocator_.get(), 1);
MessageAllocTraits::construct(*message_allocator_.get(), ptr, *msg.get());
MessageUniquePtr unique_msg(ptr, message_deleter_);
return this->publish(unique_msg);
publish(*msg);
}
virtual void
publish(const MessageT & msg)
{
// Avoid allocating when not using intra process.
if (!store_intra_process_message_) {
if (!intra_process_is_enabled_) {
// In this case we're not using intra process.
return this->do_inter_process_publish(&msg);
}
// Otherwise we have to allocate memory in a unique_ptr and pass it along.
// As the message is not const, a copy should be made.
// A shared_ptr<const MessageT> could also be constructed here.
auto ptr = MessageAllocTraits::allocate(*message_allocator_.get(), 1);
MessageAllocTraits::construct(*message_allocator_.get(), ptr, msg);
MessageUniquePtr unique_msg(ptr, message_deleter_);
return this->publish(unique_msg);
this->publish(std::move(unique_msg));
}
// Skip deprecated attribute in windows, as it raise a warning in template specialization.
#if !defined(_WIN32)
[[deprecated(
"Use publish(*msg). Check against nullptr before calling if necessary.")]]
#endif
virtual void
publish(const MessageT * msg)
{
@@ -333,22 +171,31 @@ public:
}
void
publish(const rcl_serialized_message_t * serialized_msg)
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");
}
return this->do_serialized_publish(&serialized_msg);
}
// Skip deprecated attribute in windows, as it raise a warning in template specialization.
#if !defined(_WIN32)
[[deprecated(
"Use publish(*serialized_msg). Check against nullptr before calling if necessary.")]]
#endif
void
publish(const rcl_serialized_message_t * serialized_msg)
{
return this->do_serialized_publish(serialized_msg);
}
// Skip deprecated attribute in windows, as it raise a warning in template specialization.
#if !defined(_WIN32)
[[deprecated(
"Use publish(*serialized_msg). Check against nullptr before calling if necessary.")]]
#endif
void
publish(std::shared_ptr<const rcl_serialized_message_t> serialized_msg)
{
return this->publish(serialized_msg.get());
return this->do_serialized_publish(serialized_msg.get());
}
std::shared_ptr<MessageAlloc> get_allocator() const
@@ -360,7 +207,7 @@ protected:
void
do_inter_process_publish(const MessageT * msg)
{
auto status = rcl_publish(&publisher_handle_, msg);
auto status = rcl_publish(&publisher_handle_, msg, nullptr);
if (RCL_RET_PUBLISHER_INVALID == status) {
rcl_reset_error(); // next call will reset error message if not context
if (rcl_publisher_is_valid_except_context(&publisher_handle_)) {
@@ -376,6 +223,77 @@ protected:
}
}
void
do_serialized_publish(const rcl_serialized_message_t * serialized_msg)
{
if (intra_process_is_enabled_) {
// 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, nullptr);
if (RCL_RET_OK != status) {
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish serialized message");
}
}
void
do_intra_process_publish(uint64_t message_seq)
{
rcl_interfaces::msg::IntraProcessMessage ipm;
ipm.publisher_id = intra_process_publisher_id_;
ipm.message_sequence = message_seq;
auto status = rcl_publish(&intra_process_publisher_handle_, &ipm, nullptr);
if (RCL_RET_PUBLISHER_INVALID == status) {
rcl_reset_error(); // next call will reset error message if not context
if (rcl_publisher_is_valid_except_context(&intra_process_publisher_handle_)) {
rcl_context_t * context = rcl_publisher_get_context(&intra_process_publisher_handle_);
if (nullptr != context && !rcl_context_is_valid(context)) {
// publisher is invalid due to context being shutdown
return;
}
}
}
if (RCL_RET_OK != status) {
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish intra process message");
}
}
uint64_t
store_intra_process_message(
uint64_t publisher_id,
std::shared_ptr<const MessageT> msg)
{
auto ipm = weak_ipm_.lock();
if (!ipm) {
throw std::runtime_error(
"intra process publish called after destruction of intra process manager");
}
if (!msg) {
throw std::runtime_error("cannot publisher msg which is a null pointer");
}
uint64_t message_seq =
ipm->template store_intra_process_message<MessageT, Alloc>(publisher_id, msg);
return message_seq;
}
uint64_t
store_intra_process_message(
uint64_t publisher_id,
std::unique_ptr<MessageT, MessageDeleter> msg)
{
auto ipm = weak_ipm_.lock();
if (!ipm) {
throw std::runtime_error(
"intra process publish called after destruction of intra process manager");
}
if (!msg) {
throw std::runtime_error("cannot publisher msg which is a null pointer");
}
uint64_t message_seq =
ipm->template store_intra_process_message<MessageT, Alloc>(publisher_id, std::move(msg));
return message_seq;
}
std::shared_ptr<MessageAlloc> message_allocator_;
MessageDeleter message_deleter_;

View File

@@ -0,0 +1,232 @@
// Copyright 2014 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__PUBLISHER_BASE_HPP_
#define RCLCPP__PUBLISHER_BASE_HPP_
#include <rmw/error_handling.h>
#include <rmw/rmw.h>
#include <functional>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "rcl/publisher.h"
#include "rclcpp/macros.hpp"
#include "rclcpp/mapped_ring_buffer.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
// Forward declaration is used for friend statement.
namespace node_interfaces
{
class NodeBaseInterface;
class NodeTopicsInterface;
}
namespace intra_process_manager
{
/**
* IntraProcessManager is forward declared here, avoiding a circular inclusion between
* `intra_process_manager.hpp` and `publisher_base.hpp`.
*/
class IntraProcessManager;
}
class PublisherBase
{
friend ::rclcpp::node_interfaces::NodeTopicsInterface;
public:
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
/// Default constructor.
/**
* Typically, a publisher is not created through this method, but instead is created through a
* call to `Node::create_publisher`.
* \param[in] node_base A pointer to the NodeBaseInterface for the parent node.
* \param[in] topic The topic that this publisher publishes on.
* \param[in] type_support The type support structure for the type to be published.
* \param[in] publisher_options QoS settings for this publisher.
*/
RCLCPP_PUBLIC
PublisherBase(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rosidl_message_type_support_t & type_support,
const rcl_publisher_options_t & publisher_options);
RCLCPP_PUBLIC
virtual ~PublisherBase();
/// Get the topic that this publisher publishes on.
/** \return The topic name. */
RCLCPP_PUBLIC
const char *
get_topic_name() const;
/// Get the queue size for this publisher.
/** \return The queue size. */
RCLCPP_PUBLIC
size_t
get_queue_size() const;
/// Get the global identifier for this publisher (used in rmw and by DDS).
/** \return The gid. */
RCLCPP_PUBLIC
const rmw_gid_t &
get_gid() const;
/// Get the global identifier for this publisher used by intra-process communication.
/** \return The intra-process gid. */
RCLCPP_PUBLIC
const rmw_gid_t &
get_intra_process_gid() const;
/// Get the rcl publisher handle.
/** \return The rcl publisher handle. */
RCLCPP_PUBLIC
rcl_publisher_t *
get_publisher_handle();
/// Get the rcl publisher handle.
/** \return The rcl publisher handle. */
RCLCPP_PUBLIC
const rcl_publisher_t *
get_publisher_handle() const;
/// Get all the QoS event handlers associated with this publisher.
/** \return The vector of QoS event handlers. */
RCLCPP_PUBLIC
const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
get_event_handlers() const;
/// Get subscription count
/** \return The number of subscriptions. */
RCLCPP_PUBLIC
size_t
get_subscription_count() const;
/// Get intraprocess subscription count
/** \return The number of intraprocess subscriptions. */
RCLCPP_PUBLIC
size_t
get_intra_process_subscription_count() const;
/// Manually assert that this Publisher is alive (for RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC).
/**
* If the rmw Liveliness policy is set to RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC, the creator
* of this publisher may manually call `assert_liveliness` at some point in time to signal to the
* rest of the system that this Node is still alive.
*
* \return `true` if the liveliness was asserted successfully, otherwise `false`
*/
RCLCPP_PUBLIC
RCUTILS_WARN_UNUSED
bool
assert_liveliness() const;
/// Get the actual QoS settings, after the defaults have been determined.
/**
* The actual configuration applied when using RMW_QOS_POLICY_*_SYSTEM_DEFAULT
* can only be resolved after the creation of the publisher, and it
* depends on the underlying rmw implementation.
* If the underlying setting in use can't be represented in ROS terms,
* it will be set to RMW_QOS_POLICY_*_UNKNOWN.
* May throw runtime_error when an unexpected error occurs.
* \return The actual qos settings.
*/
RCLCPP_PUBLIC
rmw_qos_profile_t
get_actual_qos() const;
/// Compare this publisher to a gid.
/**
* Note that this function calls the next function.
* \param[in] gid Reference to a gid.
* \return True if the publisher's gid matches the input.
*/
RCLCPP_PUBLIC
bool
operator==(const rmw_gid_t & gid) const;
/// Compare this publisher to a pointer gid.
/**
* A wrapper for comparing this publisher's gid to the input using rmw_compare_gids_equal.
* \param[in] gid A pointer to a gid.
* \return True if this publisher's gid matches the input.
*/
RCLCPP_PUBLIC
bool
operator==(const rmw_gid_t * gid) const;
using IntraProcessManagerSharedPtr =
std::shared_ptr<rclcpp::intra_process_manager::IntraProcessManager>;
/// Implementation utility function that creates a typed mapped ring buffer.
RCLCPP_PUBLIC
mapped_ring_buffer::MappedRingBufferBase::SharedPtr
virtual make_mapped_ring_buffer(size_t size) const;
/// Implementation utility function used to setup intra process publishing after creation.
RCLCPP_PUBLIC
void
setup_intra_process(
uint64_t intra_process_publisher_id,
IntraProcessManagerSharedPtr ipm,
const rcl_publisher_options_t & intra_process_options);
protected:
template<typename EventCallbackT>
void
add_event_handler(
const EventCallbackT & callback,
const rcl_publisher_event_type_t event_type)
{
auto handler = std::make_shared<QOSEventHandler<EventCallbackT>>(
callback,
rcl_publisher_event_init,
&publisher_handle_,
event_type);
event_handlers_.emplace_back(handler);
}
std::shared_ptr<rcl_node_t> rcl_node_handle_;
rcl_publisher_t publisher_handle_ = rcl_get_zero_initialized_publisher();
rcl_publisher_t intra_process_publisher_handle_ = rcl_get_zero_initialized_publisher();
std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;
using IntraProcessManagerWeakPtr =
std::weak_ptr<rclcpp::intra_process_manager::IntraProcessManager>;
bool intra_process_is_enabled_;
IntraProcessManagerWeakPtr weak_ipm_;
uint64_t intra_process_publisher_id_;
rmw_gid_t rmw_gid_;
rmw_gid_t intra_process_rmw_gid_;
};
} // namespace rclcpp
#endif // RCLCPP__PUBLISHER_BASE_HPP_

View File

@@ -49,94 +49,38 @@ struct PublisherFactory
rclcpp::PublisherBase::SharedPtr(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_publisher_options_t & publisher_options)>;
const 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)>;
AddPublisherToIntraProcessManagerFunction add_publisher_to_intra_process_manager;
// Creates the callback function which is called on each
// PublisherT::publish() and which handles the intra process transmission of
// the message being published.
using SharedPublishCallbackFactoryFunction = std::function<
rclcpp::PublisherBase::StoreMessageCallbackT(
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm)>;
SharedPublishCallbackFactoryFunction create_shared_publish_callback;
};
/// Return a PublisherFactory with functions setup for creating a PublisherT<MessageT, Alloc>.
template<typename MessageT, typename Alloc, typename PublisherT>
PublisherFactory
create_publisher_factory(std::shared_ptr<Alloc> allocator)
create_publisher_factory(
const PublisherEventCallbacks & event_callbacks,
std::shared_ptr<Alloc> allocator)
{
PublisherFactory factory;
// factory function that creates a MessageT specific PublisherT
factory.create_typed_publisher =
[allocator](
[event_callbacks, allocator](
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_publisher_options_t & publisher_options) -> std::shared_ptr<PublisherT>
const rcl_publisher_options_t & publisher_options
) -> std::shared_ptr<PublisherT>
{
auto options_copy = publisher_options;
auto message_alloc = std::make_shared<typename PublisherT::MessageAlloc>(*allocator.get());
publisher_options.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc.get());
options_copy.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc.get());
return std::make_shared<PublisherT>(node_base, topic_name, publisher_options, message_alloc);
};
// function to add a publisher to the intra process manager
factory.add_publisher_to_intra_process_manager =
[](
rclcpp::intra_process_manager::IntraProcessManager * ipm,
rclcpp::PublisherBase::SharedPtr publisher) -> uint64_t
{
return ipm->add_publisher<MessageT, Alloc>(std::dynamic_pointer_cast<PublisherT>(publisher));
};
// function to create a shared publish callback std::function
using StoreMessageCallbackT = rclcpp::PublisherBase::StoreMessageCallbackT;
factory.create_shared_publish_callback =
[](rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm) -> StoreMessageCallbackT
{
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = ipm;
// this function is called on each call to publish() and handles storing
// of the published message in the intra process manager
auto shared_publish_callback =
[weak_ipm](uint64_t publisher_id, void * msg, const std::type_info & type_info) -> uint64_t
{
auto ipm = weak_ipm.lock();
if (!ipm) {
// TODO(wjwwood): should this just return silently? Or maybe return with a warning?
throw std::runtime_error(
"intra process publish called after destruction of intra process manager");
}
if (!msg) {
throw std::runtime_error("cannot publisher msg which is a null pointer");
}
auto & message_type_info = typeid(MessageT);
if (message_type_info != type_info) {
throw std::runtime_error(
std::string("published type '") + type_info.name() +
"' is incompatible from the publisher type '" + message_type_info.name() + "'");
}
MessageT * typed_message_ptr = static_cast<MessageT *>(msg);
using MessageDeleter = typename Publisher<MessageT, Alloc>::MessageDeleter;
std::unique_ptr<MessageT, MessageDeleter> unique_msg(typed_message_ptr);
uint64_t message_seq =
ipm->store_intra_process_message<MessageT, Alloc>(publisher_id, unique_msg);
return message_seq;
};
return shared_publish_callback;
return std::make_shared<PublisherT>(
node_base,
topic_name,
options_copy,
event_callbacks,
message_alloc);
};
// return the factory now that it is populated

View File

@@ -19,22 +19,56 @@
#include <string>
#include <vector>
#include "rclcpp/callback_group.hpp"
#include "rclcpp/intra_process_setting.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rcl/publisher.h"
namespace rclcpp
{
/// Structure containing optional configuration for Publishers.
template<typename Allocator>
struct PublisherOptionsWithAllocator
/// Non-templated part of PublisherOptionsWithAllocator<Allocator>.
struct PublisherOptionsBase
{
/// The quality of service profile to pass on to the rmw implementation.
rmw_qos_profile_t qos_profile = rmw_qos_profile_default;
/// Optional custom allocator.
std::shared_ptr<Allocator> allocator = nullptr;
/// Setting to explicitly set intraprocess communications.
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault;
/// Callbacks for various events related to publishers.
PublisherEventCallbacks event_callbacks;
/// Callback group in which the waitable items from the publisher should be placed.
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group;
};
/// Structure containing optional configuration for Publishers.
template<typename Allocator>
struct PublisherOptionsWithAllocator : public PublisherOptionsBase
{
/// Optional custom allocator.
std::shared_ptr<Allocator> allocator = nullptr;
PublisherOptionsWithAllocator<Allocator>() {}
/// Constructor using base class as input.
explicit PublisherOptionsWithAllocator(const PublisherOptionsBase & publisher_options_base)
: PublisherOptionsBase(publisher_options_base)
{}
/// Convert this class, and a rclcpp::QoS, into an rcl_publisher_options_t.
template<typename MessageT>
rcl_publisher_options_t
to_rcl_publisher_options(const rclcpp::QoS & qos) const
{
rcl_publisher_options_t result;
using AllocatorTraits = std::allocator_traits<Allocator>;
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
auto message_alloc = std::make_shared<MessageAllocatorT>(*allocator.get());
result.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc);
result.qos = qos.get_rmw_qos_profile();
return result;
}
};
using PublisherOptions = PublisherOptionsWithAllocator<std::allocator<void>>;

View File

@@ -0,0 +1,206 @@
// Copyright 2019 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__QOS_HPP_
#define RCLCPP__QOS_HPP_
#include <rmw/qos_profiles.h>
#include <rmw/types.h>
#include "rclcpp/duration.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// QoS initialization values, cannot be created directly, use KeepAll or KeepLast instead.
struct RCLCPP_PUBLIC QoSInitialization
{
rmw_qos_history_policy_t history_policy;
size_t depth;
/// Constructor which takes both a history policy and a depth (even if it would be unused).
QoSInitialization(rmw_qos_history_policy_t history_policy_arg, size_t depth_arg);
/// Create a QoSInitialization from an existing rmw_qos_profile_t, using its history and depth.
static
QoSInitialization
from_rmw(const rmw_qos_profile_t & rmw_qos);
};
/// Use to initialize the QoS with the keep_all history setting.
struct RCLCPP_PUBLIC KeepAll : public rclcpp::QoSInitialization
{
KeepAll();
};
/// Use to initialize the QoS with the keep_last history setting and the given depth.
struct RCLCPP_PUBLIC KeepLast : public rclcpp::QoSInitialization
{
explicit KeepLast(size_t depth);
};
/// Encapsulation of Quality of Service settings.
class RCLCPP_PUBLIC QoS
{
public:
/// Constructor which allows you to construct a QoS by giving the only required settings.
explicit
QoS(
const QoSInitialization & qos_initialization,
const rmw_qos_profile_t & initial_profile = rmw_qos_profile_default);
/// Conversion constructor to ease construction in the common case of just specifying depth.
/**
* Convenience constructor, equivalent to QoS(KeepLast(history_depth)).
*/
// cppcheck-suppress noExplicitConstructor
QoS(size_t history_depth); // NOLINT(runtime/explicit): conversion constructor
/// Return the rmw qos profile.
rmw_qos_profile_t &
get_rmw_qos_profile();
/// Return the rmw qos profile.
const rmw_qos_profile_t &
get_rmw_qos_profile() const;
/// Set the history policy.
QoS &
history(rmw_qos_history_policy_t history);
/// Set the history to keep last.
QoS &
keep_last(size_t depth);
/// Set the history to keep all.
QoS &
keep_all();
/// Set the reliability setting.
QoS &
reliability(rmw_qos_reliability_policy_t reliability);
/// Set the reliability setting to reliable.
QoS &
reliable();
/// Set the reliability setting to best effort.
QoS &
best_effort();
/// Set the durability setting.
QoS &
durability(rmw_qos_durability_policy_t durability);
/// Set the durability setting to volatile.
/**
* Note that this cannot be named `volatile` because it is a C++ keyword.
*/
QoS &
durability_volatile();
/// Set the durability setting to transient local.
QoS &
transient_local();
/// Set the deadline setting.
QoS &
deadline(rmw_time_t deadline);
/// Set the deadline setting, rclcpp::Duration.
QoS &
deadline(const rclcpp::Duration & deadline);
/// Set the lifespan setting.
QoS &
lifespan(rmw_time_t lifespan);
/// Set the lifespan setting, rclcpp::Duration.
QoS &
lifespan(const rclcpp::Duration & lifespan);
/// Set the liveliness setting.
QoS &
liveliness(rmw_qos_liveliness_policy_t liveliness);
/// Set the liveliness_lease_duration setting.
QoS &
liveliness_lease_duration(rmw_time_t liveliness_lease_duration);
/// Set the liveliness_lease_duration setting, rclcpp::Duration.
QoS &
liveliness_lease_duration(const rclcpp::Duration & liveliness_lease_duration);
/// Set the avoid_ros_namespace_conventions setting.
QoS &
avoid_ros_namespace_conventions(bool avoid_ros_namespace_conventions);
private:
rmw_qos_profile_t rmw_qos_profile_;
};
class RCLCPP_PUBLIC SensorDataQoS : public QoS
{
public:
explicit
SensorDataQoS(
const QoSInitialization & qos_initialization = (
QoSInitialization::from_rmw(rmw_qos_profile_sensor_data)
));
};
class RCLCPP_PUBLIC ParametersQoS : public QoS
{
public:
explicit
ParametersQoS(
const QoSInitialization & qos_initialization = (
QoSInitialization::from_rmw(rmw_qos_profile_parameters)
));
};
class RCLCPP_PUBLIC ServicesQoS : public QoS
{
public:
explicit
ServicesQoS(
const QoSInitialization & qos_initialization = (
QoSInitialization::from_rmw(rmw_qos_profile_services_default)
));
};
class RCLCPP_PUBLIC ParameterEventsQoS : public QoS
{
public:
explicit
ParameterEventsQoS(
const QoSInitialization & qos_initialization = (
QoSInitialization::from_rmw(rmw_qos_profile_parameter_events)
));
};
class RCLCPP_PUBLIC SystemDefaultsQoS : public QoS
{
public:
explicit
SystemDefaultsQoS(
const QoSInitialization & qos_initialization = (
QoSInitialization::from_rmw(rmw_qos_profile_system_default)
));
};
} // namespace rclcpp
#endif // RCLCPP__QOS_HPP_

View File

@@ -0,0 +1,126 @@
// Copyright 2019 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__QOS_EVENT_HPP_
#define RCLCPP__QOS_EVENT_HPP_
#include <functional>
#include "rcl/error_handling.h"
#include "rcutils/logging_macros.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/function_traits.hpp"
#include "rclcpp/waitable.hpp"
namespace rclcpp
{
using QOSDeadlineRequestedInfo = rmw_requested_deadline_missed_status_t;
using QOSDeadlineOfferedInfo = rmw_offered_deadline_missed_status_t;
using QOSLivelinessChangedInfo = rmw_liveliness_changed_status_t;
using QOSLivelinessLostInfo = rmw_liveliness_lost_status_t;
using QOSDeadlineRequestedCallbackType = std::function<void (QOSDeadlineRequestedInfo &)>;
using QOSDeadlineOfferedCallbackType = std::function<void (QOSDeadlineOfferedInfo &)>;
using QOSLivelinessChangedCallbackType = std::function<void (QOSLivelinessChangedInfo &)>;
using QOSLivelinessLostCallbackType = std::function<void (QOSLivelinessLostInfo &)>;
/// Contains callbacks for various types of events a Publisher can receive from the middleware.
struct PublisherEventCallbacks
{
QOSDeadlineOfferedCallbackType deadline_callback;
QOSLivelinessLostCallbackType liveliness_callback;
};
/// Contains callbacks for non-message events that a Subscription can receive from the middleware.
struct SubscriptionEventCallbacks
{
QOSDeadlineRequestedCallbackType deadline_callback;
QOSLivelinessChangedCallbackType liveliness_callback;
};
class QOSEventHandlerBase : public Waitable
{
public:
RCLCPP_PUBLIC
virtual ~QOSEventHandlerBase();
/// Get the number of ready events
RCLCPP_PUBLIC
size_t
get_number_of_ready_events() override;
/// Add the Waitable to a wait set.
RCLCPP_PUBLIC
bool
add_to_wait_set(rcl_wait_set_t * wait_set) override;
/// Check if the Waitable is ready.
RCLCPP_PUBLIC
bool
is_ready(rcl_wait_set_t * wait_set) override;
protected:
rcl_event_t event_handle_;
size_t wait_set_event_index_;
};
template<typename EventCallbackT>
class QOSEventHandler : public QOSEventHandlerBase
{
public:
template<typename InitFuncT, typename ParentHandleT, typename EventTypeEnum>
QOSEventHandler(
const EventCallbackT & callback,
InitFuncT init_func,
ParentHandleT parent_handle,
EventTypeEnum event_type)
: event_callback_(callback)
{
event_handle_ = rcl_get_zero_initialized_event();
rcl_ret_t ret = init_func(&event_handle_, parent_handle, event_type);
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create event");
}
}
/// Execute any entities of the Waitable that are ready.
void
execute() override
{
EventCallbackInfoT callback_info;
rcl_ret_t ret = rcl_take_event(&event_handle_, &callback_info);
if (ret != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't take event info: %s", rcl_get_error_string().str);
return;
}
event_callback_(callback_info);
}
private:
using EventCallbackInfoT = typename std::remove_reference<typename
rclcpp::function_traits::function_traits<EventCallbackT>::template argument_type<0>>::type;
EventCallbackT event_callback_;
};
} // namespace rclcpp
#endif // RCLCPP__QOS_EVENT_HPP_

View File

@@ -156,6 +156,16 @@ public:
}
}
/// Default constructor.
/**
* The constructor for a Service is almost never called directly.
* Instead, services should be instantiated through the function
* rclcpp::create_service().
*
* \param[in] node_handle NodeBaseInterface pointer that is used in part of the setup.
* \param[in] service_handle service handle.
* \param[in] any_callback User defined callback to call when a client request is received.
*/
Service(
std::shared_ptr<rcl_node_t> node_handle,
std::shared_ptr<rcl_service_t> service_handle,
@@ -174,6 +184,16 @@ public:
service_handle_ = service_handle;
}
/// Default constructor.
/**
* The constructor for a Service is almost never called directly.
* Instead, services should be instantiated through the function
* rclcpp::create_service().
*
* \param[in] node_handle NodeBaseInterface pointer that is used in part of the setup.
* \param[in] service_handle service handle.
* \param[in] any_callback User defined callback to call when a client request is received.
*/
Service(
std::shared_ptr<rcl_node_t> node_handle,
rcl_service_t * service_handle,

View File

@@ -150,7 +150,7 @@ public:
);
}
bool collect_entities(const WeakNodeVector & weak_nodes)
bool collect_entities(const WeakNodeList & weak_nodes)
{
bool has_invalid_weak_nodes = false;
for (auto & weak_node : weak_nodes) {
@@ -265,7 +265,7 @@ public:
virtual void
get_next_subscription(
executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
auto it = subscription_handles_.begin();
while (it != subscription_handles_.end()) {
@@ -309,7 +309,7 @@ public:
virtual void
get_next_service(
executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
auto it = service_handles_.begin();
while (it != service_handles_.end()) {
@@ -342,7 +342,7 @@ public:
}
virtual void
get_next_client(executor::AnyExecutable & any_exec, const WeakNodeVector & weak_nodes)
get_next_client(executor::AnyExecutable & any_exec, const WeakNodeList & weak_nodes)
{
auto it = client_handles_.begin();
while (it != client_handles_.end()) {
@@ -375,7 +375,7 @@ public:
}
virtual void
get_next_waitable(executor::AnyExecutable & any_exec, const WeakNodeVector & weak_nodes)
get_next_waitable(executor::AnyExecutable & any_exec, const WeakNodeList & weak_nodes)
{
auto it = waitable_handles_.begin();
while (it != waitable_handles_.end()) {
@@ -430,6 +430,15 @@ public:
return number_of_services;
}
size_t number_of_ready_events() const
{
size_t number_of_events = 0;
for (auto waitable : waitable_handles_) {
number_of_events += waitable->get_number_of_ready_events();
}
return number_of_events;
}
size_t number_of_ready_clients() const
{
size_t number_of_clients = client_handles_.size();

View File

@@ -23,6 +23,8 @@
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include "rcl/error_handling.h"
#include "rcl/subscription.h"
@@ -32,11 +34,15 @@
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/message_memory_strategy.hpp"
#include "rclcpp/subscription_base.hpp"
#include "rclcpp/subscription_traits.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/waitable.hpp"
namespace rclcpp
{
@@ -46,122 +52,6 @@ namespace node_interfaces
class NodeTopicsInterface;
} // namespace node_interfaces
namespace intra_process_manager
{
/**
* NOTE(ivanpauno): IntraProcessManager is forward declared here, avoiding a circular inclusion between intra_process_manager.hpp and publisher.hpp.
* SharedPtr and WeakPtr of the IntraProcessManager are defined again here, to avoid a warning for accessing a member of a forward declared class.
*/
class IntraProcessManager;
}
/// Virtual base class for subscriptions. This pattern allows us to iterate over different template
/// specializations of Subscription, among other things.
class SubscriptionBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase)
/// Default constructor.
/**
* \param[in] node_handle The rcl representation of the node that owns this subscription.
* \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
* \param[in] topic_name Name of the topic to subscribe to.
* \param[in] 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,
bool is_serialized = false);
/// Default destructor.
RCLCPP_PUBLIC
virtual ~SubscriptionBase();
/// Get the topic that this subscription is subscribed on.
RCLCPP_PUBLIC
const char *
get_topic_name() const;
RCLCPP_PUBLIC
std::shared_ptr<rcl_subscription_t>
get_subscription_handle();
RCLCPP_PUBLIC
const std::shared_ptr<rcl_subscription_t>
get_subscription_handle() const;
RCLCPP_PUBLIC
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.
* \param[in] message_info Metadata associated with this message.
*/
virtual void
handle_message(std::shared_ptr<void> & message, const rmw_message_info_t & message_info) = 0;
/// Return the message borrowed in create_message.
/** \param[in] message Shared pointer to the returned message. */
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;
/// Get matching publisher count
/** \return The number of publishers on this topic. */
RCLCPP_PUBLIC
size_t
get_publisher_count() const;
protected:
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_;
using IntraProcessManagerWeakPtr =
std::weak_ptr<rclcpp::intra_process_manager::IntraProcessManager>;
bool use_intra_process_;
IntraProcessManagerWeakPtr weak_ipm_;
uint64_t intra_process_subscription_id_;
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 CallbackMessageT,
@@ -174,6 +64,7 @@ public:
using MessageAllocTraits = allocator::AllocRebind<CallbackMessageT, Alloc>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using MessageDeleter = allocator::Deleter<MessageAlloc, CallbackMessageT>;
using ConstMessageSharedPtr = std::shared_ptr<const CallbackMessageT>;
using MessageUniquePtr = std::unique_ptr<CallbackMessageT, MessageDeleter>;
RCLCPP_SMART_PTR_DEFINITIONS(Subscription)
@@ -195,6 +86,7 @@ public:
const std::string & topic_name,
const rcl_subscription_options_t & subscription_options,
AnySubscriptionCallback<CallbackMessageT, Alloc> callback,
const SubscriptionEventCallbacks & event_callbacks,
typename message_memory_strategy::MessageMemoryStrategy<CallbackMessageT, Alloc>::SharedPtr
memory_strategy = message_memory_strategy::MessageMemoryStrategy<CallbackMessageT,
Alloc>::create_default())
@@ -205,10 +97,17 @@ public:
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),
matches_any_intra_process_publishers_(nullptr)
{}
message_memory_strategy_(memory_strategy)
{
if (event_callbacks.deadline_callback) {
this->add_event_handler(event_callbacks.deadline_callback,
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
}
if (event_callbacks.liveliness_callback) {
this->add_event_handler(event_callbacks.liveliness_callback,
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
}
}
/// Support dynamically setting the message memory strategy.
/**
@@ -238,12 +137,10 @@ public:
void handle_message(std::shared_ptr<void> & message, const rmw_message_info_t & message_info)
{
if (matches_any_intra_process_publishers_) {
if (matches_any_intra_process_publishers_(&message_info.publisher_gid)) {
// In this case, the message will be delivered via intra process and
// we should ignore this copy of the message.
return;
}
if (matches_any_intra_process_publishers(&message_info.publisher_gid)) {
// In this case, the message will be delivered via intra process and
// we should ignore this copy of the message.
return;
}
auto typed_message = std::static_pointer_cast<CallbackMessageT>(message);
any_callback_.dispatch(typed_message, message_info);
@@ -266,89 +163,118 @@ public:
rcl_interfaces::msg::IntraProcessMessage & ipm,
const rmw_message_info_t & message_info)
{
if (!get_intra_process_message_callback_) {
if (!use_intra_process_) {
// throw std::runtime_error(
// "handle_intra_process_message called before setup_intra_process");
// TODO(wjwwood): for now, this could mean that intra process was just not enabled.
// However, this can only really happen if this node has it disabled, but the other doesn't.
return;
}
MessageUniquePtr msg;
get_intra_process_message_callback_(
ipm.publisher_id,
ipm.message_sequence,
intra_process_subscription_id_,
msg);
if (!msg) {
// This either occurred because the publisher no longer exists or the
// message requested is no longer being stored.
// TODO(wjwwood): should we notify someone of this? log error, log warning?
if (!matches_any_intra_process_publishers(&message_info.publisher_gid)) {
// This intra-process message has not been created by a publisher from this context.
// we should ignore this copy of the message.
return;
}
any_callback_.dispatch_intra_process(msg, message_info);
}
using GetMessageCallbackType =
std::function<void (uint64_t, uint64_t, uint64_t, MessageUniquePtr &)>;
using MatchesAnyPublishersCallbackType = std::function<bool (const rmw_gid_t *)>;
/// Implemenation detail.
// TODO(ivanpauno): This can be moved to the base class. No reason to be here.
// Also get_intra_process_message_callback_ and matches_any_intra_process_publishers_.
void setup_intra_process(
uint64_t intra_process_subscription_id,
GetMessageCallbackType get_message_callback,
MatchesAnyPublishersCallbackType matches_any_publisher_callback,
IntraProcessManagerWeakPtr weak_ipm,
const rcl_subscription_options_t & intra_process_options)
{
std::string intra_process_topic_name = std::string(get_topic_name()) + "/_intra";
rcl_ret_t ret = rcl_subscription_init(
intra_process_subscription_handle_.get(),
node_handle_.get(),
rclcpp::type_support::get_intra_process_message_msg_type_support(),
intra_process_topic_name.c_str(),
&intra_process_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
auto rcl_node_handle = node_handle_.get();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
intra_process_topic_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle));
if (any_callback_.use_take_shared_method()) {
ConstMessageSharedPtr msg;
take_intra_process_message(
ipm.publisher_id,
ipm.message_sequence,
intra_process_subscription_id_,
msg);
if (!msg) {
// This can happen when having two nodes in different process both using intraprocess
// communication. It could happen too if the publisher no longer exists or the requested
// message is not longer being stored.
// TODO(ivanpauno): Print a warn message in the last two cases described above,
// but not in the first one.
return;
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create intra process subscription");
any_callback_.dispatch_intra_process(msg, message_info);
} else {
MessageUniquePtr msg;
take_intra_process_message(
ipm.publisher_id,
ipm.message_sequence,
intra_process_subscription_id_,
msg);
if (!msg) {
// This can happen when having two nodes in different process both using intraprocess
// communication. It could happen too if the publisher no longer exists or the requested
// message is not longer being stored.
// TODO(ivanpauno): Print a warn message in the last two cases described above,
// but not in the first one.
return;
}
any_callback_.dispatch_intra_process(std::move(msg), message_info);
}
intra_process_subscription_id_ = intra_process_subscription_id;
get_intra_process_message_callback_ = get_message_callback;
matches_any_intra_process_publishers_ = matches_any_publisher_callback;
weak_ipm_ = weak_ipm;
use_intra_process_ = true;
}
/// Implemenation detail.
const std::shared_ptr<rcl_subscription_t>
get_intra_process_subscription_handle() const
{
if (!get_intra_process_message_callback_) {
if (!use_intra_process_) {
return nullptr;
}
return intra_process_subscription_handle_;
}
private:
void
take_intra_process_message(
uint64_t publisher_id,
uint64_t message_sequence,
uint64_t subscription_id,
MessageUniquePtr & message)
{
auto ipm = weak_ipm_.lock();
if (!ipm) {
throw std::runtime_error(
"intra process take called after destruction of intra process manager");
}
ipm->template take_intra_process_message<CallbackMessageT, Alloc>(
publisher_id, message_sequence, subscription_id, message);
}
void
take_intra_process_message(
uint64_t publisher_id,
uint64_t message_sequence,
uint64_t subscription_id,
ConstMessageSharedPtr & message)
{
auto ipm = weak_ipm_.lock();
if (!ipm) {
throw std::runtime_error(
"intra process take called after destruction of intra process manager");
}
ipm->template take_intra_process_message<CallbackMessageT, Alloc>(
publisher_id, message_sequence, subscription_id, message);
}
bool
matches_any_intra_process_publishers(const rmw_gid_t * sender_gid)
{
if (!use_intra_process_) {
return false;
}
auto ipm = weak_ipm_.lock();
if (!ipm) {
throw std::runtime_error(
"intra process publisher check called "
"after destruction of intra process manager");
}
return ipm->matches_any_publishers(sender_gid);
}
RCLCPP_DISABLE_COPY(Subscription)
AnySubscriptionCallback<CallbackMessageT, Alloc> any_callback_;
typename message_memory_strategy::MessageMemoryStrategy<CallbackMessageT, Alloc>::SharedPtr
message_memory_strategy_;
GetMessageCallbackType get_intra_process_message_callback_;
MatchesAnyPublishersCallbackType matches_any_intra_process_publishers_;
};
} // namespace rclcpp

View File

@@ -0,0 +1,189 @@
// Copyright 2019 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_BASE_HPP_
#define RCLCPP__SUBSCRIPTION_BASE_HPP_
#include <memory>
#include <string>
#include <vector>
#include "rcl/subscription.h"
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rmw/rmw.h"
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
class NodeTopicsInterface;
} // namespace node_interfaces
namespace intra_process_manager
{
/**
* IntraProcessManager is forward declared here, avoiding a circular inclusion between
* `intra_process_manager.hpp` and `subscription_base.hpp`.
*/
class IntraProcessManager;
}
/// Virtual base class for subscriptions. This pattern allows us to iterate over different template
/// specializations of Subscription, among other things.
class SubscriptionBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase)
/// Default constructor.
/**
* \param[in] node_handle The rcl representation of the node that owns this subscription.
* \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
* \param[in] topic_name Name of the topic to subscribe to.
* \param[in] 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,
bool is_serialized = false);
/// Default destructor.
RCLCPP_PUBLIC
virtual ~SubscriptionBase();
/// Get the topic that this subscription is subscribed on.
RCLCPP_PUBLIC
const char *
get_topic_name() const;
RCLCPP_PUBLIC
std::shared_ptr<rcl_subscription_t>
get_subscription_handle();
RCLCPP_PUBLIC
const std::shared_ptr<rcl_subscription_t>
get_subscription_handle() const;
RCLCPP_PUBLIC
virtual const std::shared_ptr<rcl_subscription_t>
get_intra_process_subscription_handle() const;
/// Get all the QoS event handlers associated with this subscription.
/** \return The vector of QoS event handlers. */
RCLCPP_PUBLIC
const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
get_event_handlers() 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.
* \param[in] message_info Metadata associated with this message.
*/
virtual void
handle_message(std::shared_ptr<void> & message, const rmw_message_info_t & message_info) = 0;
/// Return the message borrowed in create_message.
/** \param[in] message Shared pointer to the returned message. */
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;
/// Get matching publisher count.
/** \return The number of publishers on this topic. */
RCLCPP_PUBLIC
size_t
get_publisher_count() const;
using IntraProcessManagerWeakPtr =
std::weak_ptr<rclcpp::intra_process_manager::IntraProcessManager>;
/// Implemenation detail.
void setup_intra_process(
uint64_t intra_process_subscription_id,
IntraProcessManagerWeakPtr weak_ipm,
const rcl_subscription_options_t & intra_process_options);
protected:
template<typename EventCallbackT>
void
add_event_handler(
const EventCallbackT & callback,
const rcl_subscription_event_type_t event_type)
{
auto handler = std::make_shared<QOSEventHandler<EventCallbackT>>(
callback,
rcl_subscription_event_init,
get_subscription_handle().get(),
event_type);
event_handlers_.emplace_back(handler);
}
std::shared_ptr<rcl_node_t> node_handle_;
std::shared_ptr<rcl_subscription_t> subscription_handle_;
std::shared_ptr<rcl_subscription_t> intra_process_subscription_handle_;
std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;
bool use_intra_process_;
IntraProcessManagerWeakPtr weak_ipm_;
uint64_t intra_process_subscription_id_;
private:
RCLCPP_DISABLE_COPY(SubscriptionBase)
rosidl_message_type_support_t type_support_;
bool is_serialized_;
};
} // namespace rclcpp
#endif // RCLCPP__SUBSCRIPTION_BASE_HPP_

View File

@@ -51,7 +51,7 @@ struct SubscriptionFactory
rclcpp::SubscriptionBase::SharedPtr(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_subscription_options_t & subscription_options)>;
const rcl_subscription_options_t & subscription_options)>;
SubscriptionFactoryFunction create_typed_subscription;
@@ -65,7 +65,12 @@ struct SubscriptionFactory
SetupIntraProcessFunction setup_intra_process;
};
/// Return a SubscriptionFactory with functions for creating a SubscriptionT<MessageT, Alloc>.
/// Return a SubscriptionFactory setup to create a SubscriptionT<MessageT, AllocatorT>.
/**
* \param[in] callback The user-defined callback function to receive a message
* \param[in] options Additional options for the creation of the Subscription.
* \param[in] msg_mem_strat The message memory strategy to use for allocating messages.
*/
template<
typename MessageT,
typename CallbackT,
@@ -75,6 +80,7 @@ template<
SubscriptionFactory
create_subscription_factory(
CallbackT && callback,
const SubscriptionEventCallbacks & event_callbacks,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT, Alloc>::SharedPtr
msg_mem_strat,
@@ -91,13 +97,14 @@ create_subscription_factory(
// factory function that creates a MessageT specific SubscriptionT
factory.create_typed_subscription =
[allocator, msg_mem_strat, any_subscription_callback, message_alloc](
[allocator, msg_mem_strat, any_subscription_callback, event_callbacks, message_alloc](
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_subscription_options_t & subscription_options
const rcl_subscription_options_t & subscription_options
) -> rclcpp::SubscriptionBase::SharedPtr
{
subscription_options.allocator =
auto options_copy = subscription_options;
options_copy.allocator =
rclcpp::allocator::get_rcl_allocator<CallbackMessageT>(*message_alloc.get());
using rclcpp::Subscription;
@@ -107,71 +114,14 @@ create_subscription_factory(
node_base->get_shared_rcl_node_handle(),
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
topic_name,
subscription_options,
options_copy,
any_subscription_callback,
event_callbacks,
msg_mem_strat);
auto sub_base_ptr = std::dynamic_pointer_cast<SubscriptionBase>(sub);
return sub_base_ptr;
};
// function that will setup intra process communications for the subscription
factory.setup_intra_process =
[message_alloc](
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm,
rclcpp::SubscriptionBase::SharedPtr subscription,
const rcl_subscription_options_t & subscription_options)
{
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = ipm;
uint64_t intra_process_subscription_id = ipm->add_subscription(subscription);
auto intra_process_options = rcl_subscription_get_default_options();
intra_process_options.allocator = rclcpp::allocator::get_rcl_allocator<CallbackMessageT>(
*message_alloc.get());
intra_process_options.qos = subscription_options.qos;
intra_process_options.ignore_local_publications = false;
// function that will be called to take a MessageT from the intra process manager
auto take_intra_process_message_func =
[weak_ipm](
uint64_t publisher_id,
uint64_t message_sequence,
uint64_t subscription_id,
typename rclcpp::Subscription<CallbackMessageT, Alloc>::MessageUniquePtr & message)
{
auto ipm = weak_ipm.lock();
if (!ipm) {
// TODO(wjwwood): should this just return silently? Or return with a logged warning?
throw std::runtime_error(
"intra process take called after destruction of intra process manager");
}
ipm->take_intra_process_message<CallbackMessageT, Alloc>(
publisher_id, message_sequence, subscription_id, message);
};
// function that is called to see if the publisher id matches any local publishers
auto matches_any_publisher_func =
[weak_ipm](const rmw_gid_t * sender_gid) -> bool
{
auto ipm = weak_ipm.lock();
if (!ipm) {
throw std::runtime_error(
"intra process publisher check called "
"after destruction of intra process manager");
}
return ipm->matches_any_publishers(sender_gid);
};
auto typed_sub_ptr = std::dynamic_pointer_cast<SubscriptionT>(subscription);
typed_sub_ptr->setup_intra_process(
intra_process_subscription_id,
take_intra_process_message_func,
matches_any_publisher_func,
weak_ipm,
intra_process_options
);
};
// end definition of factory function to setup intra process
// return the factory now that it is populated
return factory;
}

View File

@@ -19,28 +19,59 @@
#include <string>
#include <vector>
#include "rclcpp/callback_group.hpp"
#include "rclcpp/intra_process_setting.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// Structure containing optional configuration for Subscriptions.
template<typename Allocator>
struct SubscriptionOptionsWithAllocator
/// Non-template base class for subscription options.
struct SubscriptionOptionsBase
{
/// The quality of service profile to pass on to the rmw implementation.
rmw_qos_profile_t qos_profile = rmw_qos_profile_default;
/// Callbacks for events related to this subscription.
SubscriptionEventCallbacks event_callbacks;
/// True to ignore local publications.
bool ignore_local_publications = false;
/// The callback group for this subscription. NULL to use the default callback group.
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group = nullptr;
/// Optional custom allocator.
std::shared_ptr<Allocator> allocator = nullptr;
/// Setting to explicitly set intraprocess communications.
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault;
};
/// Structure containing optional configuration for Subscriptions.
template<typename Allocator>
struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
{
/// Optional custom allocator.
std::shared_ptr<Allocator> allocator = nullptr;
SubscriptionOptionsWithAllocator<Allocator>() {}
/// Constructor using base class as input.
explicit SubscriptionOptionsWithAllocator(
const SubscriptionOptionsBase & subscription_options_base)
: SubscriptionOptionsBase(subscription_options_base)
{}
/// Convert this class, with a rclcpp::QoS, into an rcl_subscription_options_t.
template<typename MessageT>
rcl_subscription_options_t
to_rcl_subscription_options(const rclcpp::QoS & qos) const
{
rcl_subscription_options_t result;
using AllocatorTraits = std::allocator_traits<Allocator>;
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
auto message_alloc = std::make_shared<MessageAllocatorT>(*allocator.get());
result.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc);
result.ignore_local_publications = this->ignore_local_publications;
result.qos = qos.get_rmw_qos_profile();
return result;
}
};
using SubscriptionOptions = SubscriptionOptionsWithAllocator<std::allocator<void>>;
} // namespace rclcpp

View File

@@ -18,9 +18,13 @@
#include <memory>
#include "rclcpp/function_traits.hpp"
#include "rcl/types.h"
namespace rclcpp
{
class QoS;
namespace subscription_traits
{
@@ -69,7 +73,20 @@ template<typename MessageT, typename Deleter>
struct extract_message_type<std::unique_ptr<MessageT, Deleter>>: extract_message_type<MessageT>
{};
template<typename CallbackT>
template<
typename CallbackT,
// Do not attempt if CallbackT is an integer (mistaken for depth)
typename = std::enable_if_t<!std::is_integral<
std::remove_cv_t<std::remove_reference_t<CallbackT>>>::value>,
// Do not attempt if CallbackT is a QoS (mistaken for qos)
typename = std::enable_if_t<!std::is_base_of<
rclcpp::QoS,
std::remove_cv_t<std::remove_reference_t<CallbackT>>>::value>,
// Do not attempt if CallbackT is a rmw_qos_profile_t (mistaken for qos profile)
typename = std::enable_if_t<!std::is_same<
rmw_qos_profile_t,
std::remove_cv_t<std::remove_reference_t<CallbackT>>>::value>
>
struct has_message_type : extract_message_type<
typename rclcpp::function_traits::function_traits<CallbackT>::template argument_type<0>>
{};

View File

@@ -34,17 +34,32 @@ public:
RCLCPP_PUBLIC
Time(int32_t seconds, uint32_t nanoseconds, rcl_clock_type_t clock_type = RCL_SYSTEM_TIME);
/// Time constructor
/**
* \param nanoseconds since time epoch
* \param clock_type clock type
*/
RCLCPP_PUBLIC
explicit Time(int64_t nanoseconds = 0, rcl_clock_type_t clock = RCL_SYSTEM_TIME);
explicit Time(int64_t nanoseconds = 0, rcl_clock_type_t clock_type = RCL_SYSTEM_TIME);
RCLCPP_PUBLIC
Time(const Time & rhs);
/// Time constructor
/**
* \param time_msg builtin_interfaces time message to copy
* \param clock_type clock type
* \throws std::runtime_error if seconds are negative
*/
RCLCPP_PUBLIC
Time(
const builtin_interfaces::msg::Time & time_msg,
rcl_clock_type_t ros_time = RCL_ROS_TIME);
rcl_clock_type_t clock_type = RCL_ROS_TIME);
/// Time constructor
/**
* \param time_point rcl_time_point_t structure to copy
*/
RCLCPP_PUBLIC
explicit Time(const rcl_time_point_t & time_point);
@@ -58,6 +73,12 @@ public:
Time &
operator=(const Time & rhs);
/**
* Assign Time from a builtin_interfaces::msg::Time instance.
* The clock_type will be reset to RCL_ROS_TIME.
* Equivalent to *this = Time(time_msg, RCL_ROS_TIME).
* \throws std::runtime_error if seconds are negative
*/
RCLCPP_PUBLIC
Time &
operator=(const builtin_interfaces::msg::Time & time_msg);

View File

@@ -25,8 +25,6 @@
#include "rcl_interfaces/msg/parameter_event.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/parameter_client.hpp"
#include "rclcpp/parameter_events_filter.hpp"
namespace rclcpp
@@ -100,9 +98,6 @@ private:
// Destroy the subscription for the clock topic
void destroy_clock_sub();
// Parameter Client pointer
std::shared_ptr<rclcpp::AsyncParametersClient> parameter_client_;
// Parameter Event subscription
using ParamMessageT = rcl_interfaces::msg::ParameterEvent;
using ParamSubscriptionT = rclcpp::Subscription<ParamMessageT, Alloc>;

View File

@@ -126,6 +126,7 @@ public:
* \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.
* \param[in] context custom context to be used.
*/
explicit GenericTimer(
Clock::SharedPtr clock, std::chrono::nanoseconds period, FunctorT && callback,

View File

@@ -61,6 +61,17 @@ public:
size_t
get_number_of_ready_clients();
/// Get the number of ready events
/**
* Returns a value of 0 by default.
* This should be overridden if the Waitable contains one or more events.
* \return The number of events associated with the Waitable.
*/
RCLCPP_PUBLIC
virtual
size_t
get_number_of_ready_events();
/// Get the number of ready services
/**
* Returns a value of 0 by default.
@@ -88,6 +99,7 @@ public:
/**
* \param[in] wait_set A handle to the wait set to add the Waitable to.
* \return `true` if the Waitable is added successfully, `false` otherwise.
* \throws rclcpp::execptions::RCLError from rcl_wait_set_add_*()
*/
RCLCPP_PUBLIC
virtual
@@ -107,7 +119,7 @@ public:
RCLCPP_PUBLIC
virtual
bool
is_ready(rcl_wait_set_t *) = 0;
is_ready(rcl_wait_set_t * wait_set) = 0;
/// Execute any entities of the Waitable that are ready.
/**

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp</name>
<version>0.7.1</version>
<version>0.7.16</version>
<description>The ROS client library in C++.</description>
<maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
<license>Apache License 2.0</license>

View File

@@ -14,14 +14,6 @@
#include "rclcpp/clock.hpp"
#include <memory>
#include <utility>
#include <vector>
#include "builtin_interfaces/msg/time.hpp"
#include "rcl/time.h"
#include "rclcpp/exceptions.hpp"
#include "rcutils/logging_macros.h"
@@ -30,8 +22,8 @@ namespace rclcpp
{
JumpHandler::JumpHandler(
std::function<void()> pre_callback,
std::function<void(const rcl_time_jump_t &)> post_callback,
pre_callback_t pre_callback,
post_callback_t post_callback,
const rcl_jump_threshold_t & threshold)
: pre_callback(pre_callback),
post_callback(post_callback),
@@ -43,8 +35,7 @@ Clock::Clock(rcl_clock_type_t clock_type)
allocator_ = rcl_get_default_allocator();
auto ret = rcl_clock_init(clock_type, &rcl_clock_, &allocator_);
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(
ret, "could not get current time stamp");
exceptions::throw_from_rcl_error(ret, "failed to initialize rcl clock");
}
}
@@ -63,8 +54,7 @@ Clock::now()
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");
exceptions::throw_from_rcl_error(ret, "could not get current time stamp");
}
return now;
@@ -78,23 +68,23 @@ Clock::ros_time_is_active()
return false;
}
bool is_enabled;
bool is_enabled = false;
auto ret = rcl_is_enabled_ros_time_override(&rcl_clock_, &is_enabled);
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(
exceptions::throw_from_rcl_error(
ret, "Failed to check ros_time_override_status");
}
return is_enabled;
}
rcl_clock_t *
Clock::get_clock_handle()
Clock::get_clock_handle() noexcept
{
return &rcl_clock_;
}
rcl_clock_type_t
Clock::get_clock_type()
Clock::get_clock_type() const noexcept
{
return rcl_clock_.type;
}
@@ -105,7 +95,10 @@ Clock::on_time_jump(
bool before_jump,
void * user_data)
{
rclcpp::JumpHandler * handler = static_cast<rclcpp::JumpHandler *>(user_data);
const auto * handler = static_cast<JumpHandler *>(user_data);
if (nullptr == handler) {
return;
}
if (before_jump && handler->pre_callback) {
handler->pre_callback();
} else if (!before_jump && handler->post_callback) {
@@ -113,36 +106,30 @@ Clock::on_time_jump(
}
}
rclcpp::JumpHandler::SharedPtr
JumpHandler::SharedPtr
Clock::create_jump_callback(
std::function<void()> pre_callback,
std::function<void(const rcl_time_jump_t &)> post_callback,
JumpHandler::pre_callback_t pre_callback,
JumpHandler::post_callback_t post_callback,
const rcl_jump_threshold_t & threshold)
{
// Allocate a new jump handler
auto handler = new rclcpp::JumpHandler(pre_callback, post_callback, threshold);
JumpHandler::UniquePtr handler(new JumpHandler(pre_callback, post_callback, threshold));
if (nullptr == handler) {
rclcpp::exceptions::throw_from_rcl_error(RCL_RET_BAD_ALLOC, "Failed to allocate jump handler");
throw std::bad_alloc{};
}
// 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);
Clock::on_time_jump, handler.get());
if (RCL_RET_OK != ret) {
delete handler;
handler = nullptr;
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to add time jump callback");
}
if (nullptr == handler) {
// imposible to reach here; added to make cppcheck happy
return nullptr;
exceptions::throw_from_rcl_error(ret, "Failed to add time jump callback");
}
// *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,
// TODO(dorezyuk) UB, if the clock leaves scope before the JumpHandler
return JumpHandler::SharedPtr(handler.release(), [this](JumpHandler * handler) noexcept {
rcl_ret_t ret = rcl_clock_remove_jump_callback(&rcl_clock_, Clock::on_time_jump,
handler);
delete handler;
handler = NULL;

View File

@@ -47,16 +47,14 @@ Duration::Duration(std::chrono::nanoseconds nanoseconds)
rcl_duration_.nanoseconds = nanoseconds.count();
}
Duration::Duration(const Duration & rhs)
{
rcl_duration_.nanoseconds = rhs.rcl_duration_.nanoseconds;
}
Duration::Duration(const Duration & rhs) = default;
Duration::Duration(
const builtin_interfaces::msg::Duration & duration_msg)
{
rcl_duration_.nanoseconds = RCL_S_TO_NS(static_cast<uint64_t>(duration_msg.sec));
rcl_duration_.nanoseconds += duration_msg.nanosec;
rcl_duration_.nanoseconds =
RCL_S_TO_NS(static_cast<rcl_duration_value_t>(duration_msg.sec));
rcl_duration_.nanoseconds += static_cast<rcl_duration_value_t>(duration_msg.nanosec);
}
Duration::Duration(const rcl_duration_t & duration)
@@ -65,31 +63,28 @@ Duration::Duration(const rcl_duration_t & duration)
// noop
}
Duration::~Duration()
{
}
Duration::operator builtin_interfaces::msg::Duration() const
{
builtin_interfaces::msg::Duration msg_duration;
msg_duration.sec = static_cast<std::int32_t>(RCL_NS_TO_S(rcl_duration_.nanoseconds));
msg_duration.nanosec =
static_cast<std::uint32_t>(rcl_duration_.nanoseconds % (1000 * 1000 * 1000));
constexpr rcl_duration_value_t kDivisor = RCL_S_TO_NS(1);
const auto result = std::div(rcl_duration_.nanoseconds, kDivisor);
if (result.rem >= 0) {
msg_duration.sec = static_cast<std::int32_t>(result.quot);
msg_duration.nanosec = static_cast<std::uint32_t>(result.rem);
} else {
msg_duration.sec = static_cast<std::int32_t>(result.quot - 1);
msg_duration.nanosec = static_cast<std::uint32_t>(kDivisor + result.rem);
}
return msg_duration;
}
Duration &
Duration::operator=(const Duration & rhs)
{
rcl_duration_.nanoseconds = rhs.rcl_duration_.nanoseconds;
return *this;
}
Duration::operator=(const Duration & rhs) = default;
Duration &
Duration::operator=(const builtin_interfaces::msg::Duration & duration_msg)
{
rcl_duration_.nanoseconds = RCL_S_TO_NS(static_cast<int64_t>(duration_msg.sec));
rcl_duration_.nanoseconds += duration_msg.nanosec;
*this = Duration(duration_msg);
return *this;
}
@@ -99,6 +94,12 @@ Duration::operator==(const rclcpp::Duration & rhs) const
return rcl_duration_.nanoseconds == rhs.rcl_duration_.nanoseconds;
}
bool
Duration::operator!=(const rclcpp::Duration & rhs) const
{
return rcl_duration_.nanoseconds != rhs.rcl_duration_.nanoseconds;
}
bool
Duration::operator<(const rclcpp::Duration & rhs) const
{
@@ -126,15 +127,15 @@ Duration::operator>(const rclcpp::Duration & rhs) const
void
bounds_check_duration_sum(int64_t lhsns, int64_t rhsns, uint64_t max)
{
auto abs_lhs = (uint64_t)std::abs(lhsns);
auto abs_rhs = (uint64_t)std::abs(rhsns);
auto abs_lhs = static_cast<uint64_t>(std::abs(lhsns));
auto abs_rhs = static_cast<uint64_t>(std::abs(rhsns));
if (lhsns > 0 && rhsns > 0) {
if (abs_lhs + abs_rhs > (uint64_t) max) {
if (abs_lhs + abs_rhs > max) {
throw std::overflow_error("addition leads to int64_t overflow");
}
} else if (lhsns < 0 && rhsns < 0) {
if (abs_lhs + abs_rhs > (uint64_t) max) {
if (abs_lhs + abs_rhs > max) {
throw std::underflow_error("addition leads to int64_t underflow");
}
}
@@ -154,15 +155,15 @@ Duration::operator+(const rclcpp::Duration & rhs) const
void
bounds_check_duration_difference(int64_t lhsns, int64_t rhsns, uint64_t max)
{
auto abs_lhs = (uint64_t)std::abs(lhsns);
auto abs_rhs = (uint64_t)std::abs(rhsns);
auto abs_lhs = static_cast<uint64_t>(std::abs(lhsns));
auto abs_rhs = static_cast<uint64_t>(std::abs(rhsns));
if (lhsns > 0 && rhsns < 0) {
if (abs_lhs + abs_rhs > (uint64_t) max) {
if (abs_lhs + abs_rhs > max) {
throw std::overflow_error("duration subtraction leads to int64_t overflow");
}
} else if (lhsns < 0 && rhsns > 0) {
if (abs_lhs + abs_rhs > (uint64_t) max) {
if (abs_lhs + abs_rhs > max) {
throw std::underflow_error("duration subtraction leads to int64_t underflow");
}
}
@@ -185,8 +186,10 @@ 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 (abs_scale > 1.0 &&
abs_dns >
static_cast<uint64_t>(static_cast<long double>(max) / static_cast<long double>(abs_scale)))
{
if ((dns > 0 && scale > 0) || (dns < 0 && scale < 0)) {
throw std::overflow_error("duration scaling leads to int64_t overflow");
} else {
@@ -205,7 +208,9 @@ Duration::operator*(double scale) const
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));
long double scale_ld = static_cast<long double>(scale);
return Duration(static_cast<rcl_duration_value_t>(
static_cast<long double>(rcl_duration_.nanoseconds) * scale_ld));
}
rcl_duration_value_t
@@ -226,4 +231,19 @@ Duration::seconds() const
return std::chrono::duration<double>(std::chrono::nanoseconds(rcl_duration_.nanoseconds)).count();
}
rmw_time_t
Duration::to_rmw_time() const
{
if (rcl_duration_.nanoseconds < 0) {
throw std::runtime_error("rmw_time_t cannot be negative");
}
// reuse conversion logic from msg creation
builtin_interfaces::msg::Duration msg = *this;
rmw_time_t result;
result.sec = static_cast<uint64_t>(msg.sec);
result.nsec = static_cast<uint64_t>(msg.nanosec);
return result;
}
} // namespace rclcpp

View File

@@ -60,7 +60,11 @@ Executor::Executor(const ExecutorArgs & args)
// Store the context for later use.
context_ = args.context;
ret = rcl_wait_set_init(&wait_set_, 0, 2, 0, 0, 0, context_->get_rcl_context().get(), allocator);
ret = rcl_wait_set_init(
&wait_set_,
0, 2, 0, 0, 0, 0,
context_->get_rcl_context().get(),
allocator);
if (RCL_RET_OK != ret) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
@@ -87,6 +91,10 @@ Executor::~Executor()
}
}
weak_nodes_.clear();
for (auto & guard_condition : guard_conditions_) {
memory_strategy_->remove_guard_condition(guard_condition);
}
guard_conditions_.clear();
// Finalize the wait set.
if (rcl_wait_set_fini(&wait_set_) != RCL_RET_OK) {
@@ -124,6 +132,7 @@ Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_pt
}
}
weak_nodes_.push_back(node_ptr);
guard_conditions_.push_back(node_ptr->get_notify_guard_condition());
if (notify) {
// Interrupt waiting to handle new node
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
@@ -131,6 +140,7 @@ Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_pt
}
}
// Add the node's notify condition to the guard condition handles
std::unique_lock<std::mutex> lock(memory_strategy_mutex_);
memory_strategy_->add_guard_condition(node_ptr->get_notify_guard_condition());
}
@@ -144,17 +154,21 @@ void
Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
{
bool node_removed = false;
weak_nodes_.erase(
std::remove_if(
weak_nodes_.begin(), weak_nodes_.end(),
[&](rclcpp::node_interfaces::NodeBaseInterface::WeakPtr & i)
{
bool matched = (i.lock() == node_ptr);
node_removed |= matched;
return matched;
{
auto node_it = weak_nodes_.begin();
auto gc_it = guard_conditions_.begin();
while (node_it != weak_nodes_.end()) {
bool matched = (node_it->lock() == node_ptr);
if (matched) {
node_it = weak_nodes_.erase(node_it);
gc_it = guard_conditions_.erase(gc_it);
node_removed = true;
} else {
++node_it;
++gc_it;
}
)
);
}
}
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
has_executor.store(false);
if (notify) {
@@ -165,6 +179,7 @@ Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node
}
}
}
std::unique_lock<std::mutex> lock(memory_strategy_mutex_);
memory_strategy_->remove_guard_condition(node_ptr->get_notify_guard_condition());
}
@@ -219,7 +234,7 @@ Executor::spin_some(std::chrono::nanoseconds max_duration)
throw std::runtime_error("spin_some() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
while (spinning.load() && max_duration_not_elapsed()) {
while (rclcpp::ok(context_) && spinning.load() && max_duration_not_elapsed()) {
AnyExecutable any_exec;
if (get_next_executable(any_exec, std::chrono::milliseconds::zero())) {
execute_any_executable(any_exec);
@@ -229,6 +244,15 @@ Executor::spin_some(std::chrono::nanoseconds max_duration)
}
}
void
Executor::spin_once_impl(std::chrono::nanoseconds timeout)
{
AnyExecutable any_exec;
if (get_next_executable(any_exec, timeout)) {
execute_any_executable(any_exec);
}
}
void
Executor::spin_once(std::chrono::nanoseconds timeout)
{
@@ -236,10 +260,7 @@ Executor::spin_once(std::chrono::nanoseconds timeout)
throw std::runtime_error("spin_once() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
AnyExecutable any_exec;
if (get_next_executable(any_exec, timeout)) {
execute_any_executable(any_exec);
}
spin_once_impl(timeout);
}
void
@@ -304,7 +325,7 @@ Executor::execute_subscription(
auto serialized_msg = subscription->create_serialized_message();
auto ret = rcl_take_serialized_message(
subscription->get_subscription_handle().get(),
serialized_msg.get(), &message_info);
serialized_msg.get(), &message_info, nullptr);
if (RCL_RET_OK == ret) {
auto void_serialized_msg = std::static_pointer_cast<void>(serialized_msg);
subscription->handle_message(void_serialized_msg, message_info);
@@ -320,7 +341,7 @@ Executor::execute_subscription(
std::shared_ptr<void> message = subscription->create_message();
auto ret = rcl_take(
subscription->get_subscription_handle().get(),
message.get(), &message_info);
message.get(), &message_info, nullptr);
if (RCL_RET_OK == ret) {
subscription->handle_message(message, message_info);
} else if (RCL_RET_SUBSCRIPTION_TAKE_FAILED != ret) {
@@ -343,7 +364,8 @@ Executor::execute_intra_process_subscription(
rcl_ret_t status = rcl_take(
subscription->get_intra_process_subscription_handle().get(),
&ipm,
&message_info);
&message_info,
nullptr);
if (status == RCL_RET_OK) {
message_info.from_intra_process = true;
@@ -409,39 +431,47 @@ Executor::execute_client(
void
Executor::wait_for_work(std::chrono::nanoseconds timeout)
{
// Collect the subscriptions and timers to be waited on
memory_strategy_->clear_handles();
bool has_invalid_weak_nodes = memory_strategy_->collect_entities(weak_nodes_);
{
std::unique_lock<std::mutex> lock(memory_strategy_mutex_);
// Clean up any invalid nodes, if they were detected
if (has_invalid_weak_nodes) {
weak_nodes_.erase(
remove_if(
weak_nodes_.begin(), weak_nodes_.end(),
[](rclcpp::node_interfaces::NodeBaseInterface::WeakPtr i)
{
return i.expired();
// Collect the subscriptions and timers to be waited on
memory_strategy_->clear_handles();
bool has_invalid_weak_nodes = memory_strategy_->collect_entities(weak_nodes_);
// Clean up any invalid nodes, if they were detected
if (has_invalid_weak_nodes) {
auto node_it = weak_nodes_.begin();
auto gc_it = guard_conditions_.begin();
while (node_it != weak_nodes_.end()) {
if (node_it->expired()) {
node_it = weak_nodes_.erase(node_it);
memory_strategy_->remove_guard_condition(*gc_it);
gc_it = guard_conditions_.erase(gc_it);
} else {
++node_it;
++gc_it;
}
)
);
}
// clear wait set
if (rcl_wait_set_clear(&wait_set_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear wait set");
}
}
}
// clear wait set
if (rcl_wait_set_clear(&wait_set_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear wait set");
}
// The size of waitables are accounted for in size of the other entities
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 wait set : ") + rcl_get_error_string().str);
}
// The size of waitables are accounted for in size of the other entities
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(),
memory_strategy_->number_of_ready_events());
if (RCL_RET_OK != ret) {
throw std::runtime_error(
std::string("Couldn't resize the wait set : ") + rcl_get_error_string().str);
}
if (!memory_strategy_->add_handles_to_wait_set(&wait_set_)) {
throw std::runtime_error("Couldn't fill wait set");
if (!memory_strategy_->add_handles_to_wait_set(&wait_set_)) {
throw std::runtime_error("Couldn't fill wait set");
}
}
rcl_ret_t status =
rcl_wait(&wait_set_, std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count());

View File

@@ -83,6 +83,11 @@ MultiThreadedExecutor::run(size_t)
if (any_exec.timer) {
// Guard against multiple threads getting the same timer.
if (scheduled_timers_.count(any_exec.timer) != 0) {
// Make sure that any_exec's callback group is reset before
// the lock is released.
if (any_exec.callback_group) {
any_exec.callback_group->can_be_taken_from().store(true);
}
continue;
}
scheduled_timers_.insert(any_exec.timer);

View File

@@ -79,6 +79,7 @@ GraphListener::start_if_not_started()
0, // number_of_timers
0, // number_of_clients
0, // number_of_services
0, // number_of_events
this->parent_context_->get_rcl_context().get(),
rcl_get_default_allocator());
if (RCL_RET_OK != ret) {
@@ -145,7 +146,7 @@ GraphListener::run_loop()
const size_t node_graph_interfaces_size = node_graph_interfaces_.size();
// Add 2 for the interrupt and shutdown guard conditions
if (wait_set_.size_of_guard_conditions < (node_graph_interfaces_size + 2)) {
ret = rcl_wait_set_resize(&wait_set_, 0, node_graph_interfaces_size + 2, 0, 0, 0);
ret = rcl_wait_set_resize(&wait_set_, 0, node_graph_interfaces_size + 2, 0, 0, 0, 0);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to resize wait set");
}

View File

@@ -29,6 +29,21 @@ IntraProcessManager::IntraProcessManager(
IntraProcessManager::~IntraProcessManager()
{}
uint64_t
IntraProcessManager::add_publisher(
rclcpp::PublisherBase::SharedPtr publisher,
size_t buffer_size)
{
auto id = IntraProcessManager::get_next_unique_id();
size_t size = buffer_size > 0 ? buffer_size : publisher->get_queue_size();
auto mrb = publisher->make_mapped_ring_buffer(size);
impl_->add_publisher(id, publisher, mrb, size);
if (!mrb) {
throw std::runtime_error("failed to create a mapped ring buffer");
}
return id;
}
uint64_t
IntraProcessManager::add_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription)

View File

@@ -20,7 +20,7 @@ using rclcpp::memory_strategy::MemoryStrategy;
rclcpp::SubscriptionBase::SharedPtr
MemoryStrategy::get_subscription_by_handle(
std::shared_ptr<const rcl_subscription_t> subscriber_handle,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
@@ -51,7 +51,7 @@ MemoryStrategy::get_subscription_by_handle(
rclcpp::ServiceBase::SharedPtr
MemoryStrategy::get_service_by_handle(
std::shared_ptr<const rcl_service_t> service_handle,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
@@ -77,7 +77,7 @@ MemoryStrategy::get_service_by_handle(
rclcpp::ClientBase::SharedPtr
MemoryStrategy::get_client_by_handle(
std::shared_ptr<const rcl_client_t> client_handle,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
@@ -103,7 +103,7 @@ MemoryStrategy::get_client_by_handle(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
MemoryStrategy::get_node_by_group(
rclcpp::callback_group::CallbackGroup::SharedPtr group,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
if (!group) {
return nullptr;
@@ -126,7 +126,7 @@ MemoryStrategy::get_node_by_group(
rclcpp::callback_group::CallbackGroup::SharedPtr
MemoryStrategy::get_group_by_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
@@ -152,7 +152,7 @@ MemoryStrategy::get_group_by_subscription(
rclcpp::callback_group::CallbackGroup::SharedPtr
MemoryStrategy::get_group_by_service(
rclcpp::ServiceBase::SharedPtr service,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
@@ -178,7 +178,7 @@ MemoryStrategy::get_group_by_service(
rclcpp::callback_group::CallbackGroup::SharedPtr
MemoryStrategy::get_group_by_client(
rclcpp::ClientBase::SharedPtr client,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
@@ -204,7 +204,7 @@ MemoryStrategy::get_group_by_client(
rclcpp::callback_group::CallbackGroup::SharedPtr
MemoryStrategy::get_group_by_waitable(
rclcpp::Waitable::SharedPtr waitable,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();

View File

@@ -109,7 +109,11 @@ Node::Node(
const std::string & namespace_,
const NodeOptions & options)
: node_base_(new rclcpp::node_interfaces::NodeBase(
node_name, namespace_, options)),
node_name,
namespace_,
options.context(),
*(options.get_rcl_node_options()),
options.use_intra_process_comms())),
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())),
@@ -128,13 +132,13 @@ Node::Node(
node_topics_,
node_services_,
node_clock_,
options.initial_parameters(),
options.use_intra_process_comms(),
options.parameter_overrides(),
options.start_parameter_services(),
options.start_parameter_event_publisher(),
options.parameter_event_qos_profile(),
options.parameter_event_qos(),
options.parameter_event_publisher_options(),
options.allow_undeclared_parameters(),
options.automatically_declare_initial_parameters()
options.automatically_declare_parameters_from_overrides()
)),
node_time_source_(new rclcpp::node_interfaces::NodeTimeSource(
node_base_,
@@ -470,3 +474,9 @@ Node::get_node_options() const
{
return this->node_options_;
}
bool
Node::assert_liveliness() const
{
return node_base_->assert_liveliness();
}

View File

@@ -32,8 +32,11 @@ using rclcpp::node_interfaces::NodeBase;
NodeBase::NodeBase(
const std::string & node_name,
const std::string & namespace_,
const rclcpp::NodeOptions & options)
: context_(options.context()),
rclcpp::Context::SharedPtr context,
const rcl_node_options_t & rcl_node_options,
bool use_intra_process_default)
: context_(context),
use_intra_process_default_(use_intra_process_default),
node_handle_(nullptr),
default_callback_group_(nullptr),
associated_with_executor_(false),
@@ -42,7 +45,7 @@ NodeBase::NodeBase(
// Setup the guard condition that is notified when changes occur in the graph.
rcl_guard_condition_options_t guard_condition_options = rcl_guard_condition_get_default_options();
rcl_ret_t ret = rcl_guard_condition_init(
&notify_guard_condition_, options.context()->get_rcl_context().get(), guard_condition_options);
&notify_guard_condition_, context_->get_rcl_context().get(), guard_condition_options);
if (ret != RCL_RET_OK) {
throw_from_rcl_error(ret, "failed to create interrupt guard condition");
}
@@ -63,7 +66,7 @@ NodeBase::NodeBase(
ret = rcl_node_init(
rcl_node.get(),
node_name.c_str(), namespace_.c_str(),
options.context()->get_rcl_context().get(), options.get_rcl_node_options());
context_->get_rcl_context().get(), &rcl_node_options);
if (ret != RCL_RET_OK) {
// Finalize the interrupt guard condition.
finalize_notify_guard_condition();
@@ -197,6 +200,12 @@ NodeBase::get_shared_rcl_node_handle() const
return node_handle_;
}
bool
NodeBase::assert_liveliness() const
{
return RCL_RET_OK == rcl_node_assert_liveliness(get_rcl_node_handle());
}
rclcpp::callback_group::CallbackGroup::SharedPtr
NodeBase::create_callback_group(rclcpp::callback_group::CallbackGroupType group_type)
{
@@ -253,3 +262,9 @@ NodeBase::acquire_notify_guard_condition_lock() const
{
return std::unique_lock<std::recursive_mutex>(notify_guard_condition_mutex_);
}
bool
NodeBase::get_use_intra_process_default() const
{
return use_intra_process_default_;
}

View File

@@ -14,6 +14,7 @@
#include "rclcpp/node_interfaces/node_graph.hpp"
#include <algorithm>
#include <map>
#include <string>
#include <utility>
@@ -136,9 +137,24 @@ 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);
}
std::transform(names_and_namespaces.begin(),
names_and_namespaces.end(),
std::back_inserter(nodes),
[](std::pair<std::string, std::string> nns) {
std::string return_string;
if (nns.second.back() == '/') {
return_string = nns.second + nns.first;
} else {
return_string = nns.second + '/' + nns.first;
}
// Quick check to make sure that we start with a slash
// Since fully-qualified strings need to
if (return_string.front() != '/') {
return_string = "/" + return_string;
}
return return_string;
}
);
return nodes;
}
@@ -173,10 +189,12 @@ NodeGraph::get_node_names_and_namespaces() const
throw std::runtime_error(error_msg);
}
std::vector<std::pair<std::string, std::string>> node_names(node_names_c.size);
std::vector<std::pair<std::string, std::string>> node_names;
node_names.reserve(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]));
node_names.emplace_back(node_names_c.data[i], node_namespaces_c.data[i]);
}
}
@@ -302,9 +320,11 @@ rclcpp::Event::SharedPtr
NodeGraph::get_graph_event()
{
auto event = rclcpp::Event::make_shared();
std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
graph_events_.push_back(event);
graph_users_count_++;
{
std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
graph_events_.push_back(event);
graph_users_count_++;
}
// on first call, add node to graph_listener_
if (should_add_to_graph_listener_.exchange(false)) {
graph_listener_->add_node(this);

View File

@@ -26,6 +26,9 @@
#include <rcl_yaml_param_parser/parser.h>
#include <cmath>
#include <cstdlib>
#include <limits>
#include <map>
#include <memory>
#include <sstream>
@@ -45,16 +48,16 @@ using rclcpp::node_interfaces::NodeParameters;
NodeParameters::NodeParameters(
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
const rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services,
const rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock,
const std::vector<rclcpp::Parameter> & initial_parameters,
bool use_intra_process,
const std::vector<rclcpp::Parameter> & parameter_overrides,
bool start_parameter_services,
bool start_parameter_event_publisher,
const rmw_qos_profile_t & parameter_event_qos_profile,
const rclcpp::QoS & parameter_event_qos,
const rclcpp::PublisherOptionsBase & parameter_event_publisher_options,
bool allow_undeclared_parameters,
bool automatically_declare_initial_parameters)
bool automatically_declare_parameters_from_overrides)
: allow_undeclared_(allow_undeclared_parameters),
events_publisher_(nullptr),
node_logging_(node_logging),
@@ -64,7 +67,9 @@ NodeParameters::NodeParameters(
using PublisherT = rclcpp::Publisher<MessageT>;
using AllocatorT = std::allocator<void>;
// TODO(wjwwood): expose this allocator through the Parameter interface.
auto allocator = std::make_shared<AllocatorT>();
rclcpp::PublisherOptionsWithAllocator<AllocatorT> publisher_options(
parameter_event_publisher_options);
publisher_options.allocator = std::make_shared<AllocatorT>();
if (start_parameter_services) {
parameter_service_ = std::make_shared<ParameterService>(node_base, node_services, this);
@@ -72,11 +77,10 @@ NodeParameters::NodeParameters(
if (start_parameter_event_publisher) {
events_publisher_ = rclcpp::create_publisher<MessageT, AllocatorT, PublisherT>(
node_topics.get(),
node_topics,
"parameter_events",
parameter_event_qos_profile,
use_intra_process,
allocator);
parameter_event_qos,
publisher_options);
}
// Get the node options
@@ -121,18 +125,7 @@ NodeParameters::NodeParameters(
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");
}
if ('/' == node_namespace.at(node_namespace.size() - 1)) {
combined_name_ = node_namespace + node_name;
} else {
combined_name_ = node_namespace + '/' + node_name;
}
combined_name_ = node_base->get_fully_qualified_name();
// TODO(sloretz) use rcl to parse yaml when circular dependency is solved
// See https://github.com/ros2/rcl/issues/252
@@ -151,28 +144,29 @@ NodeParameters::NodeParameters(
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) {
initial_parameter_values_[param.get_name()] =
rclcpp::ParameterValue(param.get_value_message());
for (auto iter = initial_map.begin(); initial_map.end() != iter; iter++) {
// TODO(cottsay) implement further wildcard matching
if (iter->first == "/**" || iter->first == combined_name_) {
// Combine parameter yaml files, overwriting values in older ones
for (auto & param : iter->second) {
parameter_overrides_[param.get_name()] =
rclcpp::ParameterValue(param.get_value_message());
}
}
}
}
// initial values passed to constructor overwrite yaml file sources
for (auto & param : initial_parameters) {
initial_parameter_values_[param.get_name()] =
// parameter overrides passed to constructor will overwrite overrides from yaml file sources
for (auto & param : parameter_overrides) {
parameter_overrides_[param.get_name()] =
rclcpp::ParameterValue(param.get_value_message());
}
// If asked, initialize any parameters that ended up in the initial parameter values,
// but did not get declared explcitily by this point.
if (automatically_declare_initial_parameters) {
for (const auto & pair : this->get_initial_parameter_values()) {
if (automatically_declare_parameters_from_overrides) {
for (const auto & pair : this->get_parameter_overrides()) {
if (!this->has_parameter(pair.first)) {
this->declare_parameter(
pair.first,
@@ -195,6 +189,103 @@ __lockless_has_parameter(
return parameters.find(name) != parameters.end();
}
// see https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon
RCLCPP_LOCAL
bool
__are_doubles_equal(double x, double y, double ulp = 100.0)
{
return std::abs(x - y) <= std::numeric_limits<double>::epsilon() * std::abs(x + y) * ulp;
}
RCLCPP_LOCAL
inline
void
format_reason(std::string & reason, const std::string & name, const char * range_type)
{
std::ostringstream ss;
ss << "Parameter {" << name << "} doesn't comply with " << range_type << " range.";
reason = ss.str();
}
RCLCPP_LOCAL
rcl_interfaces::msg::SetParametersResult
__check_parameter_value_in_range(
const rcl_interfaces::msg::ParameterDescriptor & descriptor,
const rclcpp::ParameterValue & value)
{
rcl_interfaces::msg::SetParametersResult result;
result.successful = true;
if (!descriptor.integer_range.empty() && value.get_type() == rclcpp::PARAMETER_INTEGER) {
int64_t v = value.get<int64_t>();
auto integer_range = descriptor.integer_range.at(0);
if (v == integer_range.from_value || v == integer_range.to_value) {
return result;
}
if ((v < integer_range.from_value) || (v > integer_range.to_value)) {
result.successful = false;
format_reason(result.reason, descriptor.name, "integer");
return result;
}
if (integer_range.step == 0) {
return result;
}
if (((v - integer_range.from_value) % integer_range.step) == 0) {
return result;
}
result.successful = false;
format_reason(result.reason, descriptor.name, "integer");
return result;
}
if (!descriptor.floating_point_range.empty() && value.get_type() == rclcpp::PARAMETER_DOUBLE) {
double v = value.get<double>();
auto fp_range = descriptor.floating_point_range.at(0);
if (__are_doubles_equal(v, fp_range.from_value) ||
__are_doubles_equal(v, fp_range.to_value))
{
return result;
}
if ((v < fp_range.from_value) || (v > fp_range.to_value)) {
result.successful = false;
format_reason(result.reason, descriptor.name, "floating point");
return result;
}
if (fp_range.step == 0.0) {
return result;
}
double rounded_div = std::round((v - fp_range.from_value) / fp_range.step);
if (__are_doubles_equal(v, fp_range.from_value + rounded_div * fp_range.step)) {
return result;
}
result.successful = false;
format_reason(result.reason, descriptor.name, "floating point");
return result;
}
return result;
}
// Return true if parameter values comply with the descriptors in parameter_infos.
RCLCPP_LOCAL
rcl_interfaces::msg::SetParametersResult
__check_parameters(
std::map<std::string, rclcpp::node_interfaces::ParameterInfo> & parameter_infos,
const std::vector<rclcpp::Parameter> & parameters)
{
rcl_interfaces::msg::SetParametersResult result;
result.successful = true;
for (const rclcpp::Parameter & parameter : parameters) {
const rcl_interfaces::msg::ParameterDescriptor & descriptor =
parameter_infos[parameter.get_name()].descriptor;
result = __check_parameter_value_in_range(
descriptor,
parameter.get_parameter_value());
if (!result.successful) {
break;
}
}
return result;
}
using OnParametersSetCallbackType =
rclcpp::node_interfaces::NodeParametersInterface::OnParametersSetCallbackType;
@@ -211,7 +302,14 @@ __set_parameters_atomically_common(
if (on_set_parameters_callback) {
result = on_set_parameters_callback(parameters);
}
if (!result.successful) {
return result;
}
// Check if the value being set complies with the descriptor.
result = __check_parameters(parameter_infos, parameters);
if (!result.successful) {
return result;
}
// If accepted, actually set the values.
if (result.successful) {
for (size_t i = 0; i < parameters.size(); ++i) {
@@ -233,19 +331,20 @@ __declare_parameter_common(
const rclcpp::ParameterValue & default_value,
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
std::map<std::string, rclcpp::node_interfaces::ParameterInfo> & parameters_out,
const std::map<std::string, rclcpp::ParameterValue> & initial_values,
const std::map<std::string, rclcpp::ParameterValue> & overrides,
OnParametersSetCallbackType on_set_parameters_callback,
rcl_interfaces::msg::ParameterEvent * parameter_event_out)
rcl_interfaces::msg::ParameterEvent * parameter_event_out,
bool use_overrides = true)
{
using rclcpp::node_interfaces::ParameterInfo;
std::map<std::string, ParameterInfo> parameter_infos {{name, ParameterInfo()}};
parameter_infos.at(name).descriptor = parameter_descriptor;
// Use the value from the initial_values if available, otherwise use the default.
// Use the value from the overrides if available, otherwise use the default.
const rclcpp::ParameterValue * initial_value = &default_value;
auto initial_value_it = initial_values.find(name);
if (initial_value_it != initial_values.end()) {
initial_value = &initial_value_it->second;
auto overrides_it = overrides.find(name);
if (use_overrides && overrides_it != overrides.end()) {
initial_value = &overrides_it->second;
}
// Check with the user's callback to see if the initial value can be set.
@@ -292,7 +391,7 @@ NodeParameters::declare_parameter(
default_value,
parameter_descriptor,
parameters_,
initial_parameter_values_,
parameter_overrides_,
on_parameters_set_callback_,
&parameter_event);
@@ -302,8 +401,10 @@ NodeParameters::declare_parameter(
"parameter '" + name + "' could not be set: " + result.reason);
}
// Publish the event.
events_publisher_->publish(parameter_event);
// Publish if events_publisher_ is not nullptr, which may be if disabled in the constructor.
if (nullptr != events_publisher_) {
events_publisher_->publish(parameter_event);
}
return parameters_.at(name).value;
}
@@ -421,9 +522,10 @@ NodeParameters::set_parameters_atomically(const std::vector<rclcpp::Parameter> &
parameter_to_be_declared->get_parameter_value(),
rcl_interfaces::msg::ParameterDescriptor(), // Implicit declare uses default descriptor.
staged_parameter_changes,
initial_parameter_values_,
parameter_overrides_,
nullptr, // callback is explicitly null, so that it is called only once, when setting below.
&parameter_event_msg);
&parameter_event_msg,
false);
if (!result.successful) {
// Declare failed, return knowing that nothing was changed because the
// staged changes were not applied.
@@ -501,10 +603,10 @@ NodeParameters::set_parameters_atomically(const std::vector<rclcpp::Parameter> &
// assumption: the parameter to be undeclared should be in the parameter infos map
assert(it != parameters_.end());
if (it != parameters_.end()) {
// Remove it and update the parameter event message.
parameters_.erase(it);
// Update the parameter event message and remove it.
parameter_event_msg.deleted_parameters.push_back(
rclcpp::Parameter(it->first, it->second.value).to_parameter_msg());
parameters_.erase(it);
}
}
@@ -741,7 +843,7 @@ NodeParameters::register_param_change_callback(ParametersCallbackFunction callba
#endif
const std::map<std::string, rclcpp::ParameterValue> &
NodeParameters::get_initial_parameter_values() const
NodeParameters::get_parameter_overrides() const
{
return initial_parameter_values_;
return parameter_overrides_;
}

View File

@@ -34,7 +34,7 @@ rclcpp::PublisherBase::SharedPtr
NodeTopics::create_publisher(
const std::string & topic_name,
const rclcpp::PublisherFactory & publisher_factory,
rcl_publisher_options_t & publisher_options,
const rcl_publisher_options_t & publisher_options,
bool use_intra_process)
{
// Create the MessageT specific Publisher using the factory, but store it as PublisherBase.
@@ -47,13 +47,17 @@ NodeTopics::create_publisher(
// Get the intra process manager instance for this context.
auto ipm = context->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
// Register the publisher with the intra process manager.
uint64_t intra_process_publisher_id =
publisher_factory.add_publisher_to_intra_process_manager(ipm.get(), publisher);
// Create a function to be called when publisher to do the intra process publish.
auto shared_publish_callback = publisher_factory.create_shared_publish_callback(ipm);
if (publisher_options.qos.history == RMW_QOS_POLICY_HISTORY_KEEP_ALL) {
throw std::invalid_argument(
"intraprocess communication is not allowed with keep all history qos policy");
}
if (publisher_options.qos.depth == 0) {
throw std::invalid_argument(
"intraprocess communication is not allowed with a zero qos history depth value");
}
uint64_t intra_process_publisher_id = ipm->add_publisher(publisher);
publisher->setup_intra_process(
intra_process_publisher_id,
shared_publish_callback,
ipm,
publisher_options);
}
@@ -64,11 +68,22 @@ NodeTopics::create_publisher(
void
NodeTopics::add_publisher(
rclcpp::PublisherBase::SharedPtr publisher)
rclcpp::PublisherBase::SharedPtr publisher,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group)
{
// The publisher is not added to a callback group or anthing like that for now.
// It may be stored within the NodeTopics class or the NodeBase class in the future.
(void)publisher;
// Assign to a group.
if (callback_group) {
if (!node_base_->callback_group_in_node(callback_group)) {
throw std::runtime_error("Cannot create publisher, callback group not in node.");
}
} else {
callback_group = node_base_->get_default_callback_group();
}
for (auto & publisher_event : publisher->get_event_handlers()) {
callback_group->add_waitable(publisher_event);
}
// Notify the executor that a new publisher was created using the parent Node.
{
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
@@ -84,7 +99,7 @@ rclcpp::SubscriptionBase::SharedPtr
NodeTopics::create_subscription(
const std::string & topic_name,
const rclcpp::SubscriptionFactory & subscription_factory,
rcl_subscription_options_t & subscription_options,
const rcl_subscription_options_t & subscription_options,
bool use_intra_process)
{
auto subscription = subscription_factory.create_typed_subscription(
@@ -93,10 +108,12 @@ NodeTopics::create_subscription(
// Setup intra process publishing if requested.
if (use_intra_process) {
auto context = node_base_->get_context();
auto intra_process_manager =
auto ipm =
context->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
subscription_factory.setup_intra_process(
intra_process_manager, subscription, subscription_options);
uint64_t intra_process_subscription_id = ipm->add_subscription(subscription);
auto options_copy = subscription_options;
options_copy.ignore_local_publications = false;
subscription->setup_intra_process(intra_process_subscription_id, ipm, options_copy);
}
// Return the completed subscription.
@@ -114,9 +131,13 @@ NodeTopics::add_subscription(
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create subscription, callback group not in node.");
}
callback_group->add_subscription(subscription);
} else {
node_base_->get_default_callback_group()->add_subscription(subscription);
callback_group = node_base_->get_default_callback_group();
}
callback_group->add_subscription(subscription);
for (auto & subscription_event : subscription->get_event_handlers()) {
callback_group->add_waitable(subscription_event);
}
// Notify the executor that a new subscription was created using the parent Node.
@@ -130,3 +151,9 @@ NodeTopics::add_subscription(
}
}
}
rclcpp::node_interfaces::NodeBaseInterface *
NodeTopics::get_node_base_interface() const
{
return node_base_;
}

View File

@@ -21,6 +21,8 @@
#include "rclcpp/exceptions.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/publisher_options.hpp"
#include "rclcpp/qos.hpp"
using rclcpp::exceptions::throw_from_rcl_error;
@@ -42,6 +44,9 @@ rcl_node_options_t_destructor(rcl_node_options_t * node_options)
"failed to finalize rcl node options: %s", rcl_get_error_string().str);
rcl_reset_error();
}
delete node_options;
node_options = nullptr;
}
}
} // namespace detail
@@ -60,12 +65,19 @@ NodeOptions &
NodeOptions::operator=(const NodeOptions & other)
{
if (this != &other) {
this->node_options_.reset();
this->context_ = other.context_;
this->arguments_ = other.arguments_;
this->initial_parameters_ = other.initial_parameters_;
this->parameter_overrides_ = other.parameter_overrides_;
this->use_global_arguments_ = other.use_global_arguments_;
this->use_intra_process_comms_ = other.use_intra_process_comms_;
this->start_parameter_services_ = other.start_parameter_services_;
this->start_parameter_event_publisher_ = other.start_parameter_event_publisher_;
this->parameter_event_qos_ = other.parameter_event_qos_;
this->parameter_event_publisher_options_ = other.parameter_event_publisher_options_;
this->allow_undeclared_parameters_ = other.allow_undeclared_parameters_;
this->automatically_declare_parameters_from_overrides_ =
other.automatically_declare_parameters_from_overrides_;
this->allocator_ = other.allocator_;
}
return *this;
@@ -90,7 +102,7 @@ NodeOptions::get_rcl_node_options() const
}
}
if (this->arguments_.size() > std::numeric_limits<int>::max()) {
if (this->arguments_.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
throw_from_rcl_error(RCL_RET_INVALID_ARGUMENT, "Too many args");
}
@@ -134,28 +146,28 @@ NodeOptions::arguments(const std::vector<std::string> & arguments)
}
std::vector<rclcpp::Parameter> &
NodeOptions::initial_parameters()
NodeOptions::parameter_overrides()
{
return this->initial_parameters_;
return this->parameter_overrides_;
}
const std::vector<rclcpp::Parameter> &
NodeOptions::initial_parameters() const
NodeOptions::parameter_overrides() const
{
return this->initial_parameters_;
return this->parameter_overrides_;
}
NodeOptions &
NodeOptions::initial_parameters(const std::vector<rclcpp::Parameter> & initial_parameters)
NodeOptions::parameter_overrides(const std::vector<rclcpp::Parameter> & parameter_overrides)
{
this->initial_parameters_ = initial_parameters;
this->parameter_overrides_ = parameter_overrides;
return *this;
}
bool
NodeOptions::use_global_arguments() const
{
return this->node_options_->use_global_arguments;
return this->use_global_arguments_;
}
NodeOptions &
@@ -205,16 +217,30 @@ NodeOptions::start_parameter_event_publisher(bool start_parameter_event_publishe
return *this;
}
const rmw_qos_profile_t &
NodeOptions::parameter_event_qos_profile() const
const rclcpp::QoS &
NodeOptions::parameter_event_qos() const
{
return this->parameter_event_qos_profile_;
return this->parameter_event_qos_;
}
NodeOptions &
NodeOptions::parameter_event_qos_profile(const rmw_qos_profile_t & parameter_event_qos_profile)
NodeOptions::parameter_event_qos(const rclcpp::QoS & parameter_event_qos)
{
this->parameter_event_qos_profile_ = parameter_event_qos_profile;
this->parameter_event_qos_ = parameter_event_qos;
return *this;
}
const rclcpp::PublisherOptionsBase &
NodeOptions::parameter_event_publisher_options() const
{
return parameter_event_publisher_options_;
}
NodeOptions &
NodeOptions::parameter_event_publisher_options(
const rclcpp::PublisherOptionsBase & parameter_event_publisher_options)
{
parameter_event_publisher_options_ = parameter_event_publisher_options;
return *this;
}
@@ -232,16 +258,17 @@ NodeOptions::allow_undeclared_parameters(bool allow_undeclared_parameters)
}
bool
NodeOptions::automatically_declare_initial_parameters() const
NodeOptions::automatically_declare_parameters_from_overrides() const
{
return this->automatically_declare_initial_parameters_;
return this->automatically_declare_parameters_from_overrides_;
}
NodeOptions &
NodeOptions::automatically_declare_initial_parameters(
bool automatically_declare_initial_parameters)
NodeOptions::automatically_declare_parameters_from_overrides(
bool automatically_declare_parameters_from_overrides)
{
this->automatically_declare_initial_parameters_ = automatically_declare_initial_parameters;
this->automatically_declare_parameters_from_overrides_ =
automatically_declare_parameters_from_overrides;
return *this;
}
@@ -275,7 +302,7 @@ NodeOptions::get_domain_id_from_env() const
_dupenv_s(&ros_domain_id, &ros_domain_id_size, env_var);
#endif
if (ros_domain_id) {
uint32_t number = strtoul(ros_domain_id, NULL, 0);
uint32_t number = static_cast<uint32_t>(strtoul(ros_domain_id, NULL, 0));
if (number == (std::numeric_limits<uint32_t>::max)()) {
#ifdef _WIN32
// free the ros_domain_id before throwing, if getenv was used on Windows

View File

@@ -146,7 +146,7 @@ AsyncParametersClient::get_parameters(
auto & pvalues = cb_f.get()->values;
for (auto & pvalue : pvalues) {
auto i = &pvalue - &pvalues[0];
auto i = static_cast<size_t>(&pvalue - &pvalues[0]);
rcl_interfaces::msg::Parameter parameter;
parameter.name = request->names[i];
parameter.value = pvalue;

View File

@@ -57,11 +57,15 @@ ParameterService::ParameterService(
const std::shared_ptr<rcl_interfaces::srv::GetParameterTypes::Request> request,
std::shared_ptr<rcl_interfaces::srv::GetParameterTypes::Response> response)
{
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::ParameterType>(type);
});
try {
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::ParameterType>(type);
});
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to get parameter types: %s", ex.what());
}
},
qos_profile, nullptr);
@@ -73,12 +77,20 @@ ParameterService::ParameterService(
const std::shared_ptr<rcl_interfaces::srv::SetParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::SetParameters::Response> response)
{
std::vector<rclcpp::Parameter> pvariants;
// Set parameters one-by-one, since there's no way to return a partial result if
// set_parameters() fails.
auto result = rcl_interfaces::msg::SetParametersResult();
for (auto & p : request->parameters) {
pvariants.push_back(rclcpp::Parameter::from_parameter_msg(p));
try {
result = node_params->set_parameters_atomically(
{rclcpp::Parameter::from_parameter_msg(p)});
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to set parameter: %s", ex.what());
result.successful = false;
result.reason = ex.what();
}
response->results.push_back(result);
}
auto results = node_params->set_parameters(pvariants);
response->results = results;
},
qos_profile, nullptr);
@@ -96,8 +108,15 @@ ParameterService::ParameterService(
[](const rcl_interfaces::msg::Parameter & p) {
return rclcpp::Parameter::from_parameter_msg(p);
});
auto result = node_params->set_parameters_atomically(pvariants);
response->result = result;
try {
auto result = node_params->set_parameters_atomically(pvariants);
response->result = result;
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"), "Failed to set parameters atomically: %s", ex.what());
response->result.successful = false;
response->result.reason = "One or more parameters wer not declared before setting";
}
},
qos_profile, nullptr);
@@ -109,8 +128,12 @@ ParameterService::ParameterService(
const std::shared_ptr<rcl_interfaces::srv::DescribeParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::DescribeParameters::Response> response)
{
auto descriptors = node_params->describe_parameters(request->names);
response->descriptors = descriptors;
try {
auto descriptors = node_params->describe_parameters(request->names);
response->descriptors = descriptors;
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to describe parameters: %s", ex.what());
}
},
qos_profile, nullptr);

View File

@@ -154,7 +154,7 @@ ParameterValue::ParameterValue(const int64_t int_value)
ParameterValue::ParameterValue(const float double_value)
{
value_.double_value = double_value;
value_.double_value = static_cast<double>(double_value);
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
}

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/publisher.hpp"
#include "rclcpp/publisher_base.hpp"
#include <rmw/error_handling.h>
#include <rmw/rmw.h>
@@ -22,6 +22,7 @@
#include <mutex>
#include <sstream>
#include <string>
#include <vector>
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rcutils/logging_macros.h"
@@ -30,10 +31,10 @@
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
using rclcpp::PublisherBase;
@@ -43,8 +44,7 @@ PublisherBase::PublisherBase(
const rosidl_message_type_support_t & type_support,
const rcl_publisher_options_t & publisher_options)
: rcl_node_handle_(node_base->get_shared_rcl_node_handle()),
intra_process_is_enabled_(false), intra_process_publisher_id_(0),
store_intra_process_message_(nullptr)
intra_process_is_enabled_(false), intra_process_publisher_id_(0)
{
rcl_ret_t ret = rcl_publisher_init(
&publisher_handle_,
@@ -81,6 +81,9 @@ PublisherBase::PublisherBase(
PublisherBase::~PublisherBase()
{
// must fini the events before fini-ing the publisher
event_handlers_.clear();
if (rcl_publisher_fini(&intra_process_publisher_handle_, rcl_node_handle_.get()) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
@@ -154,6 +157,12 @@ PublisherBase::get_publisher_handle() const
return &publisher_handle_;
}
const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
PublisherBase::get_event_handlers() const
{
return event_handlers_;
}
size_t
PublisherBase::get_subscription_count() const
{
@@ -208,6 +217,12 @@ PublisherBase::get_actual_qos() const
return *qos;
}
bool
PublisherBase::assert_liveliness() const
{
return RCL_RET_OK == rcl_publisher_assert_liveliness(&publisher_handle_);
}
bool
PublisherBase::operator==(const rmw_gid_t & gid) const
{
@@ -235,16 +250,22 @@ PublisherBase::operator==(const rmw_gid_t * gid) const
return result;
}
rclcpp::mapped_ring_buffer::MappedRingBufferBase::SharedPtr
PublisherBase::make_mapped_ring_buffer(size_t size) const
{
(void)size;
return nullptr;
}
void
PublisherBase::setup_intra_process(
uint64_t intra_process_publisher_id,
StoreMessageCallbackT store_callback,
IntraProcessManagerSharedPtr ipm,
const rcl_publisher_options_t & intra_process_options)
{
// Intraprocess configuration is not allowed with "durability" qos policy non "volatile".
if (this->get_actual_qos().durability != RMW_QOS_POLICY_DURABILITY_VOLATILE) {
throw exceptions::InvalidParametersException(
throw std::invalid_argument(
"intraprocess communication is not allowed with durability qos policy non-volatile");
}
const char * topic_name = this->get_topic_name();
@@ -275,7 +296,6 @@ PublisherBase::setup_intra_process(
}
intra_process_publisher_id_ = intra_process_publisher_id;
store_intra_process_message_ = store_callback;
weak_ipm_ = ipm;
intra_process_is_enabled_ = true;

207
rclcpp/src/rclcpp/qos.cpp Normal file
View File

@@ -0,0 +1,207 @@
// Copyright 2019 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/qos.hpp"
#include <rmw/types.h>
namespace rclcpp
{
QoSInitialization::QoSInitialization(rmw_qos_history_policy_t history_policy_arg, size_t depth_arg)
: history_policy(history_policy_arg), depth(depth_arg)
{}
QoSInitialization
QoSInitialization::from_rmw(const rmw_qos_profile_t & rmw_qos)
{
switch (rmw_qos.history) {
case RMW_QOS_POLICY_HISTORY_KEEP_ALL:
return KeepAll();
case RMW_QOS_POLICY_HISTORY_KEEP_LAST:
case RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT:
case RMW_QOS_POLICY_HISTORY_UNKNOWN:
default:
return KeepLast(rmw_qos.depth);
}
}
KeepAll::KeepAll()
: QoSInitialization(RMW_QOS_POLICY_HISTORY_KEEP_ALL, 0)
{}
KeepLast::KeepLast(size_t depth)
: QoSInitialization(RMW_QOS_POLICY_HISTORY_KEEP_LAST, depth)
{}
QoS::QoS(
const QoSInitialization & qos_initialization,
const rmw_qos_profile_t & initial_profile)
: rmw_qos_profile_(initial_profile)
{
rmw_qos_profile_.history = qos_initialization.history_policy;
rmw_qos_profile_.depth = qos_initialization.depth;
}
QoS::QoS(size_t history_depth)
: QoS(KeepLast(history_depth))
{}
rmw_qos_profile_t &
QoS::get_rmw_qos_profile()
{
return rmw_qos_profile_;
}
const rmw_qos_profile_t &
QoS::get_rmw_qos_profile() const
{
return rmw_qos_profile_;
}
QoS &
QoS::history(rmw_qos_history_policy_t history)
{
rmw_qos_profile_.history = history;
return *this;
}
QoS &
QoS::keep_last(size_t depth)
{
rmw_qos_profile_.history = RMW_QOS_POLICY_HISTORY_KEEP_LAST;
rmw_qos_profile_.depth = depth;
return *this;
}
QoS &
QoS::keep_all()
{
rmw_qos_profile_.history = RMW_QOS_POLICY_HISTORY_KEEP_ALL;
rmw_qos_profile_.depth = 0;
return *this;
}
QoS &
QoS::reliability(rmw_qos_reliability_policy_t reliability)
{
rmw_qos_profile_.reliability = reliability;
return *this;
}
QoS &
QoS::reliable()
{
return this->reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE);
}
QoS &
QoS::best_effort()
{
return this->reliability(RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT);
}
QoS &
QoS::durability(rmw_qos_durability_policy_t durability)
{
rmw_qos_profile_.durability = durability;
return *this;
}
QoS &
QoS::durability_volatile()
{
return this->durability(RMW_QOS_POLICY_DURABILITY_VOLATILE);
}
QoS &
QoS::transient_local()
{
return this->durability(RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL);
}
QoS &
QoS::deadline(rmw_time_t deadline)
{
rmw_qos_profile_.deadline = deadline;
return *this;
}
QoS &
QoS::deadline(const rclcpp::Duration & deadline)
{
return this->deadline(deadline.to_rmw_time());
}
QoS &
QoS::lifespan(rmw_time_t lifespan)
{
rmw_qos_profile_.lifespan = lifespan;
return *this;
}
QoS &
QoS::lifespan(const rclcpp::Duration & lifespan)
{
return this->lifespan(lifespan.to_rmw_time());
}
QoS &
QoS::liveliness(rmw_qos_liveliness_policy_t liveliness)
{
rmw_qos_profile_.liveliness = liveliness;
return *this;
}
QoS &
QoS::liveliness_lease_duration(rmw_time_t liveliness_lease_duration)
{
rmw_qos_profile_.liveliness_lease_duration = liveliness_lease_duration;
return *this;
}
QoS &
QoS::liveliness_lease_duration(const rclcpp::Duration & liveliness_lease_duration)
{
return this->liveliness_lease_duration(liveliness_lease_duration.to_rmw_time());
}
QoS &
QoS::avoid_ros_namespace_conventions(bool avoid_ros_namespace_conventions)
{
rmw_qos_profile_.avoid_ros_namespace_conventions = avoid_ros_namespace_conventions;
return *this;
}
SensorDataQoS::SensorDataQoS(const QoSInitialization & qos_initialization)
: QoS(qos_initialization, rmw_qos_profile_sensor_data)
{}
ParametersQoS::ParametersQoS(const QoSInitialization & qos_initialization)
: QoS(qos_initialization, rmw_qos_profile_parameters)
{}
ServicesQoS::ServicesQoS(const QoSInitialization & qos_initialization)
: QoS(qos_initialization, rmw_qos_profile_services_default)
{}
ParameterEventsQoS::ParameterEventsQoS(const QoSInitialization & qos_initialization)
: QoS(qos_initialization, rmw_qos_profile_parameter_events)
{}
SystemDefaultsQoS::SystemDefaultsQoS(const QoSInitialization & qos_initialization)
: QoS(qos_initialization, rmw_qos_profile_system_default)
{}
} // namespace rclcpp

View File

@@ -0,0 +1,55 @@
// Copyright 2019 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/qos_event.hpp"
namespace rclcpp
{
QOSEventHandlerBase::~QOSEventHandlerBase()
{
if (rcl_event_fini(&event_handle_) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Error in destruction of rcl event handle: %s", rcl_get_error_string().str);
rcl_reset_error();
}
}
/// Get the number of ready events.
size_t
QOSEventHandlerBase::get_number_of_ready_events()
{
return 1;
}
/// Add the Waitable to a wait set.
bool
QOSEventHandlerBase::add_to_wait_set(rcl_wait_set_t * wait_set)
{
rcl_ret_t ret = rcl_wait_set_add_event(wait_set, &event_handle_, &wait_set_event_index_);
if (RCL_RET_OK != ret) {
exceptions::throw_from_rcl_error(ret, "Couldn't add event to wait set");
}
return true;
}
/// Check if the Waitable is ready.
bool
QOSEventHandlerBase::is_ready(rcl_wait_set_t * wait_set)
{
return wait_set->events[wait_set_event_index_] == &event_handle_;
}
} // namespace rclcpp

View File

@@ -165,13 +165,15 @@ __safe_strerror(int errnum, char * buffer, size_t buffer_length)
{
#if defined(_WIN32)
strerror_s(buffer, buffer_length, errnum);
#elif (defined(_GNU_SOURCE) && !defined(ANDROID))
#elif defined(_GNU_SOURCE) && (!defined(ANDROID) || __ANDROID_API__ >= 23)
/* GNU-specific */
char * msg = strerror_r(errnum, buffer, buffer_length);
if (msg != buffer) {
strncpy(buffer, msg, buffer_length);
buffer[buffer_length - 1] = '\0';
}
#else
/* XSI-compliant */
int error_status = strerror_r(errnum, buffer, buffer_length);
if (error_status != 0) {
throw std::runtime_error("Failed to get error string for errno: " + std::to_string(errnum));

View File

@@ -12,11 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/subscription.hpp"
#include "rclcpp/subscription_base.hpp"
#include <cstdio>
#include <memory>
#include <string>
#include <vector>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
@@ -121,6 +122,12 @@ SubscriptionBase::get_intra_process_subscription_handle() const
return intra_process_subscription_handle_;
}
const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
SubscriptionBase::get_event_handlers() const
{
return event_handlers_;
}
const rosidl_message_type_support_t &
SubscriptionBase::get_message_type_support_handle() const
{
@@ -147,3 +154,34 @@ SubscriptionBase::get_publisher_count() const
}
return inter_process_publisher_count;
}
void SubscriptionBase::setup_intra_process(
uint64_t intra_process_subscription_id,
IntraProcessManagerWeakPtr weak_ipm,
const rcl_subscription_options_t & intra_process_options)
{
std::string intra_process_topic_name = std::string(get_topic_name()) + "/_intra";
rcl_ret_t ret = rcl_subscription_init(
intra_process_subscription_handle_.get(),
node_handle_.get(),
rclcpp::type_support::get_intra_process_message_msg_type_support(),
intra_process_topic_name.c_str(),
&intra_process_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
auto rcl_node_handle = node_handle_.get();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
intra_process_topic_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle));
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create intra process subscription");
}
intra_process_subscription_id_ = intra_process_subscription_id;
weak_ipm_ = weak_ipm;
use_intra_process_ = true;
}

View File

@@ -62,17 +62,13 @@ Time::Time(int64_t nanoseconds, rcl_clock_type_t clock_type)
rcl_time_.nanoseconds = nanoseconds;
}
Time::Time(const Time & rhs)
: rcl_time_(rhs.rcl_time_)
{
rcl_time_.nanoseconds = rhs.rcl_time_.nanoseconds;
}
Time::Time(const Time & rhs) = default;
Time::Time(
const builtin_interfaces::msg::Time & time_msg,
rcl_clock_type_t ros_time)
rcl_clock_type_t clock_type)
: rcl_time_(init_time_point(clock_type))
{
rcl_time_ = init_time_point(ros_time);
if (time_msg.sec < 0) {
throw std::runtime_error("cannot store a negative time point in rclcpp::Time");
}
@@ -94,31 +90,25 @@ Time::~Time()
Time::operator builtin_interfaces::msg::Time() const
{
builtin_interfaces::msg::Time msg_time;
msg_time.sec = static_cast<std::int32_t>(RCL_NS_TO_S(rcl_time_.nanoseconds));
msg_time.nanosec = static_cast<std::uint32_t>(rcl_time_.nanoseconds % (1000 * 1000 * 1000));
constexpr rcl_time_point_value_t kRemainder = RCL_S_TO_NS(1);
const auto result = std::div(rcl_time_.nanoseconds, kRemainder);
if (result.rem >= 0) {
msg_time.sec = static_cast<std::int32_t>(result.quot);
msg_time.nanosec = static_cast<std::uint32_t>(result.rem);
} else {
msg_time.sec = static_cast<std::int32_t>(result.quot - 1);
msg_time.nanosec = static_cast<std::uint32_t>(kRemainder + result.rem);
}
return msg_time;
}
Time &
Time::operator=(const Time & rhs)
{
rcl_time_ = rhs.rcl_time_;
return *this;
}
Time::operator=(const Time & rhs) = default;
Time &
Time::operator=(const builtin_interfaces::msg::Time & time_msg)
{
if (time_msg.sec < 0) {
throw std::runtime_error("cannot store a negative time point in rclcpp::Time");
}
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<int64_t>(time_msg.sec));
rcl_time_.nanoseconds += time_msg.nanosec;
*this = Time(time_msg);
return *this;
}

View File

@@ -25,10 +25,11 @@
#include "rclcpp/exceptions.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/parameter_client.hpp"
#include "rclcpp/parameter_events_filter.hpp"
#include "rclcpp/time.hpp"
#include "rclcpp/time_source.hpp"
namespace rclcpp
{
@@ -104,22 +105,16 @@ void TimeSource::attachNode(
}
// TODO(tfoote) use parameters interface not subscribe to events via topic ticketed #609
parameter_client_ = std::make_shared<rclcpp::AsyncParametersClient>(
node_base_,
parameter_subscription_ = rclcpp::AsyncParametersClient::on_parameter_event(
node_topics_,
node_graph_,
node_services_
);
parameter_subscription_ =
parameter_client_->on_parameter_event(std::bind(&TimeSource::on_parameter_event,
this, std::placeholders::_1));
std::bind(&TimeSource::on_parameter_event, this, std::placeholders::_1));
}
void TimeSource::detachNode()
{
this->ros_time_active_ = false;
clock_subscription_.reset();
parameter_client_.reset();
parameter_subscription_.reset();
node_base_.reset();
node_topics_.reset();
node_graph_.reset();
@@ -208,26 +203,13 @@ void TimeSource::create_clock_sub()
// 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);
clock_subscription_ = rclcpp::create_subscription<rosgraph_msgs::msg::Clock>(
node_topics_,
"/clock",
rclcpp::QoS(QoSInitialization::from_rmw(rmw_qos_profile_default)),
std::bind(&TimeSource::clock_cb, this, std::placeholders::_1)
);
}
void TimeSource::destroy_clock_sub()
@@ -238,13 +220,17 @@ void TimeSource::destroy_clock_sub()
void TimeSource::on_parameter_event(const rcl_interfaces::msg::ParameterEvent::SharedPtr event)
{
// Filter out events on 'use_sim_time' parameter instances in other nodes.
if (event->node != node_base_->get_fully_qualified_name()) {
return;
}
// Filter for only 'use_sim_time' being added or changed.
rclcpp::ParameterEventsFilter filter(event, {"use_sim_time"},
{rclcpp::ParameterEventsFilter::EventType::NEW,
rclcpp::ParameterEventsFilter::EventType::CHANGED});
for (auto & it : filter.get_events()) {
if (it.second->value.type != ParameterType::PARAMETER_BOOL) {
RCLCPP_ERROR(logger_, "use_sim_time parameter set to something besides a bool");
RCLCPP_ERROR(logger_, "use_sim_time parameter cannot be set to anything but a bool");
continue;
}
if (it.second->value.bool_value) {

View File

@@ -103,9 +103,9 @@ remove_ros_arguments(int argc, char const * const argv[])
throw exceptions::RCLError(base_exc, "");
}
std::vector<std::string> return_arguments(nonros_argc);
std::vector<std::string> return_arguments(static_cast<size_t>(nonros_argc));
for (int ii = 0; ii < nonros_argc; ++ii) {
for (size_t ii = 0; ii < static_cast<size_t>(nonros_argc); ++ii) {
return_arguments[ii] = std::string(nonros_argv[ii]);
}

View File

@@ -34,6 +34,12 @@ Waitable::get_number_of_ready_clients()
return 0u;
}
size_t
Waitable::get_number_of_ready_events()
{
return 0u;
}
size_t
Waitable::get_number_of_ready_services()
{

View File

@@ -86,7 +86,7 @@ TEST_F(TestMultiThreadedExecutor, timer_over_take) {
{
std::lock_guard<std::mutex> lock(last_mutex);
double diff = std::abs((now - last).nanoseconds()) / 1.0e9;
double diff = static_cast<double>(std::abs((now - last).nanoseconds())) / 1.0e9;
last = now;
if (diff < PERIOD - TOLERANCE || diff > PERIOD + TOLERANCE) {

View File

@@ -0,0 +1,64 @@
// Copyright 2019 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 NODE_INTERFACES__NODE_WRAPPER_HPP_
#define NODE_INTERFACES__NODE_WRAPPER_HPP_
#include <memory>
#include <string>
#include "rclcpp/rclcpp.hpp"
class NodeWrapper
{
public:
explicit NodeWrapper(const std::string & name)
: node(std::make_shared<rclcpp::Node>(name))
{}
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_base_interface() {return this->node->get_node_base_interface();}
rclcpp::node_interfaces::NodeClockInterface::SharedPtr
get_node_clock_interface() {return this->node->get_node_clock_interface();}
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr
get_node_graph_interface() {return this->node->get_node_graph_interface();}
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr
get_node_logging_interface() {return this->node->get_node_logging_interface();}
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr
get_node_timers_interface() {return this->node->get_node_timers_interface();}
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr
get_node_topics_interface() {return this->node->get_node_topics_interface();}
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr
get_node_services_interface() {return this->node->get_node_services_interface();}
rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr
get_node_waitables_interface() {return this->node->get_node_waitables_interface();}
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr
get_node_parameters_interface() {return this->node->get_node_parameters_interface();}
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr
get_node_time_source_interface() {return this->node->get_node_time_source_interface();}
private:
rclcpp::Node::SharedPtr node;
};
#endif // NODE_INTERFACES__NODE_WRAPPER_HPP_

View File

@@ -0,0 +1,28 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
int main(void)
{
auto node = std::make_shared<rclcpp::Node>("test_node");
std::shared_ptr<const rclcpp::Node> const_node_ptr = node;
// Should fail because a const node cannot have a non-const method called on it.
rclcpp::node_interfaces::NodeTopicsInterface * result =
rclcpp::node_interfaces::get_node_topics_interface(const_node_ptr);
(void)result;
}

View File

@@ -0,0 +1,30 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
#include "../node_wrapper.hpp"
int main(void)
{
auto node = std::make_shared<NodeWrapper>("test_wrapped_node");
std::shared_ptr<const NodeWrapper> const_node_ptr = node;
// Should fail because a const node cannot have a non-const method called on it.
rclcpp::node_interfaces::NodeTopicsInterface * result =
rclcpp::node_interfaces::get_node_topics_interface(const_node_ptr);
(void)result;
}

View File

@@ -0,0 +1,28 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
int main(void)
{
auto node = std::make_shared<rclcpp::Node>("test_node");
const rclcpp::Node & const_node_reference = *node;
// Should fail because a const node cannot have a non-const method called on it.
rclcpp::node_interfaces::NodeTopicsInterface * result =
rclcpp::node_interfaces::get_node_topics_interface(const_node_reference);
(void)result;
}

View File

@@ -0,0 +1,30 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
#include "../node_wrapper.hpp"
int main(void)
{
auto node = std::make_shared<NodeWrapper>("test_wrapped_node");
const NodeWrapper & const_node_reference = *node;
// Should fail because a const node cannot have a non-const method called on it.
rclcpp::node_interfaces::NodeTopicsInterface * result =
rclcpp::node_interfaces::get_node_topics_interface(const_node_reference);
(void)result;
}

View File

@@ -0,0 +1,117 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <string>
#include "gtest/gtest.h"
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
#include "rclcpp/rclcpp.hpp"
#include "./node_wrapper.hpp"
static const std::string node_suffix = "test_get_node_interfaces"; // NOLINT
class TestGetNodeInterfaces : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
node = std::make_shared<rclcpp::Node>(node_suffix);
wrapped_node = std::make_shared<NodeWrapper>("wrapped_" + node_suffix);
}
static void TearDownTestCase()
{
node.reset();
wrapped_node.reset();
}
static rclcpp::Node::SharedPtr node;
static std::shared_ptr<NodeWrapper> wrapped_node;
};
rclcpp::Node::SharedPtr TestGetNodeInterfaces::node = nullptr;
std::shared_ptr<NodeWrapper> TestGetNodeInterfaces::wrapped_node = nullptr;
TEST_F(TestGetNodeInterfaces, rclcpp_node_shared_ptr) {
auto result = rclcpp::node_interfaces::get_node_topics_interface(this->node);
static_assert(
std::is_same<
rclcpp::node_interfaces::NodeTopicsInterface *,
decltype(result)
>::value, "expected rclcpp::node_interfaces::NodeTopicsInterface *");
}
TEST_F(TestGetNodeInterfaces, node_shared_ptr) {
auto result = rclcpp::node_interfaces::get_node_topics_interface(this->wrapped_node);
static_assert(
std::is_same<
rclcpp::node_interfaces::NodeTopicsInterface *,
decltype(result)
>::value, "expected rclcpp::node_interfaces::NodeTopicsInterface *");
}
TEST_F(TestGetNodeInterfaces, rclcpp_node_reference) {
rclcpp::Node & node_reference = *this->node;
auto result = rclcpp::node_interfaces::get_node_topics_interface(node_reference);
static_assert(
std::is_same<
rclcpp::node_interfaces::NodeTopicsInterface *,
decltype(result)
>::value, "expected rclcpp::node_interfaces::NodeTopicsInterface *");
}
TEST_F(TestGetNodeInterfaces, node_reference) {
NodeWrapper & wrapped_node_reference = *this->wrapped_node;
auto result = rclcpp::node_interfaces::get_node_topics_interface(wrapped_node_reference);
static_assert(
std::is_same<
rclcpp::node_interfaces::NodeTopicsInterface *,
decltype(result)
>::value, "expected rclcpp::node_interfaces::NodeTopicsInterface *");
}
TEST_F(TestGetNodeInterfaces, rclcpp_node_pointer) {
rclcpp::Node * node_pointer = this->node.get();
auto result = rclcpp::node_interfaces::get_node_topics_interface(node_pointer);
static_assert(
std::is_same<
rclcpp::node_interfaces::NodeTopicsInterface *,
decltype(result)
>::value, "expected rclcpp::node_interfaces::NodeTopicsInterface *");
}
TEST_F(TestGetNodeInterfaces, node_pointer) {
NodeWrapper * wrapped_node_pointer = this->wrapped_node.get();
auto result = rclcpp::node_interfaces::get_node_topics_interface(wrapped_node_pointer);
static_assert(
std::is_same<
rclcpp::node_interfaces::NodeTopicsInterface *,
decltype(result)
>::value, "expected rclcpp::node_interfaces::NodeTopicsInterface *");
}
TEST_F(TestGetNodeInterfaces, interface_shared_pointer) {
std::shared_ptr<rclcpp::node_interfaces::NodeTopicsInterface> interface_shared_ptr =
this->node->get_node_topics_interface();
auto result = rclcpp::node_interfaces::get_node_topics_interface(interface_shared_ptr);
static_assert(
std::is_same<
rclcpp::node_interfaces::NodeTopicsInterface *,
decltype(result)
>::value, "expected rclcpp::node_interfaces::NodeTopicsInterface *");
}

View File

@@ -0,0 +1,57 @@
// Copyright 2019 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 <vector>
#include "rcl/allocator.h"
#include "rcl/arguments.h"
#include "rcl/remap.h"
#include "rclcpp/node_options.hpp"
TEST(TestNodeOptions, use_global_arguments) {
{
auto options = rclcpp::NodeOptions();
EXPECT_TRUE(options.use_global_arguments());
EXPECT_TRUE(options.get_rcl_node_options()->use_global_arguments);
}
{
auto options = rclcpp::NodeOptions().use_global_arguments(false);
EXPECT_FALSE(options.use_global_arguments());
EXPECT_FALSE(options.get_rcl_node_options()->use_global_arguments);
}
{
auto options = rclcpp::NodeOptions().use_global_arguments(true);
EXPECT_TRUE(options.use_global_arguments());
EXPECT_TRUE(options.get_rcl_node_options()->use_global_arguments);
}
{
auto options = rclcpp::NodeOptions();
EXPECT_TRUE(options.use_global_arguments());
EXPECT_TRUE(options.get_rcl_node_options()->use_global_arguments);
options.use_global_arguments(false);
EXPECT_FALSE(options.use_global_arguments());
EXPECT_FALSE(options.get_rcl_node_options()->use_global_arguments);
options.use_global_arguments(true);
EXPECT_TRUE(options.use_global_arguments());
EXPECT_TRUE(options.get_rcl_node_options()->use_global_arguments);
}
}

View File

@@ -0,0 +1,63 @@
// Copyright 2019 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 <atomic>
#include <chrono>
#include <memory>
#include "rclcpp/create_timer.hpp"
#include "rclcpp/executors.hpp"
#include "rclcpp/node.hpp"
#include "node_interfaces/node_wrapper.hpp"
using namespace std::chrono_literals;
TEST(TestCreateTimer, timer_executes)
{
rclcpp::init(0, nullptr);
auto node = std::make_shared<rclcpp::Node>("test_create_timer_node");
std::atomic<bool> got_callback{false};
rclcpp::TimerBase::SharedPtr timer;
timer = rclcpp::create_timer(
node,
node->get_clock(),
rclcpp::Duration(0ms),
[&got_callback, &timer]() {
got_callback = true;
timer->cancel();
});
rclcpp::spin_some(node);
ASSERT_TRUE(got_callback);
rclcpp::shutdown();
}
TEST(TestCreateTimer, call_with_node_wrapper_compiles)
{
rclcpp::init(0, nullptr);
NodeWrapper node("test_create_timer_call_with_node_wrapper_compiles");
rclcpp::TimerBase::SharedPtr timer;
timer = rclcpp::create_timer(
node,
node.get_node_clock_interface()->get_clock(),
rclcpp::Duration(0ms),
[]() {});
rclcpp::shutdown();
}

View File

@@ -32,11 +32,7 @@ class TestDuration : public ::testing::Test
{
};
// TEST(TestDuration, conversions) {
// TODO(tfoote) Implement conversion methods
// }
TEST(TestDuration, operators) {
TEST_F(TestDuration, operators) {
rclcpp::Duration old(1, 0);
rclcpp::Duration young(2, 0);
@@ -45,28 +41,30 @@ TEST(TestDuration, operators) {
EXPECT_TRUE(old <= young);
EXPECT_TRUE(young >= old);
EXPECT_FALSE(young == old);
EXPECT_TRUE(young != old);
rclcpp::Duration add = old + young;
EXPECT_EQ(add.nanoseconds(), (rcl_duration_value_t)(old.nanoseconds() + young.nanoseconds()));
EXPECT_EQ(add.nanoseconds(), old.nanoseconds() + young.nanoseconds());
EXPECT_EQ(add, old + young);
rclcpp::Duration sub = young - old;
EXPECT_EQ(sub.nanoseconds(), (rcl_duration_value_t)(young.nanoseconds() - old.nanoseconds()));
EXPECT_EQ(sub.nanoseconds(), 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));
EXPECT_EQ(scale.nanoseconds(), 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);
(void)assignment_op_duration;
assignment_op_duration = time;
EXPECT_TRUE(time == copy_constructor_duration);
EXPECT_TRUE(time == assignment_op_duration);
}
TEST(TestDuration, chrono_overloads) {
TEST_F(TestDuration, chrono_overloads) {
int64_t ns = 123456789l;
auto chrono_ns = std::chrono::nanoseconds(ns);
auto d1 = rclcpp::Duration(ns);
@@ -75,9 +73,17 @@ TEST(TestDuration, chrono_overloads) {
EXPECT_EQ(d1, d2);
EXPECT_EQ(d1, d3);
EXPECT_EQ(d2, d3);
// check non-nanosecond durations
std::chrono::milliseconds chrono_ms(100);
auto d4 = rclcpp::Duration(chrono_ms);
EXPECT_EQ(chrono_ms, d4.to_chrono<std::chrono::nanoseconds>());
std::chrono::duration<double, std::chrono::seconds::period> chrono_float_seconds(3.14);
auto d5 = rclcpp::Duration(chrono_float_seconds);
EXPECT_EQ(chrono_float_seconds, d5.to_chrono<decltype(chrono_float_seconds)>());
}
TEST(TestDuration, overflows) {
TEST_F(TestDuration, overflows) {
rclcpp::Duration max(std::numeric_limits<rcl_duration_value_t>::max());
rclcpp::Duration min(std::numeric_limits<rcl_duration_value_t>::min());
@@ -98,7 +104,7 @@ TEST(TestDuration, overflows) {
EXPECT_THROW(base_d_neg * 4, std::underflow_error);
}
TEST(TestDuration, negative_duration) {
TEST_F(TestDuration, negative_duration) {
rclcpp::Duration assignable_duration = rclcpp::Duration(0) - rclcpp::Duration(5, 0);
{
@@ -121,9 +127,106 @@ TEST(TestDuration, negative_duration) {
}
}
TEST(TestDuration, maximum_duration) {
TEST_F(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);
}
static const int64_t HALF_SEC_IN_NS = 500 * 1000 * 1000;
static const int64_t ONE_SEC_IN_NS = 1000 * 1000 * 1000;
static const int64_t ONE_AND_HALF_SEC_IN_NS = 3 * HALF_SEC_IN_NS;
TEST_F(TestDuration, std_chrono_constructors) {
EXPECT_EQ(rclcpp::Duration(0), rclcpp::Duration(0.0s));
EXPECT_EQ(rclcpp::Duration(0), rclcpp::Duration(0s));
EXPECT_EQ(rclcpp::Duration(1, HALF_SEC_IN_NS), rclcpp::Duration(1.5s));
EXPECT_EQ(rclcpp::Duration(-1, 0), rclcpp::Duration(-1s));
}
TEST_F(TestDuration, conversions) {
{
const rclcpp::Duration duration(HALF_SEC_IN_NS);
const auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
EXPECT_EQ(duration_msg.sec, 0);
EXPECT_EQ(duration_msg.nanosec, HALF_SEC_IN_NS);
EXPECT_EQ(rclcpp::Duration(duration_msg).nanoseconds(), HALF_SEC_IN_NS);
const auto rmw_time = duration.to_rmw_time();
EXPECT_EQ(rmw_time.sec, 0u);
EXPECT_EQ(rmw_time.nsec, static_cast<uint64_t>(HALF_SEC_IN_NS));
const auto chrono_duration = duration.to_chrono<std::chrono::nanoseconds>();
EXPECT_EQ(chrono_duration.count(), HALF_SEC_IN_NS);
}
{
const rclcpp::Duration duration(ONE_SEC_IN_NS);
const auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
EXPECT_EQ(duration_msg.sec, 1);
EXPECT_EQ(duration_msg.nanosec, 0u);
EXPECT_EQ(rclcpp::Duration(duration_msg).nanoseconds(), ONE_SEC_IN_NS);
const auto rmw_time = duration.to_rmw_time();
EXPECT_EQ(rmw_time.sec, 1u);
EXPECT_EQ(rmw_time.nsec, 0u);
const auto chrono_duration = duration.to_chrono<std::chrono::nanoseconds>();
EXPECT_EQ(chrono_duration.count(), ONE_SEC_IN_NS);
}
{
const rclcpp::Duration duration(ONE_AND_HALF_SEC_IN_NS);
auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
EXPECT_EQ(duration_msg.sec, 1);
EXPECT_EQ(duration_msg.nanosec, HALF_SEC_IN_NS);
EXPECT_EQ(rclcpp::Duration(duration_msg).nanoseconds(), ONE_AND_HALF_SEC_IN_NS);
auto rmw_time = duration.to_rmw_time();
EXPECT_EQ(rmw_time.sec, 1u);
EXPECT_EQ(rmw_time.nsec, static_cast<uint64_t>(HALF_SEC_IN_NS));
auto chrono_duration = duration.to_chrono<std::chrono::nanoseconds>();
EXPECT_EQ(chrono_duration.count(), ONE_AND_HALF_SEC_IN_NS);
}
{
rclcpp::Duration duration(-HALF_SEC_IN_NS);
auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
EXPECT_EQ(duration_msg.sec, -1);
EXPECT_EQ(duration_msg.nanosec, HALF_SEC_IN_NS);
EXPECT_EQ(rclcpp::Duration(duration_msg).nanoseconds(), -HALF_SEC_IN_NS);
EXPECT_THROW(duration.to_rmw_time(), std::runtime_error);
auto chrono_duration = duration.to_chrono<std::chrono::nanoseconds>();
EXPECT_EQ(chrono_duration.count(), -HALF_SEC_IN_NS);
}
{
rclcpp::Duration duration(-ONE_SEC_IN_NS);
auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
EXPECT_EQ(duration_msg.sec, -1);
EXPECT_EQ(duration_msg.nanosec, 0u);
EXPECT_EQ(rclcpp::Duration(duration_msg).nanoseconds(), -ONE_SEC_IN_NS);
EXPECT_THROW(duration.to_rmw_time(), std::runtime_error);
auto chrono_duration = duration.to_chrono<std::chrono::nanoseconds>();
EXPECT_EQ(chrono_duration.count(), -ONE_SEC_IN_NS);
}
{
rclcpp::Duration duration(-ONE_AND_HALF_SEC_IN_NS);
auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
EXPECT_EQ(duration_msg.sec, -2);
EXPECT_EQ(duration_msg.nanosec, HALF_SEC_IN_NS);
EXPECT_EQ(rclcpp::Duration(duration_msg).nanoseconds(), -ONE_AND_HALF_SEC_IN_NS);
EXPECT_THROW(duration.to_rmw_time(), std::runtime_error);
auto chrono_duration = duration.to_chrono<std::chrono::nanoseconds>();
EXPECT_EQ(chrono_duration.count(), -ONE_AND_HALF_SEC_IN_NS);
}
}

View File

@@ -60,3 +60,10 @@ TEST_F(TestExecutors, detachOnDestruction) {
EXPECT_NO_THROW(executor.add_node(node));
}
}
// Make sure that the executor can automatically remove expired nodes correctly
TEST_F(TestExecutors, addTemporaryNode) {
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(std::make_shared<rclcpp::Node>("temporary_node"));
EXPECT_NO_THROW(executor.spin_some());
}

View File

@@ -36,15 +36,15 @@ TEST_F(TestFindWeakNodes, allocator_strategy_with_weak_nodes) {
rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy<>>();
auto existing_node = rclcpp::Node::make_shared("existing_node");
auto dead_node = rclcpp::Node::make_shared("dead_node");
rclcpp::memory_strategy::MemoryStrategy::WeakNodeVector weak_nodes;
rclcpp::memory_strategy::MemoryStrategy::WeakNodeList weak_nodes;
weak_nodes.push_back(existing_node->get_node_base_interface());
weak_nodes.push_back(dead_node->get_node_base_interface());
// AND
// Delete dead_node, creating a dangling pointer in weak_nodes
dead_node.reset();
ASSERT_FALSE(weak_nodes[0].expired());
ASSERT_TRUE(weak_nodes[1].expired());
ASSERT_FALSE(weak_nodes.front().expired());
ASSERT_TRUE(weak_nodes.back().expired());
// WHEN
bool has_invalid_weak_nodes = memory_strategy->collect_entities(weak_nodes);
@@ -64,11 +64,11 @@ TEST_F(TestFindWeakNodes, allocator_strategy_no_weak_nodes) {
rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy<>>();
auto existing_node1 = rclcpp::Node::make_shared("existing_node1");
auto existing_node2 = rclcpp::Node::make_shared("existing_node2");
rclcpp::memory_strategy::MemoryStrategy::WeakNodeVector weak_nodes;
rclcpp::memory_strategy::MemoryStrategy::WeakNodeList weak_nodes;
weak_nodes.push_back(existing_node1->get_node_base_interface());
weak_nodes.push_back(existing_node2->get_node_base_interface());
ASSERT_FALSE(weak_nodes[0].expired());
ASSERT_FALSE(weak_nodes[1].expired());
ASSERT_FALSE(weak_nodes.front().expired());
ASSERT_FALSE(weak_nodes.back().expired());
// WHEN
bool has_invalid_weak_nodes = memory_strategy->collect_entities(weak_nodes);

View File

@@ -80,6 +80,12 @@ struct ObjectMember
return 7;
}
int callback_one_bool_const(bool a) const
{
(void)a;
return 7;
}
int callback_two_bools(bool a, bool b)
{
(void)a;
@@ -394,6 +400,16 @@ TEST(TestFunctionTraits, argument_types) {
rclcpp::function_traits::function_traits<decltype(bind_one_bool)>::template argument_type<0>
>::value, "Functor accepts a bool as first argument");
auto bind_one_bool_const = std::bind(
&ObjectMember::callback_one_bool_const, &object_member, std::placeholders::_1);
static_assert(
std::is_same<
bool,
rclcpp::function_traits::function_traits<decltype(bind_one_bool_const)>::template
argument_type<0>
>::value, "Functor accepts a bool as first argument");
auto bind_two_bools = std::bind(
&ObjectMember::callback_two_bools, &object_member, std::placeholders::_1,
std::placeholders::_2);
@@ -561,6 +577,14 @@ TEST(TestFunctionTraits, check_arguments) {
static_assert(
rclcpp::function_traits::check_arguments<decltype(bind_one_bool), bool>::value,
"Functor accepts a single bool as arguments");
auto bind_one_bool_const = std::bind(
&ObjectMember::callback_one_bool_const, &object_member, std::placeholders::_1);
// Test std::bind functions
static_assert(
rclcpp::function_traits::check_arguments<decltype(bind_one_bool_const), bool>::value,
"Functor accepts a single bool as arguments");
}
/*

View File

@@ -14,10 +14,13 @@
#include <memory>
#include <string>
#include <utility>
#define RCLCPP_BUILDING_LIBRARY 1
#include "gtest/gtest.h"
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/mapped_ring_buffer.hpp"
#include "rmw/types.h"
// Mock up publisher and subscription base to avoid needing an rmw impl.
@@ -34,6 +37,9 @@ public:
PublisherBase()
: mock_topic_name(""), mock_queue_size(0) {}
virtual ~PublisherBase()
{}
const char * get_topic_name() const
{
return mock_topic_name.c_str();
@@ -50,6 +56,14 @@ public:
return false;
}
virtual
mapped_ring_buffer::MappedRingBufferBase::SharedPtr
make_mapped_ring_buffer(size_t size) const
{
(void)size;
return nullptr;
}
std::string mock_topic_name;
size_t mock_queue_size;
};
@@ -71,6 +85,15 @@ public:
allocator_ = std::make_shared<MessageAlloc>();
}
mapped_ring_buffer::MappedRingBufferBase::SharedPtr
make_mapped_ring_buffer(size_t size) const override
{
return mapped_ring_buffer::MappedRingBuffer<
T,
typename Publisher<T, Alloc>::MessageAlloc
>::make_shared(size, allocator_);
}
std::shared_ptr<MessageAlloc> get_allocator()
{
return allocator_;
@@ -109,10 +132,9 @@ public:
} // namespace mock
} // namespace rclcpp
// Prevent rclcpp/publisher.hpp and rclcpp/subscription.hpp from being imported.
#define RCLCPP__PUBLISHER_HPP_
#define RCLCPP__SUBSCRIPTION_HPP_
#define RCLCPP_BUILDING_LIBRARY 1
// Prevent rclcpp/publisher_base.hpp and rclcpp/subscription.hpp from being imported.
#define RCLCPP__PUBLISHER_BASE_HPP_
#define RCLCPP__SUBSCRIPTION_BASE_HPP_
// Force ipm to use our mock publisher class.
#define Publisher mock::Publisher
#define PublisherBase mock::PublisherBase
@@ -155,10 +177,8 @@ TEST(TestIntraProcessManager, nominal) {
s1->mock_topic_name = "nominal1";
s1->mock_queue_size = 10;
auto p1_id =
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
auto p2_id =
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p2);
auto p1_id = ipm.add_publisher(p1);
auto p2_id = ipm.add_publisher(p2);
auto s1_id = ipm.add_subscription(s1);
auto ipm_msg = std::make_shared<rcl_interfaces::msg::IntraProcessMessage>();
@@ -169,14 +189,14 @@ TEST(TestIntraProcessManager, nominal) {
);
auto p1_m1_original_address = unique_msg.get();
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
ipm_msg->message_sequence = 43;
ipm_msg->publisher_id = 43;
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
auto p2_m1_id = ipm.store_intra_process_message(p2_id, unique_msg);
auto p2_m1_id = ipm.store_intra_process_message(p2_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
ipm.take_intra_process_message(p2_id, p2_m1_id, s1_id, unique_msg);
@@ -198,26 +218,22 @@ TEST(TestIntraProcessManager, nominal) {
ipm_msg->publisher_id = 44;
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
ipm.store_intra_process_message(p1_id, unique_msg);
ipm.store_intra_process_message(p1_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
ipm_msg->message_sequence = 45;
ipm_msg->publisher_id = 45;
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
ipm.store_intra_process_message(p1_id, unique_msg);
ipm.store_intra_process_message(p1_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
ipm_msg->message_sequence = 46;
ipm_msg->publisher_id = 46;
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
ipm.store_intra_process_message(p1_id, unique_msg);
ASSERT_NE(nullptr, unique_msg);
if (unique_msg) {
EXPECT_EQ(44ul, unique_msg->message_sequence);
EXPECT_EQ(44ul, unique_msg->publisher_id);
}
ipm.store_intra_process_message(p1_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
}
/*
@@ -240,8 +256,7 @@ TEST(TestIntraProcessManager, remove_publisher_before_trying_to_take) {
s1->mock_topic_name = "nominal1";
s1->mock_queue_size = 10;
auto p1_id =
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
auto p1_id = ipm.add_publisher(p1);
auto s1_id = ipm.add_subscription(s1);
auto ipm_msg = std::make_shared<rcl_interfaces::msg::IntraProcessMessage>();
@@ -251,7 +266,7 @@ TEST(TestIntraProcessManager, remove_publisher_before_trying_to_take) {
new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg)
);
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
ipm.remove_publisher(p1_id);
@@ -290,8 +305,7 @@ TEST(TestIntraProcessManager, removed_subscription_affects_take) {
s3->mock_topic_name = "nominal1";
s3->mock_queue_size = 10;
auto p1_id =
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
auto p1_id = ipm.add_publisher(p1);
auto s1_id = ipm.add_subscription(s1);
auto s2_id = ipm.add_subscription(s2);
auto s3_id = ipm.add_subscription(s3);
@@ -304,7 +318,7 @@ TEST(TestIntraProcessManager, removed_subscription_affects_take) {
);
auto original_message_pointer = unique_msg.get();
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
ipm.take_intra_process_message(p1_id, p1_m1_id, s1_id, unique_msg);
@@ -361,8 +375,7 @@ TEST(TestIntraProcessManager, multiple_subscriptions_one_publisher) {
s3->mock_topic_name = "nominal1";
s3->mock_queue_size = 10;
auto p1_id =
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
auto p1_id = ipm.add_publisher(p1);
auto s1_id = ipm.add_subscription(s1);
auto s2_id = ipm.add_subscription(s2);
auto s3_id = ipm.add_subscription(s3);
@@ -375,7 +388,7 @@ TEST(TestIntraProcessManager, multiple_subscriptions_one_publisher) {
);
auto original_message_pointer = unique_msg.get();
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
ipm.take_intra_process_message(p1_id, p1_m1_id, s1_id, unique_msg);
@@ -437,12 +450,9 @@ TEST(TestIntraProcessManager, multiple_publishers_one_subscription) {
s1->mock_topic_name = "nominal1";
s1->mock_queue_size = 10;
auto p1_id =
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
auto p2_id =
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p2);
auto p3_id =
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p3);
auto p1_id = ipm.add_publisher(p1);
auto p2_id = ipm.add_publisher(p2);
auto p3_id = ipm.add_publisher(p3);
auto s1_id = ipm.add_subscription(s1);
auto ipm_msg = std::make_shared<rcl_interfaces::msg::IntraProcessMessage>();
@@ -454,7 +464,7 @@ TEST(TestIntraProcessManager, multiple_publishers_one_subscription) {
);
auto original_message_pointer1 = unique_msg.get();
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
// Second publish
@@ -463,7 +473,7 @@ TEST(TestIntraProcessManager, multiple_publishers_one_subscription) {
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
auto original_message_pointer2 = unique_msg.get();
auto p2_m1_id = ipm.store_intra_process_message(p2_id, unique_msg);
auto p2_m1_id = ipm.store_intra_process_message(p2_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
// Third publish
@@ -472,7 +482,7 @@ TEST(TestIntraProcessManager, multiple_publishers_one_subscription) {
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
auto original_message_pointer3 = unique_msg.get();
auto p3_m1_id = ipm.store_intra_process_message(p3_id, unique_msg);
auto p3_m1_id = ipm.store_intra_process_message(p3_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
// First take
@@ -545,12 +555,9 @@ TEST(TestIntraProcessManager, multiple_publishers_multiple_subscription) {
s3->mock_topic_name = "nominal1";
s3->mock_queue_size = 10;
auto p1_id =
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
auto p2_id =
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p2);
auto p3_id =
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p3);
auto p1_id = ipm.add_publisher(p1);
auto p2_id = ipm.add_publisher(p2);
auto p3_id = ipm.add_publisher(p3);
auto s1_id = ipm.add_subscription(s1);
auto s2_id = ipm.add_subscription(s2);
auto s3_id = ipm.add_subscription(s3);
@@ -564,7 +571,7 @@ TEST(TestIntraProcessManager, multiple_publishers_multiple_subscription) {
);
auto original_message_pointer1 = unique_msg.get();
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
// Second publish
@@ -573,7 +580,7 @@ TEST(TestIntraProcessManager, multiple_publishers_multiple_subscription) {
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
auto original_message_pointer2 = unique_msg.get();
auto p2_m1_id = ipm.store_intra_process_message(p2_id, unique_msg);
auto p2_m1_id = ipm.store_intra_process_message(p2_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
// Third publish
@@ -582,7 +589,7 @@ TEST(TestIntraProcessManager, multiple_publishers_multiple_subscription) {
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
auto original_message_pointer3 = unique_msg.get();
auto p3_m1_id = ipm.store_intra_process_message(p3_id, unique_msg);
auto p3_m1_id = ipm.store_intra_process_message(p3_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
// First take
@@ -692,8 +699,7 @@ TEST(TestIntraProcessManager, ring_buffer_displacement) {
s1->mock_topic_name = "nominal1";
s1->mock_queue_size = 10;
auto p1_id =
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
auto p1_id = ipm.add_publisher(p1);
auto s1_id = ipm.add_subscription(s1);
auto ipm_msg = std::make_shared<rcl_interfaces::msg::IntraProcessMessage>();
@@ -703,8 +709,7 @@ TEST(TestIntraProcessManager, ring_buffer_displacement) {
new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg)
);
auto original_message_pointer1 = unique_msg.get();
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
ipm_msg->message_sequence = 43;
@@ -712,7 +717,7 @@ TEST(TestIntraProcessManager, ring_buffer_displacement) {
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
auto original_message_pointer2 = unique_msg.get();
auto p1_m2_id = ipm.store_intra_process_message(p1_id, unique_msg);
auto p1_m2_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
ipm.take_intra_process_message(p1_id, p1_m2_id, s1_id, unique_msg);
@@ -728,14 +733,8 @@ TEST(TestIntraProcessManager, ring_buffer_displacement) {
ipm_msg->publisher_id = 44;
unique_msg.reset(new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg));
ipm.store_intra_process_message(p1_id, unique_msg);
EXPECT_NE(nullptr, unique_msg); // Should return the thing in the ring buffer it displaced.
if (unique_msg) {
// This should have been the first published message.
EXPECT_EQ(42ul, unique_msg->message_sequence);
EXPECT_EQ(42ul, unique_msg->publisher_id);
EXPECT_EQ(original_message_pointer1, unique_msg.get());
}
ipm.store_intra_process_message(p1_id, std::move(unique_msg));
EXPECT_EQ(nullptr, unique_msg);
unique_msg.reset();
// Since it just got displaced it should no longer be there to take.
@@ -759,8 +758,7 @@ TEST(TestIntraProcessManager, subscription_creation_race_condition) {
p1->mock_topic_name = "nominal1";
p1->mock_queue_size = 2;
auto p1_id =
ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
auto p1_id = ipm.add_publisher(p1);
auto ipm_msg = std::make_shared<rcl_interfaces::msg::IntraProcessMessage>();
ipm_msg->message_sequence = 42;
@@ -769,7 +767,7 @@ TEST(TestIntraProcessManager, subscription_creation_race_condition) {
new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg)
);
auto p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
auto p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
auto s1 = std::make_shared<rclcpp::mock::SubscriptionBase>();
@@ -808,7 +806,7 @@ TEST(TestIntraProcessManager, publisher_out_of_scope_take) {
p1->mock_topic_name = "nominal1";
p1->mock_queue_size = 2;
p1_id = ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
p1_id = ipm.add_publisher(p1);
auto ipm_msg = std::make_shared<rcl_interfaces::msg::IntraProcessMessage>();
ipm_msg->message_sequence = 42;
@@ -817,7 +815,7 @@ TEST(TestIntraProcessManager, publisher_out_of_scope_take) {
new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg)
);
p1_m1_id = ipm.store_intra_process_message(p1_id, unique_msg);
p1_m1_id = ipm.store_intra_process_message(p1_id, std::move(unique_msg));
ASSERT_EQ(nullptr, unique_msg);
// Explicitly remove publisher from ipm (emulate's publisher's destructor).
@@ -847,7 +845,7 @@ TEST(TestIntraProcessManager, publisher_out_of_scope_store) {
p1->mock_topic_name = "nominal1";
p1->mock_queue_size = 2;
p1_id = ipm.add_publisher<rcl_interfaces::msg::IntraProcessMessage, std::allocator<void>>(p1);
p1_id = ipm.add_publisher(p1);
}
auto ipm_msg = std::make_shared<rcl_interfaces::msg::IntraProcessMessage>();
@@ -857,6 +855,6 @@ TEST(TestIntraProcessManager, publisher_out_of_scope_store) {
new rcl_interfaces::msg::IntraProcessMessage(*ipm_msg)
);
EXPECT_THROW(ipm.store_intra_process_message(p1_id, unique_msg), std::runtime_error);
EXPECT_THROW(ipm.store_intra_process_message(p1_id, std::move(unique_msg)), std::runtime_error);
ASSERT_EQ(nullptr, unique_msg);
}

View File

@@ -12,12 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <memory>
#include <utility>
#include "gtest/gtest.h"
#define RCLCPP_BUILDING_LIBRARY 1 // Prevent including unavailable symbols
#include <rclcpp/mapped_ring_buffer.hpp>
#include <memory>
#include "rclcpp/mapped_ring_buffer.hpp"
/*
Tests get_copy and pop on an empty mrb.
@@ -28,60 +29,90 @@ TEST(TestMappedRingBuffer, empty) {
// Getting or popping an empty buffer should result in a nullptr.
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(1);
std::unique_ptr<char> actual;
mrb.get_copy_at_key(1, actual);
EXPECT_EQ(nullptr, actual);
std::unique_ptr<char> unique;
mrb.get(1, unique);
EXPECT_EQ(nullptr, unique);
mrb.pop_at_key(1, actual);
mrb.pop(1, unique);
EXPECT_EQ(nullptr, unique);
std::shared_ptr<const char> shared;
mrb.get(1, shared);
EXPECT_EQ(nullptr, shared);
mrb.pop(1, shared);
EXPECT_EQ(nullptr, shared);
}
/*
Tests push_and_replace with a temporary object, and using
get and pop methods with shared_ptr signature.
*/
TEST(TestMappedRingBuffer, temporary_l_value_with_shared_get_pop) {
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(2);
// Pass in value with temporary object
mrb.push_and_replace(1, std::shared_ptr<const char>(new char('a')));
std::shared_ptr<const char> actual;
mrb.get(1, actual);
EXPECT_EQ('a', *actual);
mrb.pop(1, actual);
EXPECT_EQ('a', *actual);
mrb.get(1, actual);
EXPECT_EQ(nullptr, actual);
}
/*
Tests push_and_replace with a temporary object.
Tests push_and_replace with a temporary object, and using
get and pop methods with unique_ptr signature.
*/
TEST(TestMappedRingBuffer, temporary_l_value) {
TEST(TestMappedRingBuffer, temporary_l_value_with_unique_get_pop) {
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(2);
// Pass in value with temporary object
mrb.push_and_replace(1, std::unique_ptr<char>(new char('a')));
mrb.push_and_replace(1, std::shared_ptr<const char>(new char('a')));
std::unique_ptr<char> actual;
mrb.get_copy_at_key(1, actual);
mrb.get(1, actual);
EXPECT_EQ('a', *actual);
mrb.pop_at_key(1, actual);
mrb.pop(1, actual);
EXPECT_EQ('a', *actual);
mrb.get_copy_at_key(1, actual);
mrb.get(1, actual);
EXPECT_EQ(nullptr, actual);
}
/*
Tests normal usage of the mrb.
Using shared push_and_replace, get and pop methods.
*/
TEST(TestMappedRingBuffer, nominal) {
TEST(TestMappedRingBuffer, nominal_push_shared_get_pop_shared) {
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(2);
std::unique_ptr<char> expected(new char('a'));
// Store expected value's address for later comparison.
char * expected_orig = expected.get();
std::shared_ptr<const char> expected(new char('a'));
EXPECT_FALSE(mrb.push_and_replace(1, expected));
EXPECT_EQ(2, expected.use_count());
std::unique_ptr<char> actual;
mrb.get_copy_at_key(1, actual);
std::shared_ptr<const char> actual;
mrb.get(1, actual);
EXPECT_NE(nullptr, actual);
if (actual) {
EXPECT_EQ('a', *actual);
}
EXPECT_NE(expected_orig, actual.get());
EXPECT_EQ(expected, actual);
EXPECT_EQ(3, actual.use_count());
mrb.pop_at_key(1, actual);
EXPECT_NE(nullptr, actual);
mrb.pop(1, actual);
EXPECT_EQ(expected, actual);
if (actual) {
EXPECT_EQ('a', *actual);
}
EXPECT_EQ(expected_orig, actual.get());
expected.reset();
EXPECT_TRUE(actual.unique());
mrb.get_copy_at_key(1, actual);
mrb.get(1, actual);
EXPECT_EQ(nullptr, actual);
expected.reset(new char('a'));
@@ -93,16 +124,16 @@ TEST(TestMappedRingBuffer, nominal) {
expected.reset(new char('c'));
EXPECT_TRUE(mrb.push_and_replace(3, expected));
mrb.get_copy_at_key(1, actual);
mrb.get(1, actual);
EXPECT_EQ(nullptr, actual);
mrb.get_copy_at_key(2, actual);
mrb.get(2, actual);
EXPECT_NE(nullptr, actual);
if (actual) {
EXPECT_EQ('b', *actual);
}
mrb.get_copy_at_key(3, actual);
mrb.get(3, actual);
EXPECT_NE(nullptr, actual);
if (actual) {
EXPECT_EQ('c', *actual);
@@ -110,40 +141,167 @@ TEST(TestMappedRingBuffer, nominal) {
}
/*
Tests get_ownership on a normal mrb.
Tests normal usage of the mrb.
Using shared push_and_replace, unique get and pop methods.
*/
TEST(TestMappedRingBuffer, get_ownership) {
TEST(TestMappedRingBuffer, nominal_push_shared_get_pop_unique) {
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(2);
std::unique_ptr<char> expected(new char('a'));
// Store expected value's address for later comparison.
char * expected_orig = expected.get();
std::shared_ptr<const char> expected(new char('a'));
const char * expected_orig = expected.get();
EXPECT_FALSE(mrb.push_and_replace(1, expected));
EXPECT_EQ(2, expected.use_count());
std::unique_ptr<char> actual;
mrb.get_copy_at_key(1, actual);
mrb.get(1, actual);
EXPECT_NE(nullptr, actual);
if (actual) {
EXPECT_EQ('a', *actual);
}
EXPECT_NE(expected_orig, actual.get());
mrb.pop(1, actual);
EXPECT_NE(expected_orig, actual.get());
if (actual) {
EXPECT_EQ('a', *actual);
}
mrb.get(1, actual);
EXPECT_EQ(nullptr, actual);
mrb.get_ownership_at_key(1, actual);
EXPECT_FALSE(mrb.push_and_replace(1, expected));
expected.reset();
mrb.pop(1, actual);
EXPECT_NE(expected_orig, actual.get());
if (actual) {
EXPECT_EQ('a', *actual);
}
mrb.get(1, actual);
EXPECT_EQ(nullptr, actual);
expected.reset(new char('a'));
EXPECT_FALSE(mrb.push_and_replace(1, expected));
expected.reset(new char('b'));
EXPECT_FALSE(mrb.push_and_replace(2, expected));
expected.reset(new char('c'));
EXPECT_TRUE(mrb.push_and_replace(3, expected));
mrb.get(1, actual);
EXPECT_EQ(nullptr, actual);
mrb.get(2, actual);
EXPECT_NE(nullptr, actual);
if (actual) {
EXPECT_EQ('b', *actual);
}
mrb.get(3, actual);
EXPECT_NE(nullptr, actual);
if (actual) {
EXPECT_EQ('c', *actual);
}
}
/*
Tests normal usage of the mrb.
Using unique push_and_replace, get and pop methods.
*/
TEST(TestMappedRingBuffer, nominal_push_unique_get_pop_unique) {
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(2);
std::unique_ptr<char> expected(new char('a'));
const char * expected_orig = expected.get();
EXPECT_FALSE(mrb.push_and_replace(1, std::move(expected)));
std::unique_ptr<char> actual;
mrb.get(1, actual);
EXPECT_NE(nullptr, actual);
if (actual) {
EXPECT_EQ('a', *actual);
}
EXPECT_NE(expected_orig, actual.get());
mrb.pop(1, actual);
if (actual) {
EXPECT_EQ('a', *actual);
}
EXPECT_EQ(expected_orig, actual.get());
mrb.get(1, actual);
EXPECT_EQ(nullptr, actual);
expected.reset(new char('a'));
EXPECT_FALSE(mrb.push_and_replace(1, std::move(expected)));
expected.reset(new char('b'));
EXPECT_FALSE(mrb.push_and_replace(2, std::move(expected)));
expected.reset(new char('c'));
EXPECT_TRUE(mrb.push_and_replace(3, std::move(expected)));
mrb.get(1, actual);
EXPECT_EQ(nullptr, actual);
mrb.get(2, actual);
EXPECT_NE(nullptr, actual);
if (actual) {
EXPECT_EQ('b', *actual);
}
mrb.get(3, actual);
EXPECT_NE(nullptr, actual);
if (actual) {
EXPECT_EQ('c', *actual);
}
}
/*
Tests normal usage of the mrb.
Using unique push_and_replace, shared get and pop methods.
*/
TEST(TestMappedRingBuffer, nominal_push_unique_get_pop_shared) {
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(2);
std::unique_ptr<char> expected(new char('a'));
const char * expected_orig = expected.get();
EXPECT_FALSE(mrb.push_and_replace(1, std::move(expected)));
std::shared_ptr<const char> actual;
mrb.get(1, actual);
EXPECT_NE(nullptr, actual);
if (actual) {
EXPECT_EQ('a', *actual);
}
EXPECT_EQ(expected_orig, actual.get());
mrb.pop(1, actual);
if (actual) {
EXPECT_EQ('a', *actual);
}
EXPECT_EQ(expected_orig, actual.get());
mrb.get(1, actual);
EXPECT_EQ(nullptr, actual);
mrb.pop_at_key(1, actual);
expected.reset(new char('a'));
EXPECT_FALSE(mrb.push_and_replace(1, std::move(expected)));
expected.reset(new char('b'));
EXPECT_FALSE(mrb.push_and_replace(2, std::move(expected)));
expected.reset(new char('c'));
EXPECT_TRUE(mrb.push_and_replace(3, std::move(expected)));
mrb.get(1, actual);
EXPECT_EQ(nullptr, actual);
mrb.get(2, actual);
EXPECT_NE(nullptr, actual);
if (actual) {
EXPECT_EQ('a', *actual); // The value should be the same.
EXPECT_EQ('b', *actual);
}
EXPECT_NE(expected_orig, actual.get()); // Even though we pop'ed, we didn't get the original.
mrb.get_copy_at_key(1, actual);
EXPECT_EQ(nullptr, actual);
mrb.get(3, actual);
EXPECT_NE(nullptr, actual);
if (actual) {
EXPECT_EQ('c', *actual);
}
}
/*
@@ -152,22 +310,23 @@ TEST(TestMappedRingBuffer, get_ownership) {
TEST(TestMappedRingBuffer, non_unique_keys) {
rclcpp::mapped_ring_buffer::MappedRingBuffer<char> mrb(2);
std::unique_ptr<char> input(new char('a'));
std::shared_ptr<const char> input(new char('a'));
mrb.push_and_replace(1, input);
input.reset(new char('b'));
// Different value, same key.
mrb.push_and_replace(1, input);
input.reset();
std::unique_ptr<char> actual;
mrb.pop_at_key(1, actual);
mrb.pop(1, actual);
EXPECT_NE(nullptr, actual);
if (actual) {
EXPECT_EQ('a', *actual);
}
actual = nullptr;
mrb.pop_at_key(1, actual);
mrb.pop(1, actual);
EXPECT_NE(nullptr, actual);
if (actual) {
EXPECT_EQ('b', *actual);

View File

@@ -14,9 +14,10 @@
#include <gtest/gtest.h>
#include <string>
#include <map>
#include <memory>
#include <string>
#include <unordered_set>
#include <vector>
#include "rclcpp/exceptions.hpp"
@@ -99,6 +100,23 @@ TEST_F(TestNode, get_name_and_namespace) {
EXPECT_STREQ("/my/ns", node->get_namespace());
EXPECT_STREQ("/my/ns/my_node", node->get_fully_qualified_name());
}
{
auto node1 = std::make_shared<rclcpp::Node>("my_node1", "my/ns");
auto node2 = std::make_shared<rclcpp::Node>("my_node2", "my/ns");
auto node3 = std::make_shared<rclcpp::Node>("my_node3", "/ns2");
auto node4 = std::make_shared<rclcpp::Node>("my_node4", "my/ns3");
auto names_and_namespaces = node1->get_node_names();
auto name_namespace_set = std::unordered_set<std::string>(names_and_namespaces.begin(),
names_and_namespaces.end());
std::function<bool(std::string)> Set_Contains = [&](std::string string_key)
{
return name_namespace_set.find(string_key) != name_namespace_set.end();
};
EXPECT_TRUE(Set_Contains("/my/ns/my_node1"));
EXPECT_TRUE(Set_Contains("/my/ns/my_node2"));
EXPECT_TRUE(Set_Contains("/ns2/my_node3"));
EXPECT_TRUE(Set_Contains("/my/ns3/my_node4"));
}
}
TEST_F(TestNode, subnode_get_name_and_namespace) {
@@ -347,8 +365,10 @@ TEST_F(TestNode, declare_parameter_with_no_initial_values) {
TEST_F(TestNode, declare_parameter_with_initial_values) {
// test cases with initial values
rclcpp::NodeOptions no;
no.initial_parameters({
no.parameter_overrides({
{"parameter_no_default", 42},
{"parameter_no_default_set", 42},
{"parameter_no_default_set_cvref", 42},
{"parameter_and_default", 42},
{"parameter_custom", 42},
{"parameter_template", 42},
@@ -363,6 +383,25 @@ TEST_F(TestNode, declare_parameter_with_initial_values) {
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
EXPECT_EQ(value.get<int>(), 42);
}
{
// no default, with initial, and set after
rclcpp::ParameterValue value = node->declare_parameter("parameter_no_default_set");
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
EXPECT_EQ(value.get<int>(), 42);
// check that the value is changed after a set
node->set_parameter({"parameter_no_default_set", 44});
EXPECT_EQ(node->get_parameter("parameter_no_default_set").get_value<int>(), 44);
}
{
// no default, with initial
const rclcpp::ParameterValue & value =
node->declare_parameter("parameter_no_default_set_cvref");
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
EXPECT_EQ(value.get<int>(), 42);
// check that the value is changed after a set
node->set_parameter({"parameter_no_default_set_cvref", 44});
EXPECT_EQ(value.get<int>(), 44);
}
{
// int default, with initial
rclcpp::ParameterValue default_value(43);
@@ -677,12 +716,370 @@ TEST_F(TestNode, set_parameter_undeclared_parameters_not_allowed) {
EXPECT_FALSE(node->has_parameter(name));
}
{
// setting a parameter with integer range descriptor
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.integer_range.resize(1);
auto & integer_range = descriptor.integer_range.at(0);
integer_range.from_value = 10;
integer_range.to_value = 18;
integer_range.step = 2;
node->declare_parameter(name, 10, descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
EXPECT_EQ(value.get_value<int64_t>(), 10);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 14)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 14);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 18)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 18);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 15)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 18);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 20)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 18);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 8)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 18);
}
{
// setting a parameter with integer range descriptor, from_value > to_value
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.integer_range.resize(1);
auto & integer_range = descriptor.integer_range.at(0);
integer_range.from_value = 20;
integer_range.to_value = 18;
integer_range.step = 1;
node->declare_parameter(name, 20, descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
EXPECT_EQ(value.get_value<int64_t>(), 20);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 18)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 18);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 19)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 18);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 10)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 18);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 25)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 18);
}
{
// setting a parameter with integer range descriptor, from_value = to_value
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.integer_range.resize(1);
auto & integer_range = descriptor.integer_range.at(0);
integer_range.from_value = 18;
integer_range.to_value = 18;
integer_range.step = 1;
node->declare_parameter(name, 18, descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
EXPECT_EQ(value.get_value<int64_t>(), 18);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 17)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 18);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 19)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 18);
}
{
// setting a parameter with integer range descriptor, step > distance(from_value, to_value)
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.integer_range.resize(1);
auto & integer_range = descriptor.integer_range.at(0);
integer_range.from_value = 18;
integer_range.to_value = 25;
integer_range.step = 10;
node->declare_parameter(name, 18, descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
EXPECT_EQ(value.get_value<int64_t>(), 18);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 25)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 25);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 17)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 25);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 19)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 25);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 26)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 25);
}
{
// setting a parameter with integer range descriptor, distance not multiple of the step.
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.integer_range.resize(1);
auto & integer_range = descriptor.integer_range.at(0);
integer_range.from_value = 18;
integer_range.to_value = 28;
integer_range.step = 7;
node->declare_parameter(name, 18, descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
EXPECT_EQ(value.get_value<int64_t>(), 18);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 28)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 28);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 25)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 25);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 17)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 25);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 19)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 25);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 32)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 25);
}
{
// setting a parameter with integer range descriptor, step=0
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.integer_range.resize(1);
auto & integer_range = descriptor.integer_range.at(0);
integer_range.from_value = 10;
integer_range.to_value = 18;
integer_range.step = 0;
node->declare_parameter(name, 10, descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
EXPECT_EQ(value.get_value<int64_t>(), 10);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 11)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 11);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 15)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 15);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 18)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 18);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 9)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 18);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 19)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<int64_t>(), 18);
}
{
// setting a parameter with integer range descriptor and wrong default value will throw
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.integer_range.resize(1);
auto & integer_range = descriptor.integer_range.at(0);
integer_range.from_value = 10;
integer_range.to_value = 18;
integer_range.step = 2;
ASSERT_THROW(
node->declare_parameter(name, 42, descriptor),
rclcpp::exceptions::InvalidParameterValueException);
}
{
// setting a parameter with floating point range descriptor
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.floating_point_range.resize(1);
auto & floating_point_range = descriptor.floating_point_range.at(0);
floating_point_range.from_value = 10.0;
floating_point_range.to_value = 11.0;
floating_point_range.step = 0.2;
node->declare_parameter(name, 10.0, descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE);
EXPECT_EQ(value.get_value<double>(), 10.0);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 10.2)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 10.2);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 11.0)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 11.3)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 12.0)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 9.4)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
}
{
// setting a parameter with floating point range descriptor, negative step
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.floating_point_range.resize(1);
auto & floating_point_range = descriptor.floating_point_range.at(0);
floating_point_range.from_value = 10.0;
floating_point_range.to_value = 11.0;
floating_point_range.step = -0.2;
node->declare_parameter(name, 10.0, descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE);
EXPECT_EQ(value.get_value<double>(), 10.0);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 10.2)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 10.2);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 11.0)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 11.3)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 12.0)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 9.4)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
}
{
// setting a parameter with floating point range descriptor, from_value > to_value
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.floating_point_range.resize(1);
auto & floating_point_range = descriptor.floating_point_range.at(0);
floating_point_range.from_value = 11.0;
floating_point_range.to_value = 10.0;
floating_point_range.step = 0.2;
node->declare_parameter(name, 10.0, descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE);
EXPECT_EQ(value.get_value<double>(), 10.0);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 11.0)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 11.2)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 12.0)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 9.4)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
}
{
// setting a parameter with floating point range descriptor, from_value = to_value
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.floating_point_range.resize(1);
auto & floating_point_range = descriptor.floating_point_range.at(0);
floating_point_range.from_value = 10.0;
floating_point_range.to_value = 10.0;
floating_point_range.step = 0.2;
node->declare_parameter(name, 10.0, descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE);
EXPECT_EQ(value.get_value<double>(), 10.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 11.2)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 10.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 12.0)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 10.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 9.4)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 10.0);
}
{
// setting a parameter with floating point range descriptor, step > distance
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.floating_point_range.resize(1);
auto & floating_point_range = descriptor.floating_point_range.at(0);
floating_point_range.from_value = 10.0;
floating_point_range.to_value = 11.0;
floating_point_range.step = 2.2;
node->declare_parameter(name, 10.0, descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE);
EXPECT_EQ(value.get_value<double>(), 10.0);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 11.0)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 12.2)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 7.8)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
}
{
// setting a parameter with floating point range descriptor, distance not multiple of the step.
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.floating_point_range.resize(1);
auto & floating_point_range = descriptor.floating_point_range.at(0);
floating_point_range.from_value = 10.0;
floating_point_range.to_value = 11.0;
floating_point_range.step = 0.7;
node->declare_parameter(name, 10.0, descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE);
EXPECT_EQ(value.get_value<double>(), 10.0);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 11.0)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 10.7)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 10.7);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 11.4)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 10.7);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 9.3)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 10.7);
}
{
// setting a parameter with floating point range descriptor, step=0
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.floating_point_range.resize(1);
auto & floating_point_range = descriptor.floating_point_range.at(0);
floating_point_range.from_value = 10.0;
floating_point_range.to_value = 11.0;
floating_point_range.step = 0.0;
node->declare_parameter(name, 10.0, descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE);
EXPECT_EQ(value.get_value<double>(), 10.0);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 10.0001)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 10.0001);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 10.5479051)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 10.5479051);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 11.0)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 11.001)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 9.999)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
}
{
// setting a parameter with a different type is still possible
// when having a descriptor specifying a type (type is a status, not a constraint).
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.type = rclcpp::PARAMETER_INTEGER;
node->declare_parameter(name, 42, descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
EXPECT_EQ(value.get_value<int64_t>(), 42);
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, "asd")).successful);
EXPECT_TRUE(node->has_parameter(name));
value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_STRING);
EXPECT_EQ(value.get_value<std::string>(), "asd");
}
}
TEST_F(TestNode, set_parameter_undeclared_parameters_allowed) {
auto node = std::make_shared<rclcpp::Node>(
"test_set_parameter_node"_unq,
rclcpp::NodeOptions().allow_undeclared_parameters(true));
rclcpp::NodeOptions no;
no.parameter_overrides({
{"parameter_with_override", 30},
});
no.allow_undeclared_parameters(true);
auto node = std::make_shared<rclcpp::Node>("test_set_parameter_node"_unq, no);
{
// overrides are ignored when not declaring a parameter
auto name = "parameter_with_override";
EXPECT_FALSE(node->has_parameter(name));
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, 43)).successful);
EXPECT_TRUE(node->has_parameter(name));
EXPECT_EQ(node->get_parameter(name).get_value<int>(), 43);
}
{
// normal use (declare first) still works with this true
auto name = "parameter"_unq;

View File

@@ -32,7 +32,7 @@ protected:
}
};
TEST(TestParameter, not_set_variant) {
TEST_F(TestParameter, not_set_variant) {
// Direct instantiation
rclcpp::Parameter not_set_variant;
EXPECT_EQ(rclcpp::PARAMETER_NOT_SET, not_set_variant.get_type());
@@ -57,7 +57,7 @@ TEST(TestParameter, not_set_variant) {
rclcpp::Parameter::from_parameter_msg(not_set_param).get_type());
}
TEST(TestParameter, bool_variant) {
TEST_F(TestParameter, bool_variant) {
// Direct instantiation
rclcpp::Parameter bool_variant_true("bool_param", true);
EXPECT_EQ("bool_param", bool_variant_true.get_name());
@@ -111,7 +111,7 @@ TEST(TestParameter, bool_variant) {
bool_variant_false.get_value_message().type);
}
TEST(TestParameter, integer_variant) {
TEST_F(TestParameter, integer_variant) {
const int TEST_VALUE {42};
// Direct instantiation
@@ -155,7 +155,7 @@ TEST(TestParameter, integer_variant) {
from_msg.get_value_message().type);
}
TEST(TestParameter, long_integer_variant) {
TEST_F(TestParameter, long_integer_variant) {
const int64_t TEST_VALUE {std::numeric_limits<int64_t>::max()};
// Direct instantiation
@@ -199,7 +199,7 @@ TEST(TestParameter, long_integer_variant) {
from_msg.get_value_message().type);
}
TEST(TestParameter, float_variant) {
TEST_F(TestParameter, float_variant) {
const float TEST_VALUE {42.0f};
// Direct instantiation
@@ -243,7 +243,7 @@ TEST(TestParameter, float_variant) {
from_msg.get_value_message().type);
}
TEST(TestParameter, double_variant) {
TEST_F(TestParameter, double_variant) {
const double TEST_VALUE {-42.1};
// Direct instantiation
@@ -287,7 +287,7 @@ TEST(TestParameter, double_variant) {
from_msg.get_value_message().type);
}
TEST(TestParameter, string_variant) {
TEST_F(TestParameter, string_variant) {
const std::string TEST_VALUE {"ROS2"};
// Direct instantiation
@@ -330,7 +330,7 @@ TEST(TestParameter, string_variant) {
from_msg.get_value_message().type);
}
TEST(TestParameter, byte_array_variant) {
TEST_F(TestParameter, byte_array_variant) {
const std::vector<uint8_t> TEST_VALUE {0x52, 0x4f, 0x53, 0x32};
// Direct instantiation
@@ -374,7 +374,7 @@ TEST(TestParameter, byte_array_variant) {
from_msg.get_value_message().type);
}
TEST(TestParameter, bool_array_variant) {
TEST_F(TestParameter, bool_array_variant) {
const std::vector<bool> TEST_VALUE {false, true, true, false, false, true};
// Direct instantiation
@@ -418,7 +418,7 @@ TEST(TestParameter, bool_array_variant) {
from_msg.get_value_message().type);
}
TEST(TestParameter, integer_array_variant) {
TEST_F(TestParameter, integer_array_variant) {
const std::vector<int> TEST_VALUE
{42, -99, std::numeric_limits<int>::max(), std::numeric_limits<int>::lowest(), 0};
@@ -493,7 +493,7 @@ TEST(TestParameter, integer_array_variant) {
rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY);
}
TEST(TestParameter, long_integer_array_variant) {
TEST_F(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};
@@ -541,7 +541,7 @@ TEST(TestParameter, long_integer_array_variant) {
from_msg.get_value_message().type);
}
TEST(TestParameter, float_array_variant) {
TEST_F(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};
@@ -616,7 +616,7 @@ TEST(TestParameter, float_array_variant) {
from_msg.get_value_message().type);
}
TEST(TestParameter, double_array_variant) {
TEST_F(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};
@@ -664,7 +664,7 @@ TEST(TestParameter, double_array_variant) {
from_msg.get_value_message().type);
}
TEST(TestParameter, string_array_variant) {
TEST_F(TestParameter, string_array_variant) {
const std::vector<std::string> TEST_VALUE {"R", "O", "S2"};
// Direct instantiation

View File

@@ -0,0 +1,155 @@
// Copyright 2019 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_interfaces/msg/parameter_event.hpp"
class TestParameterClient : public ::testing::Test
{
public:
void OnMessage(const rcl_interfaces::msg::ParameterEvent::SharedPtr event)
{
(void)event;
}
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
{
node = std::make_shared<rclcpp::Node>("test_parameter_client", "/ns");
}
void TearDown()
{
node.reset();
}
rclcpp::Node::SharedPtr node;
};
/*
Testing async parameter client construction and destruction.
*/
TEST_F(TestParameterClient, async_construction_and_destruction) {
{
auto asynchronous_client = std::make_shared<rclcpp::AsyncParametersClient>(node);
(void)asynchronous_client;
}
{
auto asynchronous_client = std::make_shared<rclcpp::AsyncParametersClient>(
node->get_node_base_interface(),
node->get_node_topics_interface(),
node->get_node_graph_interface(),
node->get_node_services_interface());
(void)asynchronous_client;
}
{
ASSERT_THROW({
std::make_shared<rclcpp::AsyncParametersClient>(node, "invalid_remote_node?");
}, rclcpp::exceptions::InvalidServiceNameError);
}
}
/*
Testing sync parameter client construction and destruction.
*/
TEST_F(TestParameterClient, sync_construction_and_destruction) {
{
auto synchronous_client = std::make_shared<rclcpp::SyncParametersClient>(node);
(void)synchronous_client;
}
{
auto synchronous_client = std::make_shared<rclcpp::SyncParametersClient>(
std::make_shared<rclcpp::executors::SingleThreadedExecutor>(),
node);
(void)synchronous_client;
}
{
auto synchronous_client = std::make_shared<rclcpp::SyncParametersClient>(
std::make_shared<rclcpp::executors::SingleThreadedExecutor>(),
node->get_node_base_interface(),
node->get_node_topics_interface(),
node->get_node_graph_interface(),
node->get_node_services_interface());
(void)synchronous_client;
}
{
ASSERT_THROW({
std::make_shared<rclcpp::SyncParametersClient>(node, "invalid_remote_node?");
}, rclcpp::exceptions::InvalidServiceNameError);
}
}
/*
Testing different methods for parameter event subscription from asynchronous clients.
*/
TEST_F(TestParameterClient, async_parameter_event_subscription) {
auto callback = std::bind(&TestParameterClient::OnMessage, this, std::placeholders::_1);
{
auto asynchronous_client = std::make_shared<rclcpp::AsyncParametersClient>(node);
auto event_sub = asynchronous_client->on_parameter_event(callback);
(void)event_sub;
}
{
auto event_sub = rclcpp::AsyncParametersClient::on_parameter_event(node, callback);
(void)event_sub;
}
{
auto event_sub = rclcpp::AsyncParametersClient::on_parameter_event(
node->get_node_topics_interface(),
callback);
(void)event_sub;
}
}
/*
Testing different methods for parameter event subscription from synchronous clients.
*/
TEST_F(TestParameterClient, sync_parameter_event_subscription) {
auto callback = std::bind(&TestParameterClient::OnMessage, this, std::placeholders::_1);
{
auto synchronous_client = std::make_shared<rclcpp::SyncParametersClient>(node);
auto event_sub = synchronous_client->on_parameter_event(callback);
(void)event_sub;
}
{
auto event_sub = rclcpp::SyncParametersClient::on_parameter_event(node, callback);
(void)event_sub;
}
{
auto event_sub = rclcpp::SyncParametersClient::on_parameter_event(
node->get_node_topics_interface(),
callback);
(void)event_sub;
}
}

View File

@@ -1,70 +0,0 @@
// Copyright 2019 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 "test_msgs/msg/empty.hpp"
class TestPubSubOptionAPI : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
{
node = std::make_shared<rclcpp::Node>("my_node", "/ns");
}
void TearDown()
{
node.reset();
}
rclcpp::Node::SharedPtr node;
};
TEST_F(TestPubSubOptionAPI, check_for_ambiguous) {
rclcpp::PublisherOptions pub_options;
rclcpp::SubscriptionOptions sub_options;
auto topic_only_pub = node->create_publisher<test_msgs::msg::Empty>(
"topic_only");
auto topic_depth_pub = node->create_publisher<test_msgs::msg::Empty>(
"topic_depth",
10);
auto all_options_pub = node->create_publisher<test_msgs::msg::Empty>(
"topic_options",
10,
pub_options);
auto topic_only_sub = node->create_subscription<test_msgs::msg::Empty>(
"topic_only",
[](std::shared_ptr<test_msgs::msg::Empty>) {});
auto topic_depth_sub = node->create_subscription<test_msgs::msg::Empty>(
"topic_depth",
[](std::shared_ptr<test_msgs::msg::Empty>) {},
10);
auto all_options_sub = node->create_subscription<test_msgs::msg::Empty>(
"topic_options",
[](std::shared_ptr<test_msgs::msg::Empty>) {},
10,
sub_options);
}

View File

@@ -16,6 +16,7 @@
#include <string>
#include <memory>
#include <vector>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
@@ -24,12 +25,13 @@
class TestPublisher : public ::testing::Test
{
protected:
public:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
protected:
void initialize(const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions())
{
node = std::make_shared<rclcpp::Node>("my_node", "/ns", node_options);
@@ -43,6 +45,25 @@ protected:
rclcpp::Node::SharedPtr node;
};
struct TestParameters
{
TestParameters(rclcpp::QoS qos, std::string description)
: qos(qos), description(description) {}
rclcpp::QoS qos;
std::string description;
};
std::ostream & operator<<(std::ostream & out, const TestParameters & params)
{
out << params.description;
return out;
}
class TestPublisherInvalidIntraprocessQos
: public TestPublisher,
public ::testing::WithParamInterface<TestParameters>
{};
class TestPublisherSub : public ::testing::Test
{
protected:
@@ -65,14 +86,6 @@ protected:
rclcpp::Node::SharedPtr subnode;
};
static constexpr rmw_qos_profile_t invalid_qos_profile()
{
rmw_qos_profile_t profile = rmw_qos_profile_default;
profile.depth = 1;
profile.durability = RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL;
return profile;
}
/*
Testing publisher construction and destruction.
*/
@@ -80,50 +93,148 @@ TEST_F(TestPublisher, construction_and_destruction) {
initialize();
using rcl_interfaces::msg::IntraProcessMessage;
{
auto publisher = node->create_publisher<IntraProcessMessage>("topic");
auto publisher = node->create_publisher<IntraProcessMessage>("topic", 42);
(void)publisher;
}
{
ASSERT_THROW({
auto publisher = node->create_publisher<IntraProcessMessage>("invalid_topic?");
auto publisher = node->create_publisher<IntraProcessMessage>("invalid_topic?", 42);
}, rclcpp::exceptions::InvalidTopicNameError);
}
}
/*
Testing publisher creation signatures.
*/
TEST_F(TestPublisher, various_creation_signatures) {
initialize();
using rcl_interfaces::msg::IntraProcessMessage;
{
auto publisher = node->create_publisher<IntraProcessMessage>("topic", 42);
(void)publisher;
}
{
auto publisher = node->create_publisher<IntraProcessMessage>("topic", rclcpp::QoS(42));
(void)publisher;
}
{
auto publisher =
node->create_publisher<IntraProcessMessage>("topic", rclcpp::QoS(rclcpp::KeepLast(42)));
(void)publisher;
}
{
auto publisher =
node->create_publisher<IntraProcessMessage>("topic", rclcpp::QoS(rclcpp::KeepAll()));
(void)publisher;
}
{
auto publisher =
node->create_publisher<IntraProcessMessage>("topic", 42, rclcpp::PublisherOptions());
(void)publisher;
}
{
auto publisher =
rclcpp::create_publisher<IntraProcessMessage>(node, "topic", 42, rclcpp::PublisherOptions());
(void)publisher;
}
// Now deprecated functions.
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
{
auto publisher = node->create_publisher<IntraProcessMessage>("topic");
(void)publisher;
}
{
auto publisher = node->create_publisher<IntraProcessMessage>(
"topic",
42,
std::make_shared<std::allocator<IntraProcessMessage>>());
(void)publisher;
}
{
auto publisher = node->create_publisher<IntraProcessMessage>("topic", rmw_qos_profile_default);
(void)publisher;
}
{
auto publisher = node->create_publisher<IntraProcessMessage>(
"topic",
rmw_qos_profile_default,
std::make_shared<std::allocator<IntraProcessMessage>>());
(void)publisher;
}
#if !defined(_WIN32)
# pragma GCC diagnostic pop
#else // !defined(_WIN32)
# pragma warning(pop)
#endif
}
/*
Testing publisher with intraprocess enabled and invalid QoS
*/
TEST_F(TestPublisher, intraprocess_with_invalid_qos) {
TEST_P(TestPublisherInvalidIntraprocessQos, test_publisher_throws) {
initialize(rclcpp::NodeOptions().use_intra_process_comms(true));
rmw_qos_profile_t qos = invalid_qos_profile();
rclcpp::QoS qos = GetParam().qos;
using rcl_interfaces::msg::IntraProcessMessage;
{
ASSERT_THROW(
{auto publisher = node->create_publisher<IntraProcessMessage>("topic", qos);},
rclcpp::exceptions::InvalidParametersException);
std::invalid_argument);
}
}
static std::vector<TestParameters> invalid_qos_profiles()
{
std::vector<TestParameters> parameters;
parameters.reserve(3);
parameters.push_back(
TestParameters(
rclcpp::QoS(rclcpp::KeepLast(10)).transient_local(),
"transient_local_qos"));
parameters.push_back(
TestParameters(
rclcpp::QoS(rclcpp::KeepLast(0)),
"keep_last_qos_with_zero_history_depth"));
parameters.push_back(
TestParameters(
rclcpp::QoS(rclcpp::KeepAll()),
"keep_all_qos"));
return parameters;
}
INSTANTIATE_TEST_CASE_P(
TestPublisherThrows, TestPublisherInvalidIntraprocessQos,
::testing::ValuesIn(invalid_qos_profiles()),
::testing::PrintToStringParamName());
/*
Testing publisher construction and destruction for subnodes.
*/
TEST_F(TestPublisherSub, construction_and_destruction) {
using rcl_interfaces::msg::IntraProcessMessage;
{
auto publisher = subnode->create_publisher<IntraProcessMessage>("topic");
auto publisher = subnode->create_publisher<IntraProcessMessage>("topic", 42);
EXPECT_STREQ(publisher->get_topic_name(), "/ns/sub_ns/topic");
}
{
auto publisher = subnode->create_publisher<IntraProcessMessage>("/topic");
auto publisher = subnode->create_publisher<IntraProcessMessage>("/topic", 42);
EXPECT_STREQ(publisher->get_topic_name(), "/topic");
}
{
ASSERT_THROW({
auto publisher = subnode->create_publisher<IntraProcessMessage>("invalid_topic?");
auto publisher = subnode->create_publisher<IntraProcessMessage>("invalid_topic?", 42);
}, rclcpp::exceptions::InvalidTopicNameError);
}
}

View File

@@ -74,12 +74,12 @@ TEST_P(TestPublisherSubscriptionCount, increasing_and_decreasing_counts)
"my_node",
"/ns",
parameters.node_options[0]);
auto publisher = node->create_publisher<IntraProcessMessage>("/topic");
auto publisher = node->create_publisher<IntraProcessMessage>("/topic", 10);
EXPECT_EQ(publisher->get_subscription_count(), 0u);
EXPECT_EQ(publisher->get_intra_process_subscription_count(), 0u);
{
auto sub = node->create_subscription<IntraProcessMessage>("/topic", &OnMessage);
auto sub = node->create_subscription<IntraProcessMessage>("/topic", 10, &OnMessage);
rclcpp::sleep_for(offset);
EXPECT_EQ(publisher->get_subscription_count(), 1u);
EXPECT_EQ(
@@ -91,7 +91,7 @@ TEST_P(TestPublisherSubscriptionCount, increasing_and_decreasing_counts)
"/ns",
parameters.node_options[1]);
auto another_sub =
another_node->create_subscription<IntraProcessMessage>("/topic", &OnMessage);
another_node->create_subscription<IntraProcessMessage>("/topic", 10, &OnMessage);
rclcpp::sleep_for(offset);
EXPECT_EQ(publisher->get_subscription_count(), 2u);

View File

@@ -56,8 +56,8 @@ TEST(TestSerializedMessageAllocator, borrow_from_subscription) {
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;});
node->create_subscription<test_msgs::msg::Empty>("~/dummy_topic", 10,
[](std::shared_ptr<test_msgs::msg::Empty> test_msg) {(void) test_msg;});
auto msg0 = sub->create_serialized_message();
EXPECT_EQ(0u, msg0->buffer_capacity);

View File

@@ -95,7 +95,7 @@ public:
auto callback = std::bind(
&SubscriptionClassNodeInheritance::OnMessage, this, std::placeholders::_1);
using rcl_interfaces::msg::IntraProcessMessage;
auto sub = this->create_subscription<IntraProcessMessage>("topic", callback);
auto sub = this->create_subscription<IntraProcessMessage>("topic", 10, callback);
}
};
@@ -112,7 +112,7 @@ public:
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);
auto sub = node->create_subscription<IntraProcessMessage>("topic", 10, callback);
}
};
@@ -125,12 +125,12 @@ TEST_F(TestSubscription, construction_and_destruction) {
(void)msg;
};
{
auto sub = node->create_subscription<IntraProcessMessage>("topic", callback);
auto sub = node->create_subscription<IntraProcessMessage>("topic", 10, callback);
}
{
ASSERT_THROW({
auto sub = node->create_subscription<IntraProcessMessage>("invalid_topic?", callback);
auto sub = node->create_subscription<IntraProcessMessage>("invalid_topic?", 10, callback);
}, rclcpp::exceptions::InvalidTopicNameError);
}
}
@@ -144,17 +144,17 @@ TEST_F(TestSubscriptionSub, construction_and_destruction) {
(void)msg;
};
{
auto sub = subnode->create_subscription<IntraProcessMessage>("topic", callback);
auto sub = subnode->create_subscription<IntraProcessMessage>("topic", 1, callback);
EXPECT_STREQ(sub->get_topic_name(), "/ns/sub_ns/topic");
}
{
auto sub = subnode->create_subscription<IntraProcessMessage>("/topic", callback);
auto sub = subnode->create_subscription<IntraProcessMessage>("/topic", 1, callback);
EXPECT_STREQ(sub->get_topic_name(), "/topic");
}
{
auto sub = subnode->create_subscription<IntraProcessMessage>("~/topic", callback);
auto sub = subnode->create_subscription<IntraProcessMessage>("~/topic", 1, callback);
std::string expected_topic_name =
std::string(node->get_namespace()) + "/" + node->get_name() + "/topic";
EXPECT_STREQ(sub->get_topic_name(), expected_topic_name.c_str());
@@ -162,11 +162,81 @@ TEST_F(TestSubscriptionSub, construction_and_destruction) {
{
ASSERT_THROW({
auto sub = node->create_subscription<IntraProcessMessage>("invalid_topic?", callback);
auto sub = node->create_subscription<IntraProcessMessage>("invalid_topic?", 1, callback);
}, rclcpp::exceptions::InvalidTopicNameError);
}
}
/*
Testing subscription creation signatures.
*/
TEST_F(TestSubscription, various_creation_signatures) {
using rcl_interfaces::msg::IntraProcessMessage;
auto cb = [](rcl_interfaces::msg::IntraProcessMessage::SharedPtr) {};
{
auto sub = node->create_subscription<IntraProcessMessage>("topic", 1, cb);
(void)sub;
}
{
auto sub = node->create_subscription<IntraProcessMessage>("topic", rclcpp::QoS(1), cb);
(void)sub;
}
{
auto sub =
node->create_subscription<IntraProcessMessage>("topic", rclcpp::QoS(rclcpp::KeepLast(1)), cb);
(void)sub;
}
{
auto sub =
node->create_subscription<IntraProcessMessage>("topic", rclcpp::QoS(rclcpp::KeepAll()), cb);
(void)sub;
}
{
auto sub = node->create_subscription<IntraProcessMessage>(
"topic", 42, cb, rclcpp::SubscriptionOptions());
(void)sub;
}
{
auto sub = rclcpp::create_subscription<IntraProcessMessage>(
node, "topic", 42, cb, rclcpp::SubscriptionOptions());
(void)sub;
}
// Now deprecated functions.
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
{
auto sub = node->create_subscription<IntraProcessMessage>("topic", cb, 42);
(void)sub;
}
{
auto sub = node->create_subscription<IntraProcessMessage>("topic", cb);
(void)sub;
}
{
auto sub = node->create_subscription<IntraProcessMessage>("topic", cb, rmw_qos_profile_default);
(void)sub;
}
{
auto sub =
node->create_subscription<IntraProcessMessage>("topic", cb, rmw_qos_profile_default, nullptr);
(void)sub;
}
{
auto sub = node->create_subscription<IntraProcessMessage>("topic", cb, 42, nullptr);
(void)sub;
}
#if !defined(_WIN32)
# pragma GCC diagnostic pop
#else // !defined(_WIN32)
# pragma warning(pop)
#endif
}
/*
Testing subscriptions using std::bind.
*/
@@ -187,6 +257,6 @@ TEST_F(TestSubscription, callback_bind) {
// 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);
auto sub = node->create_subscription<IntraProcessMessage>("topic", 1, callback);
}
}

View File

@@ -65,11 +65,11 @@ TEST_P(TestSubscriptionPublisherCount, increasing_and_decreasing_counts)
"my_node",
"/ns",
node_options);
auto subscription = node->create_subscription<IntraProcessMessage>("/topic", &OnMessage);
auto subscription = node->create_subscription<IntraProcessMessage>("/topic", 10, &OnMessage);
EXPECT_EQ(subscription->get_publisher_count(), 0u);
{
auto pub = node->create_publisher<IntraProcessMessage>("/topic");
auto pub = node->create_publisher<IntraProcessMessage>("/topic", 10);
rclcpp::sleep_for(offset);
EXPECT_EQ(subscription->get_publisher_count(), 1u);
{
@@ -78,7 +78,7 @@ TEST_P(TestSubscriptionPublisherCount, increasing_and_decreasing_counts)
"/ns",
node_options);
auto another_pub =
another_node->create_publisher<IntraProcessMessage>("/topic");
another_node->create_publisher<IntraProcessMessage>("/topic", 10);
rclcpp::sleep_for(offset);
EXPECT_EQ(subscription->get_publisher_count(), 2u);

View File

@@ -45,7 +45,7 @@ protected:
}
};
TEST(TestTime, clock_type_access) {
TEST_F(TestTime, clock_type_access) {
rclcpp::Clock ros_clock(RCL_ROS_TIME);
EXPECT_EQ(RCL_ROS_TIME, ros_clock.get_clock_type());
@@ -56,7 +56,7 @@ TEST(TestTime, clock_type_access) {
EXPECT_EQ(RCL_STEADY_TIME, steady_clock.get_clock_type());
}
TEST(TestTime, time_sources) {
TEST_F(TestTime, time_sources) {
using builtin_interfaces::msg::Time;
rclcpp::Clock ros_clock(RCL_ROS_TIME);
Time ros_now = ros_clock.now();
@@ -74,42 +74,124 @@ TEST(TestTime, time_sources) {
EXPECT_NE(0u, steady_now.nanosec);
}
TEST(TestTime, conversions) {
static const int64_t HALF_SEC_IN_NS = 500 * 1000 * 1000;
static const int64_t ONE_SEC_IN_NS = 1000 * 1000 * 1000;
static const int64_t ONE_AND_HALF_SEC_IN_NS = 3 * HALF_SEC_IN_NS;
TEST_F(TestTime, conversions) {
rclcpp::Clock system_clock(RCL_SYSTEM_TIME);
rclcpp::Time now = system_clock.now();
builtin_interfaces::msg::Time now_msg = now;
{
rclcpp::Time now = system_clock.now();
builtin_interfaces::msg::Time now_msg = now;
rclcpp::Time now_again = now_msg;
EXPECT_EQ(now.nanoseconds(), now_again.nanoseconds());
rclcpp::Time now_again = now_msg;
EXPECT_EQ(now.nanoseconds(), now_again.nanoseconds());
}
builtin_interfaces::msg::Time msg;
msg.sec = 12345;
msg.nanosec = 67890;
{
rclcpp::Time positive_time = rclcpp::Time(12345, 67890u);
rclcpp::Time time = msg;
EXPECT_EQ(
RCL_S_TO_NS(static_cast<int64_t>(msg.sec)) + static_cast<int64_t>(msg.nanosec),
time.nanoseconds());
EXPECT_EQ(static_cast<int64_t>(msg.sec), RCL_NS_TO_S(time.nanoseconds()));
builtin_interfaces::msg::Time msg = positive_time;
EXPECT_EQ(msg.sec, 12345);
EXPECT_EQ(msg.nanosec, 67890u);
builtin_interfaces::msg::Time negative_time_msg;
negative_time_msg.sec = -1;
negative_time_msg.nanosec = 1;
rclcpp::Time time = msg;
EXPECT_EQ(time.nanoseconds(), positive_time.nanoseconds());
EXPECT_EQ(
RCL_S_TO_NS(static_cast<int64_t>(msg.sec)) + static_cast<int64_t>(msg.nanosec),
time.nanoseconds());
EXPECT_EQ(static_cast<int64_t>(msg.sec), RCL_NS_TO_S(time.nanoseconds()));
}
EXPECT_ANY_THROW({
rclcpp::Time negative_time = negative_time_msg;
});
// throw on construction/assignment of negative times
{
builtin_interfaces::msg::Time negative_time_msg;
negative_time_msg.sec = -1;
negative_time_msg.nanosec = 1;
EXPECT_ANY_THROW(rclcpp::Time(-1, 1));
EXPECT_ANY_THROW(
{
rclcpp::Time negative_time = negative_time_msg;
});
EXPECT_ANY_THROW({
rclcpp::Time assignment(1, 2);
assignment = negative_time_msg;
});
EXPECT_ANY_THROW(rclcpp::Time(-1, 1));
EXPECT_ANY_THROW(
{
rclcpp::Time assignment(1, 2);
assignment = negative_time_msg;
});
}
{
const rclcpp::Time time(HALF_SEC_IN_NS);
const auto time_msg = static_cast<builtin_interfaces::msg::Time>(time);
EXPECT_EQ(time_msg.sec, 0);
EXPECT_EQ(time_msg.nanosec, HALF_SEC_IN_NS);
EXPECT_EQ(rclcpp::Time(time_msg).nanoseconds(), HALF_SEC_IN_NS);
}
{
const rclcpp::Time time(ONE_SEC_IN_NS);
const auto time_msg = static_cast<builtin_interfaces::msg::Time>(time);
EXPECT_EQ(time_msg.sec, 1);
EXPECT_EQ(time_msg.nanosec, 0u);
EXPECT_EQ(rclcpp::Time(time_msg).nanoseconds(), ONE_SEC_IN_NS);
}
{
const rclcpp::Time time(ONE_AND_HALF_SEC_IN_NS);
auto time_msg = static_cast<builtin_interfaces::msg::Time>(time);
EXPECT_EQ(time_msg.sec, 1);
EXPECT_EQ(time_msg.nanosec, HALF_SEC_IN_NS);
EXPECT_EQ(rclcpp::Time(time_msg).nanoseconds(), ONE_AND_HALF_SEC_IN_NS);
}
{
// Can rclcpp::Time be negative or not? The following constructor works:
rclcpp::Time time(-HALF_SEC_IN_NS);
auto time_msg = static_cast<builtin_interfaces::msg::Time>(time);
EXPECT_EQ(time_msg.sec, -1);
EXPECT_EQ(time_msg.nanosec, HALF_SEC_IN_NS);
// The opposite conversion throws...
EXPECT_ANY_THROW(
{
rclcpp::Time negative_time(time_msg);
});
}
{
// Can rclcpp::Time be negative or not? The following constructor works:
rclcpp::Time time(-ONE_SEC_IN_NS);
auto time_msg = static_cast<builtin_interfaces::msg::Time>(time);
EXPECT_EQ(time_msg.sec, -1);
EXPECT_EQ(time_msg.nanosec, 0u);
// The opposite conversion throws...
EXPECT_ANY_THROW(
{
rclcpp::Time negative_time(time_msg);
});
}
{
// Can rclcpp::Time be negative or not? The following constructor works:
rclcpp::Time time(-ONE_AND_HALF_SEC_IN_NS);
auto time_msg = static_cast<builtin_interfaces::msg::Time>(time);
EXPECT_EQ(time_msg.sec, -2);
EXPECT_EQ(time_msg.nanosec, HALF_SEC_IN_NS);
// The opposite conversion throws...
EXPECT_ANY_THROW(
{
rclcpp::Time negative_time(time_msg);
});
}
}
TEST(TestTime, operators) {
TEST_F(TestTime, operators) {
rclcpp::Time old(1, 0);
rclcpp::Time young(2, 0);
@@ -121,7 +203,7 @@ TEST(TestTime, operators) {
EXPECT_TRUE(young != old);
rclcpp::Duration sub = young - old;
EXPECT_EQ(sub.nanoseconds(), (rcl_duration_value_t)(young.nanoseconds() - old.nanoseconds()));
EXPECT_EQ(sub.nanoseconds(), (young.nanoseconds() - old.nanoseconds()));
EXPECT_EQ(sub, young - old);
rclcpp::Time system_time(0, 0, RCL_SYSTEM_TIME);
@@ -160,7 +242,7 @@ TEST(TestTime, operators) {
}
}
TEST(TestTime, overflow_detectors) {
TEST_F(TestTime, overflow_detectors) {
/////////////////////////////////////////////////////////////////////////////
// Test logical_eq call first:
EXPECT_TRUE(logical_eq(false, false));
@@ -180,8 +262,8 @@ TEST(TestTime, overflow_detectors) {
// 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 big_type_t sum = static_cast<big_type_t>(x + y);
const big_type_t diff = static_cast<big_type_t>(x - y);
const bool add_will_overflow =
rclcpp::add_will_overflow(test_type_t(x), test_type_t(y));
@@ -217,7 +299,7 @@ TEST(TestTime, overflow_detectors) {
EXPECT_TRUE(rclcpp::sub_will_underflow<int64_t>(INT64_MIN, 1));
}
TEST(TestTime, overflows) {
TEST_F(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);
@@ -245,7 +327,7 @@ TEST(TestTime, overflows) {
EXPECT_NO_THROW(one_time - two_time);
}
TEST(TestTime, seconds) {
TEST_F(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());

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