Compare commits
445 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9171122eae | ||
|
|
ce13f1afba | ||
|
|
df08474d38 | ||
|
|
f9050cd666 | ||
|
|
33cbd76c07 | ||
|
|
ae8b033ae0 | ||
|
|
4fa3489cfd | ||
|
|
7f575103d8 | ||
|
|
166007dde3 | ||
|
|
cf2a27805e | ||
|
|
c24e485084 | ||
|
|
d99157d731 | ||
|
|
248d911ea5 | ||
|
|
76aae4f799 | ||
|
|
6815022909 | ||
|
|
85a7046ac3 | ||
|
|
6c06a29050 | ||
|
|
03fa731d23 | ||
|
|
0699aeb851 | ||
|
|
06e6da414a | ||
|
|
12de518956 | ||
|
|
eac0063176 | ||
|
|
492770c12f | ||
|
|
d302e3c628 | ||
|
|
011ea39e99 | ||
|
|
49c2dd4813 | ||
|
|
6afec4805c | ||
|
|
9ae35e347e | ||
|
|
0f58bb8700 | ||
|
|
8b9cabfb9d | ||
|
|
2e39e09803 | ||
|
|
91f0b64493 | ||
|
|
3bd6900f98 | ||
|
|
f2f7ffdf53 | ||
|
|
16914e31a1 | ||
|
|
4e3c6be76a | ||
|
|
79241a3cdc | ||
|
|
9c5ad79b63 | ||
|
|
d8e1aed520 | ||
|
|
c5a16dce11 | ||
|
|
32c03dde2d | ||
|
|
4f778199b4 | ||
|
|
025cd5ccc8 | ||
|
|
4c8cfa39f8 | ||
|
|
891fff0153 | ||
|
|
43db06dad7 | ||
|
|
0d1747e066 | ||
|
|
2520d398c7 | ||
|
|
d3c0049b24 | ||
|
|
c54a6f1cd2 | ||
|
|
9ec0393e63 | ||
|
|
bca3fd7da1 | ||
|
|
8afef51cfd | ||
|
|
3123f5a643 | ||
|
|
aa18ef51cc | ||
|
|
1688f05aea | ||
|
|
80f93d1dbb | ||
|
|
5e314c253e | ||
|
|
6b321edb4f | ||
|
|
8093648ee3 | ||
|
|
9583ec7855 | ||
|
|
2d6e6364cd | ||
|
|
5613d613cf | ||
|
|
7a2ee23281 | ||
|
|
802bfc2c74 | ||
|
|
152dbc6e38 | ||
|
|
9342c257b8 | ||
|
|
d2e563afd5 | ||
|
|
8ac848bbc2 | ||
|
|
b2b676d317 | ||
|
|
ee20dd31e6 | ||
|
|
0775e2f6e7 | ||
|
|
321c74c2b3 | ||
|
|
536df11ee0 | ||
|
|
6f6e9e8ce9 | ||
|
|
39dad4d9a7 | ||
|
|
87d754b219 | ||
|
|
e03e98220d | ||
|
|
f7bb88fc8f | ||
|
|
0781ea543c | ||
|
|
e0e96681d9 | ||
|
|
b1f31e0eaa | ||
|
|
e2aeb1028b | ||
|
|
94264320b4 | ||
|
|
b135e89c1e | ||
|
|
c59793618a | ||
|
|
5ecc5b6c19 | ||
|
|
2d7bd9f4cb | ||
|
|
0fd866d201 | ||
|
|
82950f1141 | ||
|
|
4a343a1f23 | ||
|
|
a569214273 | ||
|
|
942b74c8bd | ||
|
|
d107a844ea | ||
|
|
301957515a | ||
|
|
d04319a438 | ||
|
|
d5ec258080 | ||
|
|
9e445bdb91 | ||
|
|
fa3a6fa597 | ||
|
|
81df5843f3 | ||
|
|
f3a5187775 | ||
|
|
2801553d61 | ||
|
|
001f0fb620 | ||
|
|
665e37784a | ||
|
|
ecb81ef2c3 | ||
|
|
d0cd6bb0a4 | ||
|
|
fd08f0dbe7 | ||
|
|
2dd09ae274 | ||
|
|
679fb2ba33 | ||
|
|
4fcd05db72 | ||
|
|
d7764b4322 | ||
|
|
893b9b4f82 | ||
|
|
b918bd4c25 | ||
|
|
3b1144f1e0 | ||
|
|
6c0a46bcc8 | ||
|
|
3cddb4edab | ||
|
|
d5f3d35fbe | ||
|
|
bf752c75f5 | ||
|
|
01f6ebdd3d | ||
|
|
7bf52dd8a6 | ||
|
|
1b28f389c2 | ||
|
|
e8cbfe6a1b | ||
|
|
0c01a43a4f | ||
|
|
133088e9a3 | ||
|
|
3d42c9a5df | ||
|
|
5c4f809f2a | ||
|
|
f7a301441a | ||
|
|
00f2d563be | ||
|
|
64ee7d6822 | ||
|
|
0750dc418a | ||
|
|
0d6d9e6778 | ||
|
|
86c079de31 | ||
|
|
fb8519070c | ||
|
|
e1095adeee | ||
|
|
4ecb3dd090 | ||
|
|
0034929eef | ||
|
|
dbb717cd6e | ||
|
|
e9e398d2d4 | ||
|
|
7d8b2690ff | ||
|
|
55d2f67b90 | ||
|
|
39bfb30eb0 | ||
|
|
1c61751540 | ||
|
|
0251f70fe8 | ||
|
|
07b6ea0ff4 | ||
|
|
56f68f9c44 | ||
|
|
18fcd9138b | ||
|
|
f245b4cc81 | ||
|
|
d488535f36 | ||
|
|
72443ff295 | ||
|
|
a3383c309a | ||
|
|
5dad229662 | ||
|
|
2e4c97ac56 | ||
|
|
0659d829ce | ||
|
|
1fc2d58799 | ||
|
|
c15eb1eaac | ||
|
|
d051b8aa20 | ||
|
|
6806cdf825 | ||
|
|
79c2dd8e8b | ||
|
|
fba080cf34 | ||
|
|
bff6916e8f | ||
|
|
b8df9347a1 | ||
|
|
ec70642c55 | ||
|
|
14aba06922 | ||
|
|
1c2bd84725 | ||
|
|
c4a68b4199 | ||
|
|
085f161230 | ||
|
|
893679e44f | ||
|
|
a62287bf8d | ||
|
|
98ab933a73 | ||
|
|
a9c6521466 | ||
|
|
41fedb7beb | ||
|
|
98fc7fecc2 | ||
|
|
f9f90e0226 | ||
|
|
61fcc766f8 | ||
|
|
091a8bcf86 | ||
|
|
6af478d0ba | ||
|
|
06a4ee01d4 | ||
|
|
7b94f288e5 | ||
|
|
7226725d2f | ||
|
|
1037822a63 | ||
|
|
95adde2a19 | ||
|
|
cd3fd53c8c | ||
|
|
70dfa2e778 | ||
|
|
e7e3504fcf | ||
|
|
474720511e | ||
|
|
51a4b2155e | ||
|
|
aa3a65d3ff | ||
|
|
f986ca3edc | ||
|
|
2562f715ab | ||
|
|
3ab6571593 | ||
|
|
bc8c71b63f | ||
|
|
63e79223f9 | ||
|
|
92ece94ca3 | ||
|
|
a7ec7d9243 | ||
|
|
06a4a56017 | ||
|
|
98a62f6a18 | ||
|
|
dbb4c354d6 | ||
|
|
0088e35052 | ||
|
|
3fa511a8a0 | ||
|
|
763c56481f | ||
|
|
63fdf82ebf | ||
|
|
052596c971 | ||
|
|
182ad3860e | ||
|
|
59ad83ab5a | ||
|
|
cae3836ca0 | ||
|
|
75051616f1 | ||
|
|
f5afe380d5 | ||
|
|
7bfda87d32 | ||
|
|
e33105057c | ||
|
|
736550cace | ||
|
|
35c89c8afc | ||
|
|
37ee1ff309 | ||
|
|
d01a577f87 | ||
|
|
6cc89caa63 | ||
|
|
c767f0b4c4 | ||
|
|
13312dc6ed | ||
|
|
e11986bbdb | ||
|
|
c8713edbe4 | ||
|
|
5a832e4db0 | ||
|
|
267786207b | ||
|
|
b9ffd72f42 | ||
|
|
dc7e95aaa5 | ||
|
|
ee7080d95d | ||
|
|
b9fb9bda3d | ||
|
|
24bb65305d | ||
|
|
ab743392e4 | ||
|
|
18b112fcce | ||
|
|
09950ba23f | ||
|
|
377fc4f076 | ||
|
|
c2a75f0b5b | ||
|
|
bce19675a2 | ||
|
|
5d6e48c800 | ||
|
|
7c984f1a4c | ||
|
|
9bf0f8374e | ||
|
|
1c92e6d609 | ||
|
|
490e12ea86 | ||
|
|
900be20a5a | ||
|
|
963b5bbea3 | ||
|
|
a2fc45f955 | ||
|
|
4da9a0c2cd | ||
|
|
83af414811 | ||
|
|
a7500478d8 | ||
|
|
b1ff2d5bdc | ||
|
|
f38cd8a8bb | ||
|
|
064a021c7a | ||
|
|
5cb2274e8c | ||
|
|
ca3ad7da2f | ||
|
|
7ccd64c9c1 | ||
|
|
8d2c682c09 | ||
|
|
56a037a3da | ||
|
|
8d5af66858 | ||
|
|
438822fe13 | ||
|
|
7a31d7c01b | ||
|
|
700e3c47bf | ||
|
|
069b20b7af | ||
|
|
bcceecb24b | ||
|
|
7ed61e52fe | ||
|
|
85c32a94a5 | ||
|
|
5821361dcb | ||
|
|
b9ef1fedfa | ||
|
|
adebda52c5 | ||
|
|
715b0e9008 | ||
|
|
6257103e76 | ||
|
|
543a3c39c1 | ||
|
|
62c958be30 | ||
|
|
febe86e6b5 | ||
|
|
504b082d8b | ||
|
|
9c62c1c946 | ||
|
|
5b6e0af339 | ||
|
|
c64ba72bbd | ||
|
|
eddb938aec | ||
|
|
0dc782aca8 | ||
|
|
ea0ee50318 | ||
|
|
35c73aa61e | ||
|
|
08963df926 | ||
|
|
f5e35bda86 | ||
|
|
a7db1dcca2 | ||
|
|
ba1056fe63 | ||
|
|
048d9b57c0 | ||
|
|
de353f9e45 | ||
|
|
cba6f20988 | ||
|
|
71a58d40f1 | ||
|
|
add6d61231 | ||
|
|
8c8c268aad | ||
|
|
27d1b11647 | ||
|
|
4c5986aa2d | ||
|
|
dd0f97f179 | ||
|
|
7d257177e0 | ||
|
|
a95efa452e | ||
|
|
06465ba827 | ||
|
|
5fe6840ad1 | ||
|
|
361be5e4c0 | ||
|
|
58bd8d6c21 | ||
|
|
3b04b056e3 | ||
|
|
0aa416e17f | ||
|
|
79403119e4 | ||
|
|
3ae5170b52 | ||
|
|
2309811814 | ||
|
|
aa159a5e8f | ||
|
|
8a8e46d7e9 | ||
|
|
1ddc8c815c | ||
|
|
579e9d01d6 | ||
|
|
371074523a | ||
|
|
eb7c46ea43 | ||
|
|
0810140e18 | ||
|
|
5d9db5de74 | ||
|
|
8e5ddb1f81 | ||
|
|
018cfaa219 | ||
|
|
31c202e325 | ||
|
|
b5b87824ff | ||
|
|
8d50bb3123 | ||
|
|
c88cc649d3 | ||
|
|
94d17d3963 | ||
|
|
7c929473a4 | ||
|
|
5851eebdda | ||
|
|
c4c18d592a | ||
|
|
90ef1e1f9c | ||
|
|
dd21615288 | ||
|
|
bf660c543d | ||
|
|
d9377dc740 | ||
|
|
99d76be3a5 | ||
|
|
a37df26a9f | ||
|
|
8581c24d89 | ||
|
|
78a3354a06 | ||
|
|
611856225b | ||
|
|
47fdb6326a | ||
|
|
5acb775278 | ||
|
|
d077e9c610 | ||
|
|
049dc286c4 | ||
|
|
4a6e5e4d6b | ||
|
|
677af44910 | ||
|
|
bd214d3b65 | ||
|
|
e73b613a01 | ||
|
|
43c07027bb | ||
|
|
0b54476ff7 | ||
|
|
1b652841c6 | ||
|
|
a9add88c2a | ||
|
|
554d933f51 | ||
|
|
4a2231d7a5 | ||
|
|
869f3ed873 | ||
|
|
175bc64d54 | ||
|
|
db65f8004e | ||
|
|
3b1a5c32b9 | ||
|
|
bb3debcfba | ||
|
|
3b71ca627c | ||
|
|
3896d27c7c | ||
|
|
3e7d6bca4f | ||
|
|
148d295416 | ||
|
|
ef9c1c4652 | ||
|
|
9549369005 | ||
|
|
e6a258e9f3 | ||
|
|
939a5a591e | ||
|
|
8c1d829e72 | ||
|
|
acf6971086 | ||
|
|
eb9fc5f139 | ||
|
|
31ae9c6a60 | ||
|
|
94c4d7fb0b | ||
|
|
2531787550 | ||
|
|
974772e2ab | ||
|
|
3defa8fc9d | ||
|
|
a855a7d29b | ||
|
|
3a4ac0ca20 | ||
|
|
bf1396b272 | ||
|
|
e62f3280f5 | ||
|
|
10fbde8062 | ||
|
|
14c53e117b | ||
|
|
374deb9191 | ||
|
|
d0d12f77d7 | ||
|
|
1be58c057d | ||
|
|
29c48a4a98 | ||
|
|
0e0a6a495c | ||
|
|
0313417f02 | ||
|
|
180a596f7f | ||
|
|
7b2d983734 | ||
|
|
0276809f1a | ||
|
|
3ee59c0b30 | ||
|
|
d66cd96f25 | ||
|
|
3f0f2e28c3 | ||
|
|
01d6f52e32 | ||
|
|
633e1157f8 | ||
|
|
3270aad95e | ||
|
|
df3cfa7e4f | ||
|
|
96cccf5fde | ||
|
|
42682d1b66 | ||
|
|
b4b0afc267 | ||
|
|
a721d06ec5 | ||
|
|
6a3a5ed841 | ||
|
|
9b2e1a857e | ||
|
|
bbf2c4cbfb | ||
|
|
3733a1051a | ||
|
|
0e7fed993d | ||
|
|
f2cd2fbf0e | ||
|
|
7c84b724b4 | ||
|
|
61e59f846f | ||
|
|
d378cff7bf | ||
|
|
3b8c334d5d | ||
|
|
72fd2f57b2 | ||
|
|
7e68d3549c | ||
|
|
a0376768f7 | ||
|
|
b0754dacc5 | ||
|
|
f945d89aa8 | ||
|
|
3f1d2bdd7b | ||
|
|
e3490a29cd | ||
|
|
0986fbb0f7 | ||
|
|
b22574305c | ||
|
|
751727652d | ||
|
|
a640c3ea2e | ||
|
|
9a7e33f3b1 | ||
|
|
5499882773 | ||
|
|
4b57fb2b8e | ||
|
|
84ae5a1897 | ||
|
|
05e6b96847 | ||
|
|
860a9e0e4d | ||
|
|
f125c78fa8 | ||
|
|
a8cd936239 | ||
|
|
c92f3b7ff9 | ||
|
|
c0b96616bb | ||
|
|
55fccffc89 | ||
|
|
873a3d5cd0 | ||
|
|
a9d5a3feb9 | ||
|
|
c23572fa14 | ||
|
|
8245808c0e | ||
|
|
35c27e8250 | ||
|
|
8a62104ea7 | ||
|
|
c63060c42d | ||
|
|
2a7b722d5f | ||
|
|
a6741a4f8e | ||
|
|
40f0040f0d | ||
|
|
4f14a0cc14 | ||
|
|
83b086a9ef | ||
|
|
2ed85420d5 | ||
|
|
cc65905efa | ||
|
|
88768eaabd | ||
|
|
696d9ed1be | ||
|
|
74daff052b | ||
|
|
a667583821 | ||
|
|
552c1a8deb | ||
|
|
5cecbf99bb | ||
|
|
f703314f95 | ||
|
|
0fa68d54e7 | ||
|
|
cbde45481e | ||
|
|
2a653c47f8 | ||
|
|
bba9dce253 | ||
|
|
898a30e0e2 | ||
|
|
6e8aaa2ae6 |
@@ -12,6 +12,6 @@ Visit the [rclcpp API documentation](http://docs.ros2.org/latest/api/rclcpp/) fo
|
||||
|
||||
### Examples
|
||||
|
||||
The ROS 2 tutorials [Writing a simple publisher and subscriber](https://index.ros.org/doc/ros2/Tutorials/Writing-A-Simple-Cpp-Publisher-And-Subscriber/)
|
||||
and [Writing a simple service and client](https://index.ros.org/doc/ros2/Tutorials/Writing-A-Simple-Cpp-Service-And-Client/)
|
||||
The ROS 2 tutorials [Writing a simple publisher and subscriber](https://docs.ros.org/en/rolling/Tutorials/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html).
|
||||
and [Writing a simple service and client](https://docs.ros.org/en/rolling/Tutorials/Writing-A-Simple-Cpp-Service-And-Client.html)
|
||||
contain some examples of rclcpp APIs in use.
|
||||
|
||||
@@ -2,6 +2,479 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
16.0.3 (2023-01-10)
|
||||
-------------------
|
||||
* Fix SharedFuture from async_send_request never becomes valid (`#2044 <https://github.com/ros2/rclcpp/issues/2044>`_) (`#2076 <https://github.com/ros2/rclcpp/issues/2076>`_)
|
||||
* do not throw exception if trying to dequeue an empty intra-process buffer (`#2061 <https://github.com/ros2/rclcpp/issues/2061>`_) (`#2070 <https://github.com/ros2/rclcpp/issues/2070>`_)
|
||||
* fix nullptr dereference in prune_requests_older_than (`#2008 <https://github.com/ros2/rclcpp/issues/2008>`_) (`#2065 <https://github.com/ros2/rclcpp/issues/2065>`_)
|
||||
* Fix bug that a callback not reached (`#1640 <https://github.com/ros2/rclcpp/issues/1640>`_) (`#2033 <https://github.com/ros2/rclcpp/issues/2033>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.2 (2022-11-07)
|
||||
-------------------
|
||||
* fix mismatched issue if using zero_allocate (`#1995 <https://github.com/ros2/rclcpp/issues/1995>`_) (`#2026 <https://github.com/ros2/rclcpp/issues/2026>`_)
|
||||
* use regex for wildcard matching (backport `#1839 <https://github.com/ros2/rclcpp/issues/1839>`_) (`#1986 <https://github.com/ros2/rclcpp/issues/1986>`_)
|
||||
* Drop wrong template specialization (`#1926 <https://github.com/ros2/rclcpp/issues/1926>`_) (`#1937 <https://github.com/ros2/rclcpp/issues/1937>`_)
|
||||
* Add statistics for handle_loaned_message (`#1927 <https://github.com/ros2/rclcpp/issues/1927>`_) (`#1932 <https://github.com/ros2/rclcpp/issues/1932>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.1 (2022-04-13)
|
||||
-------------------
|
||||
* remove DEFINE_CONTENT_FILTER cmake option (`#1914 <https://github.com/ros2/rclcpp/issues/1914>`_)
|
||||
* Contributors: Chen Lihui
|
||||
|
||||
16.0.0 (2022-04-08)
|
||||
-------------------
|
||||
* remove things that were deprecated during galactic (`#1913 <https://github.com/ros2/rclcpp/issues/1913>`_)
|
||||
* Contributors: William Woodall
|
||||
|
||||
15.4.0 (2022-04-05)
|
||||
-------------------
|
||||
* add take_data_by_entity_id API to waitable (`#1892 <https://github.com/ros2/rclcpp/issues/1892>`_)
|
||||
* add content-filtered-topic interfaces (`#1561 <https://github.com/ros2/rclcpp/issues/1561>`_)
|
||||
* Contributors: Alberto Soragna, Chen Lihui
|
||||
|
||||
15.3.0 (2022-03-30)
|
||||
-------------------
|
||||
* [NodeParameters] Set name in param info pre-check (`#1908 <https://github.com/ros2/rclcpp/issues/1908>`_)
|
||||
* Add test-dep ament_cmake_google_benchmark (`#1904 <https://github.com/ros2/rclcpp/issues/1904>`_)
|
||||
* Add publish by loaned message in GenericPublisher (`#1856 <https://github.com/ros2/rclcpp/issues/1856>`_)
|
||||
* Contributors: Abrar Rahman Protyasha, Barry Xu, Gaël Écorchard
|
||||
|
||||
15.2.0 (2022-03-24)
|
||||
-------------------
|
||||
* Add missing ament dependency on rcl_interfaces (`#1903 <https://github.com/ros2/rclcpp/issues/1903>`_)
|
||||
* Update data callback tests to account for all published samples (`#1900 <https://github.com/ros2/rclcpp/issues/1900>`_)
|
||||
* Increase timeout for acknowledgments to account for slower Connext settings (`#1901 <https://github.com/ros2/rclcpp/issues/1901>`_)
|
||||
* clang-tidy: explicit constructors (`#1782 <https://github.com/ros2/rclcpp/issues/1782>`_)
|
||||
* Add client/service QoS getters (`#1784 <https://github.com/ros2/rclcpp/issues/1784>`_)
|
||||
* Fix a bunch more rosdoc2 issues in rclcpp. (`#1897 <https://github.com/ros2/rclcpp/issues/1897>`_)
|
||||
* time_until_trigger returns max time if timer is cancelled (`#1893 <https://github.com/ros2/rclcpp/issues/1893>`_)
|
||||
* Micro-optimizations in rclcpp (`#1896 <https://github.com/ros2/rclcpp/issues/1896>`_)
|
||||
* Contributors: Andrea Sorbini, Chris Lalancette, Mauro Passerino, Scott K Logan, William Woodall
|
||||
|
||||
15.1.0 (2022-03-01)
|
||||
-------------------
|
||||
* spin_all with a zero timeout. (`#1878 <https://github.com/ros2/rclcpp/issues/1878>`_)
|
||||
* Add RMW listener APIs (`#1579 <https://github.com/ros2/rclcpp/issues/1579>`_)
|
||||
* Remove fastrtps customization on tests (`#1887 <https://github.com/ros2/rclcpp/issues/1887>`_)
|
||||
* Install headers to include/${PROJECT_NAME} (`#1888 <https://github.com/ros2/rclcpp/issues/1888>`_)
|
||||
* Use ament_generate_version_header (`#1886 <https://github.com/ros2/rclcpp/issues/1886>`_)
|
||||
* use universal reference to support rvalue. (`#1883 <https://github.com/ros2/rclcpp/issues/1883>`_)
|
||||
* fix one subscription can wait_for_message twice (`#1870 <https://github.com/ros2/rclcpp/issues/1870>`_)
|
||||
* Add return value version of get_parameter_or (`#1813 <https://github.com/ros2/rclcpp/issues/1813>`_)
|
||||
* Cleanup time source object lifetimes (`#1867 <https://github.com/ros2/rclcpp/issues/1867>`_)
|
||||
* add is_spinning() method to executor base class
|
||||
* Contributors: Alberto Soragna, Chen Lihui, Chris Lalancette, Kenji Miyake, Miguel Company, Shane Loretz, Tomoya Fujita, iRobot ROS
|
||||
|
||||
15.0.0 (2022-01-14)
|
||||
-------------------
|
||||
* Cleanup the TypeAdapt tests (`#1858 <https://github.com/ros2/rclcpp/issues/1858>`_)
|
||||
* Cleanup includes (`#1857 <https://github.com/ros2/rclcpp/issues/1857>`_)
|
||||
* Fix include order and relative paths for cpplint (`#1859 <https://github.com/ros2/rclcpp/issues/1859>`_)
|
||||
* Rename stringstream in macros to a more unique name (`#1862 <https://github.com/ros2/rclcpp/issues/1862>`_)
|
||||
* Add non transform capabilities for intra-process (`#1849 <https://github.com/ros2/rclcpp/issues/1849>`_)
|
||||
* Fix rclcpp documentation build (`#1779 <https://github.com/ros2/rclcpp/issues/1779>`_)
|
||||
* Contributors: Chris Lalancette, Doug Smith, Gonzo, Jacob Perron, Michel Hidalgo
|
||||
|
||||
14.1.0 (2022-01-05)
|
||||
-------------------
|
||||
* Use UninitializedStaticallyTypedParameterException (`#1689 <https://github.com/ros2/rclcpp/issues/1689>`_)
|
||||
* Add wait_for_all_acked support (`#1662 <https://github.com/ros2/rclcpp/issues/1662>`_)
|
||||
* Add tests for function templates of declare_parameter (`#1747 <https://github.com/ros2/rclcpp/issues/1747>`_)
|
||||
* Contributors: Barry Xu, Bi0T1N, M. Mostafa Farzan
|
||||
|
||||
14.0.0 (2021-12-17)
|
||||
-------------------
|
||||
* Fixes for uncrustify 0.72 (`#1844 <https://github.com/ros2/rclcpp/issues/1844>`_)
|
||||
* use private member to keep the all reference underneath. (`#1845 <https://github.com/ros2/rclcpp/issues/1845>`_)
|
||||
* Make node base sharable (`#1832 <https://github.com/ros2/rclcpp/issues/1832>`_)
|
||||
* Add Clock::sleep_for() (`#1828 <https://github.com/ros2/rclcpp/issues/1828>`_)
|
||||
* Synchronize rcl and std::chrono steady clocks in Clock::sleep_until (`#1830 <https://github.com/ros2/rclcpp/issues/1830>`_)
|
||||
* Use rclcpp::guard_condition (`#1612 <https://github.com/ros2/rclcpp/issues/1612>`_)
|
||||
* Call CMake function to generate version header (`#1805 <https://github.com/ros2/rclcpp/issues/1805>`_)
|
||||
* Use parantheses around logging macro parameter (`#1820 <https://github.com/ros2/rclcpp/issues/1820>`_)
|
||||
* Remove author by request (`#1818 <https://github.com/ros2/rclcpp/issues/1818>`_)
|
||||
* Update maintainers (`#1817 <https://github.com/ros2/rclcpp/issues/1817>`_)
|
||||
* min_forward & min_backward thresholds must not be disabled (`#1815 <https://github.com/ros2/rclcpp/issues/1815>`_)
|
||||
* Re-add Clock::sleep_until (`#1814 <https://github.com/ros2/rclcpp/issues/1814>`_)
|
||||
* Fix lifetime of context so it remains alive while its dependent node handles are still in use (`#1754 <https://github.com/ros2/rclcpp/issues/1754>`_)
|
||||
* Add the interface for pre-shutdown callback (`#1714 <https://github.com/ros2/rclcpp/issues/1714>`_)
|
||||
* Take message ownership from moved LoanedMessage (`#1808 <https://github.com/ros2/rclcpp/issues/1808>`_)
|
||||
* Suppress clang dead-store warnings in the benchmarks. (`#1802 <https://github.com/ros2/rclcpp/issues/1802>`_)
|
||||
* Wait for publisher and subscription to match (`#1777 <https://github.com/ros2/rclcpp/issues/1777>`_)
|
||||
* Fix unused QoS profile for clock subscription and make ClockQoS the default (`#1801 <https://github.com/ros2/rclcpp/issues/1801>`_)
|
||||
* Contributors: Abrar Rahman Protyasha, Barry Xu, Chen Lihui, Chris Lalancette, Grey, Jacob Perron, Nikolai Morin, Shane Loretz, Tomoya Fujita, mauropasse
|
||||
|
||||
13.1.0 (2021-10-18)
|
||||
-------------------
|
||||
* Fix dangerous std::bind capture in TimeSource implementation. (`#1768 <https://github.com/ros2/rclcpp/issues/1768>`_)
|
||||
* Fix dangerous std::bind capture in ParameterEventHandler implementation. (`#1770 <https://github.com/ros2/rclcpp/issues/1770>`_)
|
||||
* Handle sigterm, in the same way sigint is being handled. (`#1771 <https://github.com/ros2/rclcpp/issues/1771>`_)
|
||||
* rclcpp::Node copy constructor: make copy of node_waitables\_ member. (`#1799 <https://github.com/ros2/rclcpp/issues/1799>`_)
|
||||
* Extend NodeGraph to match what rcl provides. (`#1484 <https://github.com/ros2/rclcpp/issues/1484>`_)
|
||||
* Context::sleep_for(): replace recursion with do-while to avoid potential stack-overflow. (`#1765 <https://github.com/ros2/rclcpp/issues/1765>`_)
|
||||
* extend_sub_namespace(): Verify string::empty() before calling string::front(). (`#1764 <https://github.com/ros2/rclcpp/issues/1764>`_)
|
||||
* Deprecate the `void shared_ptr<MessageT>` subscription callback signatures. (`#1713 <https://github.com/ros2/rclcpp/issues/1713>`_)
|
||||
* Contributors: Abrar Rahman Protyasha, Chris Lalancette, Emerson Knapp, Geoffrey Biggs, Ivan Santiago Paunovic, Jorge Perez, Tomoya Fujita, William Woodall, Yong-Hao Zou, livanov93
|
||||
|
||||
13.0.0 (2021-08-23)
|
||||
-------------------
|
||||
* Remove can_be_nullptr assignment check for QNX case. (`#1752 <https://github.com/ros2/rclcpp/issues/1752>`_)
|
||||
* Update client API to be able to remove pending requests. (`#1734 <https://github.com/ros2/rclcpp/issues/1734>`_)
|
||||
* Fix: Allow to add a node while spinning in the StaticSingleThreadedExecutor. (`#1690 <https://github.com/ros2/rclcpp/issues/1690>`_)
|
||||
* Add tracing instrumentation for executor and message taking. (`#1738 <https://github.com/ros2/rclcpp/issues/1738>`_)
|
||||
* Fix: Reset timer trigger time before execute in StaticSingleThreadedExecutor. (`#1739 <https://github.com/ros2/rclcpp/issues/1739>`_)
|
||||
* Use FindPython3 and make python3 dependency explicit. (`#1745 <https://github.com/ros2/rclcpp/issues/1745>`_)
|
||||
* Use rosidl_get_typesupport_target(). (`#1729 <https://github.com/ros2/rclcpp/issues/1729>`_)
|
||||
* Fix returning invalid namespace if sub_namespace is empty. (`#1658 <https://github.com/ros2/rclcpp/issues/1658>`_)
|
||||
* Add free function to wait for a subscription message. (`#1705 <https://github.com/ros2/rclcpp/issues/1705>`_)
|
||||
* Use rcpputils/scope_exit.hpp and remove rclcpp/scope_exit.hpp. (`#1727 <https://github.com/ros2/rclcpp/issues/1727>`_)
|
||||
* Contributors: Ahmed Sobhy, Christophe Bedard, Ivan Santiago Paunovic, Karsten Knese, M. Hofstätter, Mauro Passerino, Shane Loretz, mauropasse
|
||||
|
||||
12.0.0 (2021-07-26)
|
||||
-------------------
|
||||
* Remove unsafe get_callback_groups API.
|
||||
Callers should change to using for_each_callback_group(), or
|
||||
store the callback groups they need internally.
|
||||
* Add in callback_groups_for_each.
|
||||
The main reason to add this method in is to make accesses to the
|
||||
callback_groups\_ vector thread-safe. By having a
|
||||
callback_groups_for_each that accepts a std::function, we can
|
||||
just have the callers give us the callback they are interested
|
||||
in, and we can take care of the locking.
|
||||
The rest of this fairly large PR is cleaning up all of the places
|
||||
that use get_callback_groups() to instead use
|
||||
callback_groups_for_each().
|
||||
* Use a different mechanism to avoid timers being scheduled multiple times by the MultiThreadedExecutor (`#1692 <https://github.com/ros2/rclcpp/issues/1692>`_)
|
||||
* Fix windows CI (`#1726 <https://github.com/ros2/rclcpp/issues/1726>`_)
|
||||
Fix bug in AnyServiceCallback introduced in `#1709 <https://github.com/ros2/rclcpp/issues/1709>`_.
|
||||
* Contributors: Chris Lalancette, Ivan Santiago Paunovic
|
||||
|
||||
11.2.0 (2021-07-21)
|
||||
-------------------
|
||||
* Support to defer to send a response in services. (`#1709 <https://github.com/ros2/rclcpp/issues/1709>`_)
|
||||
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
|
||||
* Fix documentation bug. (`#1719 <https://github.com/ros2/rclcpp/issues/1719>`_)
|
||||
Signed-off-by: William Woodall <william@osrfoundation.org>
|
||||
* Contributors: Ivan Santiago Paunovic, William Woodall
|
||||
|
||||
11.1.0 (2021-07-13)
|
||||
-------------------
|
||||
* Removed left over ``is_initialized()`` implementation (`#1711 <https://github.com/ros2/rclcpp/issues/1711>`_)
|
||||
Leftover from https://github.com/ros2/rclcpp/pull/1622
|
||||
* Fixed declare parameter methods for int and float vectors (`#1696 <https://github.com/ros2/rclcpp/issues/1696>`_)
|
||||
* Cleaned up implementation of the intra-process manager (`#1695 <https://github.com/ros2/rclcpp/issues/1695>`_)
|
||||
* Added the node name to an executor ``runtime_error`` (`#1686 <https://github.com/ros2/rclcpp/issues/1686>`_)
|
||||
* Fixed a typo "Attack" -> "Attach" (`#1687 <https://github.com/ros2/rclcpp/issues/1687>`_)
|
||||
* Removed use of std::allocator<>::rebind (`#1678 <https://github.com/ros2/rclcpp/issues/1678>`_)
|
||||
rebind is deprecated in c++17 and removed in c++20
|
||||
* Contributors: Alberto Soragna, Chen Lihui, Chris Lalancette, Petter Nilsson, Steve Macenski, William Woodall
|
||||
|
||||
11.0.0 (2021-05-18)
|
||||
-------------------
|
||||
* Allow declare uninitialized parameters (`#1673 <https://github.com/ros2/rclcpp/issues/1673>`_)
|
||||
* Fix syntax issue with gcc (`#1674 <https://github.com/ros2/rclcpp/issues/1674>`_)
|
||||
* [service] Don't use a weak_ptr to avoid leaking (`#1668 <https://github.com/ros2/rclcpp/issues/1668>`_)
|
||||
* Contributors: Ivan Santiago Paunovic, Jacob Perron, William Woodall
|
||||
|
||||
10.0.0 (2021-05-11)
|
||||
-------------------
|
||||
* Fix doc typo (`#1663 <https://github.com/ros2/rclcpp/issues/1663>`_)
|
||||
* [rclcpp] Type Adaptation feature (`#1557 <https://github.com/ros2/rclcpp/issues/1557>`_)
|
||||
* Do not attempt to use void allocators for memory allocation. (`#1657 <https://github.com/ros2/rclcpp/issues/1657>`_)
|
||||
* Keep custom allocator in publisher and subscription options alive. (`#1647 <https://github.com/ros2/rclcpp/issues/1647>`_)
|
||||
* Fix get_publishers_subscriptions_info_by_topic test in test_node.cpp (`#1648 <https://github.com/ros2/rclcpp/issues/1648>`_)
|
||||
* Use OnShutdown callback handle instead of OnShutdown callback (`#1639 <https://github.com/ros2/rclcpp/issues/1639>`_)
|
||||
* use dynamic_pointer_cast to detect allocator mismatch in intra process manager (`#1643 <https://github.com/ros2/rclcpp/issues/1643>`_)
|
||||
* Increase cppcheck timeout to 500s (`#1634 <https://github.com/ros2/rclcpp/issues/1634>`_)
|
||||
* Clarify node parameters docs (`#1631 <https://github.com/ros2/rclcpp/issues/1631>`_)
|
||||
* Contributors: Audrow Nash, Barry Xu, Jacob Perron, Michel Hidalgo, Shane Loretz, William Woodall
|
||||
|
||||
9.0.2 (2021-04-14)
|
||||
------------------
|
||||
* Avoid returning loan when none was obtained. (`#1629 <https://github.com/ros2/rclcpp/issues/1629>`_)
|
||||
* Use a different implementation of mutex two priorities (`#1628 <https://github.com/ros2/rclcpp/issues/1628>`_)
|
||||
* Do not test the value of the history policy when testing the get_publishers/subscriptions_info_by_topic() methods (`#1626 <https://github.com/ros2/rclcpp/issues/1626>`_)
|
||||
* Check first parameter type and range before calling the user validation callbacks (`#1627 <https://github.com/ros2/rclcpp/issues/1627>`_)
|
||||
* Contributors: Ivan Santiago Paunovic, Miguel Company
|
||||
|
||||
9.0.1 (2021-04-12)
|
||||
------------------
|
||||
* Restore test exception for Connext (`#1625 <https://github.com/ros2/rclcpp/issues/1625>`_)
|
||||
* Fix race condition in TimeSource clock thread setup (`#1623 <https://github.com/ros2/rclcpp/issues/1623>`_)
|
||||
* Contributors: Andrea Sorbini, Michel Hidalgo
|
||||
|
||||
9.0.0 (2021-04-06)
|
||||
------------------
|
||||
* remove deprecated code which was deprecated in foxy and should be removed in galactic (`#1622 <https://github.com/ros2/rclcpp/issues/1622>`_)
|
||||
* Change index.ros.org -> docs.ros.org. (`#1620 <https://github.com/ros2/rclcpp/issues/1620>`_)
|
||||
* Unique network flows (`#1496 <https://github.com/ros2/rclcpp/issues/1496>`_)
|
||||
* Add spin_some support to the StaticSingleThreadedExecutor (`#1338 <https://github.com/ros2/rclcpp/issues/1338>`_)
|
||||
* Add publishing instrumentation (`#1600 <https://github.com/ros2/rclcpp/issues/1600>`_)
|
||||
* Create load_parameters and delete_parameters methods (`#1596 <https://github.com/ros2/rclcpp/issues/1596>`_)
|
||||
* refactor AnySubscriptionCallback and add/deprecate callback signatures (`#1598 <https://github.com/ros2/rclcpp/issues/1598>`_)
|
||||
* Add generic publisher and generic subscription for serialized messages (`#1452 <https://github.com/ros2/rclcpp/issues/1452>`_)
|
||||
* use context from `node_base\_` for clock executor. (`#1617 <https://github.com/ros2/rclcpp/issues/1617>`_)
|
||||
* updating quality declaration links (re: `ros2/docs.ros2.org#52 <https://github.com/ros2/docs.ros2.org/issues/52>`_) (`#1615 <https://github.com/ros2/rclcpp/issues/1615>`_)
|
||||
* Contributors: Ananya Muddukrishna, BriceRenaudeau, Chris Lalancette, Christophe Bedard, Nikolai Morin, Tomoya Fujita, William Woodall, mauropasse, shonigmann
|
||||
|
||||
8.2.0 (2021-03-31)
|
||||
------------------
|
||||
* Initialize integers in test_parameter_event_handler.cpp to avoid undefined behavior (`#1609 <https://github.com/ros2/rclcpp/issues/1609>`_)
|
||||
* Namespace tracetools C++ functions (`#1608 <https://github.com/ros2/rclcpp/issues/1608>`_)
|
||||
* Revert "Namespace tracetools C++ functions (`#1603 <https://github.com/ros2/rclcpp/issues/1603>`_)" (`#1607 <https://github.com/ros2/rclcpp/issues/1607>`_)
|
||||
* Namespace tracetools C++ functions (`#1603 <https://github.com/ros2/rclcpp/issues/1603>`_)
|
||||
* Clock subscription callback group spins in its own thread (`#1556 <https://github.com/ros2/rclcpp/issues/1556>`_)
|
||||
* Contributors: Chris Lalancette, Christophe Bedard, Ivan Santiago Paunovic, anaelle-sw
|
||||
|
||||
8.1.0 (2021-03-25)
|
||||
------------------
|
||||
* Remove rmw_connext_cpp references. (`#1595 <https://github.com/ros2/rclcpp/issues/1595>`_)
|
||||
* Add API for checking QoS profile compatibility (`#1554 <https://github.com/ros2/rclcpp/issues/1554>`_)
|
||||
* Document misuse of parameters callback (`#1590 <https://github.com/ros2/rclcpp/issues/1590>`_)
|
||||
* use const auto & to iterate over parameters (`#1593 <https://github.com/ros2/rclcpp/issues/1593>`_)
|
||||
* Contributors: Chris Lalancette, Jacob Perron, Karsten Knese
|
||||
|
||||
8.0.0 (2021-03-23)
|
||||
------------------
|
||||
* Guard against integer overflow in duration conversion (`#1584 <https://github.com/ros2/rclcpp/issues/1584>`_)
|
||||
* Contributors: Jacob Perron
|
||||
|
||||
7.0.1 (2021-03-22)
|
||||
------------------
|
||||
* get_parameters service should return empty if undeclared parameters are allowed (`#1514 <https://github.com/ros2/rclcpp/issues/1514>`_)
|
||||
* Made 'Context::shutdown_reason' function a const function (`#1578 <https://github.com/ros2/rclcpp/issues/1578>`_)
|
||||
* Contributors: Tomoya Fujita, suab321321
|
||||
|
||||
7.0.0 (2021-03-18)
|
||||
------------------
|
||||
* Document design decisions that were made for statically typed parameters (`#1568 <https://github.com/ros2/rclcpp/issues/1568>`_)
|
||||
* Fix doc typo in CallbackGroup constructor (`#1582 <https://github.com/ros2/rclcpp/issues/1582>`_)
|
||||
* Enable qos parameter overrides for the /parameter_events topic (`#1532 <https://github.com/ros2/rclcpp/issues/1532>`_)
|
||||
* Add support for rmw_connextdds (`#1574 <https://github.com/ros2/rclcpp/issues/1574>`_)
|
||||
* Remove 'struct' from the rcl_time_jump_t. (`#1577 <https://github.com/ros2/rclcpp/issues/1577>`_)
|
||||
* Add tests for declaring statically typed parameters when undeclared parameters are allowed (`#1575 <https://github.com/ros2/rclcpp/issues/1575>`_)
|
||||
* Quiet clang memory leak warning on "DoNotOptimize". (`#1571 <https://github.com/ros2/rclcpp/issues/1571>`_)
|
||||
* Add ParameterEventsSubscriber class (`#829 <https://github.com/ros2/rclcpp/issues/829>`_)
|
||||
* When a parameter change is rejected, the parameters map shouldn't be updated. (`#1567 <https://github.com/ros2/rclcpp/pull/1567>`_)
|
||||
* Fix when to throw the NoParameterOverrideProvided exception. (`#1567 <https://github.com/ros2/rclcpp/pull/1567>`_)
|
||||
* Fix SEGV caused by order of destruction of Node sub-interfaces (`#1469 <https://github.com/ros2/rclcpp/issues/1469>`_)
|
||||
* Fix benchmark test failure introduced in `#1522 <https://github.com/ros2/rclcpp/issues/1522>`_ (`#1564 <https://github.com/ros2/rclcpp/issues/1564>`_)
|
||||
* Fix documented example in create_publisher (`#1558 <https://github.com/ros2/rclcpp/issues/1558>`_)
|
||||
* Enforce static parameter types (`#1522 <https://github.com/ros2/rclcpp/issues/1522>`_)
|
||||
* Allow timers to keep up the intended rate in MultiThreadedExecutor (`#1516 <https://github.com/ros2/rclcpp/issues/1516>`_)
|
||||
* Fix UBSAN warnings in any_subscription_callback. (`#1551 <https://github.com/ros2/rclcpp/issues/1551>`_)
|
||||
* Fix runtime error: reference binding to null pointer of type (`#1547 <https://github.com/ros2/rclcpp/issues/1547>`_)
|
||||
* Contributors: Andrea Sorbini, Chris Lalancette, Colin MacKenzie, Ivan Santiago Paunovic, Jacob Perron, Steven! Ragnarök, bpwilcox, tomoya
|
||||
|
||||
6.3.1 (2021-02-08)
|
||||
------------------
|
||||
* Reference test resources directly from source tree (`#1543 <https://github.com/ros2/rclcpp/issues/1543>`_)
|
||||
* clear statistics after window reset (`#1531 <https://github.com/ros2/rclcpp/issues/1531>`_) (`#1535 <https://github.com/ros2/rclcpp/issues/1535>`_)
|
||||
* Fix a minor string error in the topic_statistics test. (`#1541 <https://github.com/ros2/rclcpp/issues/1541>`_)
|
||||
* Avoid `Resource deadlock avoided` if use intra_process_comms (`#1530 <https://github.com/ros2/rclcpp/issues/1530>`_)
|
||||
* Avoid an object copy in parameter_value.cpp. (`#1538 <https://github.com/ros2/rclcpp/issues/1538>`_)
|
||||
* Assert that the publisher_list size is 1. (`#1537 <https://github.com/ros2/rclcpp/issues/1537>`_)
|
||||
* Don't access objects after they have been std::move (`#1536 <https://github.com/ros2/rclcpp/issues/1536>`_)
|
||||
* Update for checking correct variable (`#1534 <https://github.com/ros2/rclcpp/issues/1534>`_)
|
||||
* Destroy msg extracted from LoanedMessage. (`#1305 <https://github.com/ros2/rclcpp/issues/1305>`_)
|
||||
* Contributors: Chen Lihui, Chris Lalancette, Ivan Santiago Paunovic, Miaofei Mei, Scott K Logan, William Woodall, hsgwa
|
||||
|
||||
6.3.0 (2021-01-25)
|
||||
------------------
|
||||
* Add instrumentation for linking a timer to a node (`#1500 <https://github.com/ros2/rclcpp/issues/1500>`_)
|
||||
* Fix error when using IPC with StaticSingleThreadExecutor (`#1520 <https://github.com/ros2/rclcpp/issues/1520>`_)
|
||||
* Change to using unique_ptrs for DummyExecutor. (`#1517 <https://github.com/ros2/rclcpp/issues/1517>`_)
|
||||
* Allow reconfiguring 'clock' topic qos (`#1512 <https://github.com/ros2/rclcpp/issues/1512>`_)
|
||||
* Allow to add/remove nodes thread safely in rclcpp::Executor (`#1505 <https://github.com/ros2/rclcpp/issues/1505>`_)
|
||||
* Call rclcpp::shutdown in test_node for clean shutdown on Windows (`#1515 <https://github.com/ros2/rclcpp/issues/1515>`_)
|
||||
* Reapply "Add get_logging_directory method to rclcpp::Logger (`#1509 <https://github.com/ros2/rclcpp/issues/1509>`_)" (`#1513 <https://github.com/ros2/rclcpp/issues/1513>`_)
|
||||
* use describe_parameters of parameter client for test (`#1499 <https://github.com/ros2/rclcpp/issues/1499>`_)
|
||||
* Revert "Add get_logging_directory method to rclcpp::Logger (`#1509 <https://github.com/ros2/rclcpp/issues/1509>`_)" (`#1511 <https://github.com/ros2/rclcpp/issues/1511>`_)
|
||||
* Add get_logging_directory method to rclcpp::Logger (`#1509 <https://github.com/ros2/rclcpp/issues/1509>`_)
|
||||
* Contributors: Chris Lalancette, Christophe Bedard, Ivan Santiago Paunovic, eboasson, mauropasse, tomoya
|
||||
|
||||
6.2.0 (2021-01-08)
|
||||
------------------
|
||||
* Better documentation for the QoS class (`#1508 <https://github.com/ros2/rclcpp/issues/1508>`_)
|
||||
* Modify excluding callback duration from topic statistics (`#1492 <https://github.com/ros2/rclcpp/issues/1492>`_)
|
||||
* Make the test of graph users more robust. (`#1504 <https://github.com/ros2/rclcpp/issues/1504>`_)
|
||||
* Make sure to wait for graph change events in test_node_graph. (`#1503 <https://github.com/ros2/rclcpp/issues/1503>`_)
|
||||
* add timeout to SyncParametersClient methods (`#1493 <https://github.com/ros2/rclcpp/issues/1493>`_)
|
||||
* Fix wrong test expectations (`#1497 <https://github.com/ros2/rclcpp/issues/1497>`_)
|
||||
* Update create_publisher/subscription documentation, clarifying when a parameters interface is required (`#1494 <https://github.com/ros2/rclcpp/issues/1494>`_)
|
||||
* Fix string literal warnings (`#1442 <https://github.com/ros2/rclcpp/issues/1442>`_)
|
||||
* support describe_parameters methods to parameter client. (`#1453 <https://github.com/ros2/rclcpp/issues/1453>`_)
|
||||
* Contributors: Audrow Nash, Chris Lalancette, Ivan Santiago Paunovic, Nikolai Morin, hsgwa, tomoya
|
||||
|
||||
6.1.0 (2020-12-10)
|
||||
------------------
|
||||
* Add getters to rclcpp::qos and rclcpp::Policy enum classes (`#1467 <https://github.com/ros2/rclcpp/issues/1467>`_)
|
||||
* Change nullptr checks to use ASSERT_TRUE. (`#1486 <https://github.com/ros2/rclcpp/issues/1486>`_)
|
||||
* Adjust logic around finding and erasing guard_condition (`#1474 <https://github.com/ros2/rclcpp/issues/1474>`_)
|
||||
* Update QDs to QL 1 (`#1477 <https://github.com/ros2/rclcpp/issues/1477>`_)
|
||||
* Add performance tests for parameter transport (`#1463 <https://github.com/ros2/rclcpp/issues/1463>`_)
|
||||
* Contributors: Chris Lalancette, Ivan Santiago Paunovic, Scott K Logan, Stephen Brawner
|
||||
|
||||
6.0.0 (2020-11-18)
|
||||
------------------
|
||||
* Move ownership of shutdown_guard_condition to executors/graph_listener (`#1404 <https://github.com/ros2/rclcpp/issues/1404>`_)
|
||||
* Add options to automatically declare qos parameters when creating a publisher/subscription (`#1465 <https://github.com/ros2/rclcpp/issues/1465>`_)
|
||||
* Add `take_data` to `Waitable` and `data` to `AnyExecutable` (`#1241 <https://github.com/ros2/rclcpp/issues/1241>`_)
|
||||
* Add benchmarks for node parameters interface (`#1444 <https://github.com/ros2/rclcpp/issues/1444>`_)
|
||||
* Remove allocation from executor::remove_node() (`#1448 <https://github.com/ros2/rclcpp/issues/1448>`_)
|
||||
* Fix test crashes on CentOS 7 (`#1449 <https://github.com/ros2/rclcpp/issues/1449>`_)
|
||||
* Bump rclcpp packages to Quality Level 2 (`#1445 <https://github.com/ros2/rclcpp/issues/1445>`_)
|
||||
* Added executor benchmark tests (`#1413 <https://github.com/ros2/rclcpp/issues/1413>`_)
|
||||
* Add fully-qualified namespace to WeakCallbackGroupsToNodesMap (`#1435 <https://github.com/ros2/rclcpp/issues/1435>`_)
|
||||
* Contributors: Alejandro Hernández Cordero, Audrow Nash, Chris Lalancette, Ivan Santiago Paunovic, Louise Poubel, Scott K Logan, brawner
|
||||
|
||||
5.1.0 (2020-11-02)
|
||||
------------------
|
||||
* Deprecate Duration(rcl_duration_value_t) in favor of static Duration::from_nanoseconds(rcl_duration_value_t) (`#1432 <https://github.com/ros2/rclcpp/issues/1432>`_)
|
||||
* Avoid parsing arguments twice in `rclcpp::init_and_remove_ros_arguments` (`#1415 <https://github.com/ros2/rclcpp/issues/1415>`_)
|
||||
* Add service and client benchmarks (`#1425 <https://github.com/ros2/rclcpp/issues/1425>`_)
|
||||
* Set CMakeLists to only use default rmw for benchmarks (`#1427 <https://github.com/ros2/rclcpp/issues/1427>`_)
|
||||
* Update tracetools' QL in rclcpp's QD (`#1428 <https://github.com/ros2/rclcpp/issues/1428>`_)
|
||||
* Add missing locking to the rclcpp_action::ServerBase. (`#1421 <https://github.com/ros2/rclcpp/issues/1421>`_)
|
||||
* Initial benchmark tests for rclcpp::init/shutdown create/destroy node (`#1411 <https://github.com/ros2/rclcpp/issues/1411>`_)
|
||||
* Refactor test CMakeLists in prep for benchmarks (`#1422 <https://github.com/ros2/rclcpp/issues/1422>`_)
|
||||
* Add methods in topic and service interface to resolve a name (`#1410 <https://github.com/ros2/rclcpp/issues/1410>`_)
|
||||
* Update deprecated gtest macros (`#1370 <https://github.com/ros2/rclcpp/issues/1370>`_)
|
||||
* Clear members for StaticExecutorEntitiesCollector to avoid shared_ptr dependency (`#1303 <https://github.com/ros2/rclcpp/issues/1303>`_)
|
||||
* Increase test timeouts of slow running tests with rmw_connext_cpp (`#1400 <https://github.com/ros2/rclcpp/issues/1400>`_)
|
||||
* Avoid self dependency that not destoryed (`#1301 <https://github.com/ros2/rclcpp/issues/1301>`_)
|
||||
* Update maintainers (`#1384 <https://github.com/ros2/rclcpp/issues/1384>`_)
|
||||
* Add clock qos to node options (`#1375 <https://github.com/ros2/rclcpp/issues/1375>`_)
|
||||
* Fix NodeOptions copy constructor (`#1376 <https://github.com/ros2/rclcpp/issues/1376>`_)
|
||||
* Make sure to clean the external client/service handle. (`#1296 <https://github.com/ros2/rclcpp/issues/1296>`_)
|
||||
* Increase coverage of WaitSetTemplate (`#1368 <https://github.com/ros2/rclcpp/issues/1368>`_)
|
||||
* Increase coverage of guard_condition.cpp to 100% (`#1369 <https://github.com/ros2/rclcpp/issues/1369>`_)
|
||||
* Add coverage statement (`#1367 <https://github.com/ros2/rclcpp/issues/1367>`_)
|
||||
* Tests for LoanedMessage with mocked loaned message publisher (`#1366 <https://github.com/ros2/rclcpp/issues/1366>`_)
|
||||
* Add unit tests for qos and qos_event files (`#1352 <https://github.com/ros2/rclcpp/issues/1352>`_)
|
||||
* Finish coverage of publisher API (`#1365 <https://github.com/ros2/rclcpp/issues/1365>`_)
|
||||
* Finish API coverage on executors. (`#1364 <https://github.com/ros2/rclcpp/issues/1364>`_)
|
||||
* Add test for ParameterService (`#1355 <https://github.com/ros2/rclcpp/issues/1355>`_)
|
||||
* Add time API coverage tests (`#1347 <https://github.com/ros2/rclcpp/issues/1347>`_)
|
||||
* Add timer coverage tests (`#1363 <https://github.com/ros2/rclcpp/issues/1363>`_)
|
||||
* Add in additional tests for parameter_client.cpp coverage.
|
||||
* Minor fixes to the parameter_service.cpp file.
|
||||
* reset rcl_context shared_ptr after calling rcl_init sucessfully (`#1357 <https://github.com/ros2/rclcpp/issues/1357>`_)
|
||||
* Improved test publisher - zero qos history depth value exception (`#1360 <https://github.com/ros2/rclcpp/issues/1360>`_)
|
||||
* Covered resolve_use_intra_process (`#1359 <https://github.com/ros2/rclcpp/issues/1359>`_)
|
||||
* Improve test_subscription_options (`#1358 <https://github.com/ros2/rclcpp/issues/1358>`_)
|
||||
* Add in more tests for init_options coverage. (`#1353 <https://github.com/ros2/rclcpp/issues/1353>`_)
|
||||
* Test the remaining node public API (`#1342 <https://github.com/ros2/rclcpp/issues/1342>`_)
|
||||
* Complete coverage of Parameter and ParameterValue API (`#1344 <https://github.com/ros2/rclcpp/issues/1344>`_)
|
||||
* Add in more tests for the utilities. (`#1349 <https://github.com/ros2/rclcpp/issues/1349>`_)
|
||||
* Add in two more tests for expand_topic_or_service_name. (`#1350 <https://github.com/ros2/rclcpp/issues/1350>`_)
|
||||
* Add tests for node_options API (`#1343 <https://github.com/ros2/rclcpp/issues/1343>`_)
|
||||
* Add in more coverage for expand_topic_or_service_name. (`#1346 <https://github.com/ros2/rclcpp/issues/1346>`_)
|
||||
* Test exception in spin_until_future_complete. (`#1345 <https://github.com/ros2/rclcpp/issues/1345>`_)
|
||||
* Add coverage tests graph_listener (`#1330 <https://github.com/ros2/rclcpp/issues/1330>`_)
|
||||
* Add in unit tests for the Executor class.
|
||||
* Allow mimick patching of methods with up to 9 arguments.
|
||||
* Improve the error messages in the Executor class.
|
||||
* Add coverage for client API (`#1329 <https://github.com/ros2/rclcpp/issues/1329>`_)
|
||||
* Increase service coverage (`#1332 <https://github.com/ros2/rclcpp/issues/1332>`_)
|
||||
* Make more of the static entity collector API private.
|
||||
* Const-ify more of the static executor.
|
||||
* Add more tests for the static single threaded executor.
|
||||
* Many more tests for the static_executor_entities_collector.
|
||||
* Get one more line of code coverage in memory_strategy.cpp
|
||||
* Bugfix when adding callback group.
|
||||
* Fix typos in comments.
|
||||
* Remove deprecated executor::FutureReturnCode APIs. (`#1327 <https://github.com/ros2/rclcpp/issues/1327>`_)
|
||||
* Increase coverage of publisher/subscription API (`#1325 <https://github.com/ros2/rclcpp/issues/1325>`_)
|
||||
* Not finalize guard condition while destructing SubscriptionIntraProcess (`#1307 <https://github.com/ros2/rclcpp/issues/1307>`_)
|
||||
* Expose qos setting for /rosout (`#1247 <https://github.com/ros2/rclcpp/issues/1247>`_)
|
||||
* Add coverage for missing API (except executors) (`#1326 <https://github.com/ros2/rclcpp/issues/1326>`_)
|
||||
* Include topic name in QoS mismatch warning messages (`#1286 <https://github.com/ros2/rclcpp/issues/1286>`_)
|
||||
* Add coverage tests context functions (`#1321 <https://github.com/ros2/rclcpp/issues/1321>`_)
|
||||
* Increase coverage of node_interfaces, including with mocking rcl errors (`#1322 <https://github.com/ros2/rclcpp/issues/1322>`_)
|
||||
* Contributors: Ada-King, Alejandro Hernández Cordero, Audrow Nash, Barry Xu, Chen Lihui, Chris Lalancette, Christophe Bedard, Ivan Santiago Paunovic, Jorge Perez, Morgan Quigley, brawner
|
||||
|
||||
5.0.0 (2020-09-18)
|
||||
------------------
|
||||
* Make node_graph::count_graph_users() const (`#1320 <https://github.com/ros2/rclcpp/issues/1320>`_)
|
||||
* Add coverage for wait_set_policies (`#1316 <https://github.com/ros2/rclcpp/issues/1316>`_)
|
||||
* Only exchange intra_process waitable if nonnull (`#1317 <https://github.com/ros2/rclcpp/issues/1317>`_)
|
||||
* Check waitable for nullptr during constructor (`#1315 <https://github.com/ros2/rclcpp/issues/1315>`_)
|
||||
* Call vector.erase with end iterator overload (`#1314 <https://github.com/ros2/rclcpp/issues/1314>`_)
|
||||
* Use best effort, keep last, history depth 1 QoS Profile for '/clock' subscriptions (`#1312 <https://github.com/ros2/rclcpp/issues/1312>`_)
|
||||
* Add tests type_support module (`#1308 <https://github.com/ros2/rclcpp/issues/1308>`_)
|
||||
* Replace std_msgs with test_msgs in executors test (`#1310 <https://github.com/ros2/rclcpp/issues/1310>`_)
|
||||
* Add set_level for rclcpp::Logger (`#1284 <https://github.com/ros2/rclcpp/issues/1284>`_)
|
||||
* Remove unused private function (rclcpp::Node and rclcpp_lifecycle::Node) (`#1294 <https://github.com/ros2/rclcpp/issues/1294>`_)
|
||||
* Adding tests basic getters (`#1291 <https://github.com/ros2/rclcpp/issues/1291>`_)
|
||||
* Adding callback groups in executor (`#1218 <https://github.com/ros2/rclcpp/issues/1218>`_)
|
||||
* Refactor Subscription Topic Statistics Tests (`#1281 <https://github.com/ros2/rclcpp/issues/1281>`_)
|
||||
* Add operator!= for duration (`#1236 <https://github.com/ros2/rclcpp/issues/1236>`_)
|
||||
* Fix clock thread issue (`#1266 <https://github.com/ros2/rclcpp/issues/1266>`_) (`#1267 <https://github.com/ros2/rclcpp/issues/1267>`_)
|
||||
* Fix topic stats test, wait for more messages, only check the ones with samples (`#1274 <https://github.com/ros2/rclcpp/issues/1274>`_)
|
||||
* Add get_domain_id method to rclcpp::Context (`#1271 <https://github.com/ros2/rclcpp/issues/1271>`_)
|
||||
* Fixes for unit tests that fail under cyclonedds (`#1270 <https://github.com/ros2/rclcpp/issues/1270>`_)
|
||||
* initialize_logging\_ should be copied (`#1272 <https://github.com/ros2/rclcpp/issues/1272>`_)
|
||||
* Use static_cast instead of C-style cast for instrumentation (`#1263 <https://github.com/ros2/rclcpp/issues/1263>`_)
|
||||
* Make parameter clients use template constructors (`#1249 <https://github.com/ros2/rclcpp/issues/1249>`_)
|
||||
* Ability to configure domain_id via InitOptions. (`#1165 <https://github.com/ros2/rclcpp/issues/1165>`_)
|
||||
* Simplify and fix allocator memory strategy unit test for connext (`#1252 <https://github.com/ros2/rclcpp/issues/1252>`_)
|
||||
* Use global namespace for parameter events subscription topic (`#1257 <https://github.com/ros2/rclcpp/issues/1257>`_)
|
||||
* Increase timeouts for connext for long tests (`#1253 <https://github.com/ros2/rclcpp/issues/1253>`_)
|
||||
* Adjust test_static_executor_entities_collector for rmw_connext_cpp (`#1251 <https://github.com/ros2/rclcpp/issues/1251>`_)
|
||||
* Fix failing test with Connext since it doesn't wait for discovery (`#1246 <https://github.com/ros2/rclcpp/issues/1246>`_)
|
||||
* Fix node graph test with Connext and CycloneDDS returning actual data (`#1245 <https://github.com/ros2/rclcpp/issues/1245>`_)
|
||||
* Warn about unused result of add_on_set_parameters_callback (`#1238 <https://github.com/ros2/rclcpp/issues/1238>`_)
|
||||
* Unittests for memory strategy files, except allocator_memory_strategy (`#1189 <https://github.com/ros2/rclcpp/issues/1189>`_)
|
||||
* EXPECT_THROW_EQ and ASSERT_THROW_EQ macros for unittests (`#1232 <https://github.com/ros2/rclcpp/issues/1232>`_)
|
||||
* Add unit test for static_executor_entities_collector (`#1221 <https://github.com/ros2/rclcpp/issues/1221>`_)
|
||||
* Parameterize test executors for all executor types (`#1222 <https://github.com/ros2/rclcpp/issues/1222>`_)
|
||||
* Unit tests for allocator_memory_strategy.cpp part 2 (`#1198 <https://github.com/ros2/rclcpp/issues/1198>`_)
|
||||
* Unit tests for allocator_memory_strategy.hpp (`#1197 <https://github.com/ros2/rclcpp/issues/1197>`_)
|
||||
* Derive and throw exception in spin_some spin_all for StaticSingleThreadedExecutor (`#1220 <https://github.com/ros2/rclcpp/issues/1220>`_)
|
||||
* Make ring buffer thread-safe (`#1213 <https://github.com/ros2/rclcpp/issues/1213>`_)
|
||||
* Add missing RCLCPP_PUBLIC to ~StaticExecutorEntitiesCollector (`#1227 <https://github.com/ros2/rclcpp/issues/1227>`_)
|
||||
* Document graph functions don't apply remap rules (`#1225 <https://github.com/ros2/rclcpp/issues/1225>`_)
|
||||
* Remove recreation of entities_collector (`#1217 <https://github.com/ros2/rclcpp/issues/1217>`_)
|
||||
* Contributors: Audrow Nash, Chen Lihui, Christophe Bedard, Daisuke Sato, Devin Bonnie, Dirk Thomas, Ivan Santiago Paunovic, Jacob Perron, Jannik Abbenseth, Jorge Perez, Pedro Pena, Shane Loretz, Stephen Brawner, Tomoya Fujita
|
||||
|
||||
4.0.0 (2020-07-09)
|
||||
------------------
|
||||
* Fix rclcpp::NodeOptions::operator= (`#1211 <https://github.com/ros2/rclcpp/issues/1211>`_)
|
||||
* Link against thread library where necessary (`#1210 <https://github.com/ros2/rclcpp/issues/1210>`_)
|
||||
* Unit tests for node interfaces (`#1202 <https://github.com/ros2/rclcpp/issues/1202>`_)
|
||||
* Remove usage of domain id in node options (`#1205 <https://github.com/ros2/rclcpp/issues/1205>`_)
|
||||
* Remove deprecated set_on_parameters_set_callback function (`#1199 <https://github.com/ros2/rclcpp/issues/1199>`_)
|
||||
* Fix conversion of negative durations to messages (`#1188 <https://github.com/ros2/rclcpp/issues/1188>`_)
|
||||
* Fix implementation of NodeOptions::use_global_arguments() (`#1176 <https://github.com/ros2/rclcpp/issues/1176>`_)
|
||||
* Bump to QD to level 3 and fixed links (`#1158 <https://github.com/ros2/rclcpp/issues/1158>`_)
|
||||
* Fix pub/sub count API tests (`#1203 <https://github.com/ros2/rclcpp/issues/1203>`_)
|
||||
* Update tracetools' QL to 2 in rclcpp's QD (`#1187 <https://github.com/ros2/rclcpp/issues/1187>`_)
|
||||
* Fix exception message on rcl_clock_init (`#1182 <https://github.com/ros2/rclcpp/issues/1182>`_)
|
||||
* Throw exception if rcl_timer_init fails (`#1179 <https://github.com/ros2/rclcpp/issues/1179>`_)
|
||||
* Unit tests for some header-only functions/classes (`#1181 <https://github.com/ros2/rclcpp/issues/1181>`_)
|
||||
* Callback should be perfectly-forwarded (`#1183 <https://github.com/ros2/rclcpp/issues/1183>`_)
|
||||
* Add unit tests for logging functionality (`#1184 <https://github.com/ros2/rclcpp/issues/1184>`_)
|
||||
* Add create_publisher include to create_subscription (`#1180 <https://github.com/ros2/rclcpp/issues/1180>`_)
|
||||
* Contributors: Alejandro Hernández Cordero, Christophe Bedard, Claire Wang, Dirk Thomas, Ivan Santiago Paunovic, Johannes Meyer, Michel Hidalgo, Stephen Brawner, tomoya
|
||||
|
||||
3.0.0 (2020-06-18)
|
||||
------------------
|
||||
* Check period duration in create_wall_timer (`#1178 <https://github.com/ros2/rclcpp/issues/1178>`_)
|
||||
* Fix get_node_time_source_interface() docstring (`#988 <https://github.com/ros2/rclcpp/issues/988>`_)
|
||||
* Add message lost subscription event (`#1164 <https://github.com/ros2/rclcpp/issues/1164>`_)
|
||||
* Add spin_all method to Executor (`#1156 <https://github.com/ros2/rclcpp/issues/1156>`_)
|
||||
* Reorganize test directory and split CMakeLists.txt (`#1173 <https://github.com/ros2/rclcpp/issues/1173>`_)
|
||||
* Check if context is valid when looping in spin_some (`#1167 <https://github.com/ros2/rclcpp/issues/1167>`_)
|
||||
* Add check for invalid topic statistics publish period (`#1151 <https://github.com/ros2/rclcpp/issues/1151>`_)
|
||||
* Fix spin_until_future_complete: check spinning value (`#1023 <https://github.com/ros2/rclcpp/issues/1023>`_)
|
||||
* Fix doxygen warnings (`#1163 <https://github.com/ros2/rclcpp/issues/1163>`_)
|
||||
* Fix reference to rclcpp in its Quality declaration (`#1161 <https://github.com/ros2/rclcpp/issues/1161>`_)
|
||||
* Allow spin_until_future_complete to accept any future like object (`#1113 <https://github.com/ros2/rclcpp/issues/1113>`_)
|
||||
* Contributors: Alejandro Hernández Cordero, Christophe Bedard, Devin Bonnie, Dirk Thomas, DongheeYe, Ivan Santiago Paunovic, Jacob Perron, Sarthak Mittal, brawner, tomoya
|
||||
|
||||
2.0.0 (2020-06-01)
|
||||
------------------
|
||||
* Added missing virtual destructors. (`#1149 <https://github.com/ros2/rclcpp/issues/1149>`_)
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
project(rclcpp)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
find_package(ament_cmake_ros REQUIRED)
|
||||
find_package(ament_index_cpp REQUIRED)
|
||||
find_package(builtin_interfaces REQUIRED)
|
||||
find_package(libstatistics_collector REQUIRED)
|
||||
find_package(rcl REQUIRED)
|
||||
@@ -18,12 +21,17 @@ find_package(rosidl_typesupport_cpp REQUIRED)
|
||||
find_package(statistics_msgs REQUIRED)
|
||||
find_package(tracetools REQUIRED)
|
||||
|
||||
# Default to C++14
|
||||
# TODO(wjwwood): remove this when gtest can build on its own, when using target_compile_features()
|
||||
# Default to C++17
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options(-Wall -Wextra -Wpedantic -Wnon-virtual-dtor -Woverloaded-virtual)
|
||||
# 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 -Wnon-virtual-dtor -Woverloaded-virtual)
|
||||
endif()
|
||||
|
||||
set(${PROJECT_NAME}_SRCS
|
||||
@@ -33,6 +41,8 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/clock.cpp
|
||||
src/rclcpp/context.cpp
|
||||
src/rclcpp/contexts/default_context.cpp
|
||||
src/rclcpp/detail/add_guard_condition_to_rcl_wait_set.cpp
|
||||
src/rclcpp/detail/resolve_parameter_overrides.cpp
|
||||
src/rclcpp/detail/rmw_implementation_specific_payload.cpp
|
||||
src/rclcpp/detail/rmw_implementation_specific_publisher_payload.cpp
|
||||
src/rclcpp/detail/rmw_implementation_specific_subscription_payload.cpp
|
||||
@@ -43,12 +53,14 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/executable_list.cpp
|
||||
src/rclcpp/executor.cpp
|
||||
src/rclcpp/executors.cpp
|
||||
src/rclcpp/expand_topic_or_service_name.cpp
|
||||
src/rclcpp/executors/multi_threaded_executor.cpp
|
||||
src/rclcpp/executors/single_threaded_executor.cpp
|
||||
src/rclcpp/executors/static_executor_entities_collector.cpp
|
||||
src/rclcpp/executors/static_single_threaded_executor.cpp
|
||||
src/rclcpp/expand_topic_or_service_name.cpp
|
||||
src/rclcpp/future_return_code.cpp
|
||||
src/rclcpp/generic_publisher.cpp
|
||||
src/rclcpp/generic_subscription.cpp
|
||||
src/rclcpp/graph_listener.cpp
|
||||
src/rclcpp/guard_condition.cpp
|
||||
src/rclcpp/init_options.cpp
|
||||
@@ -58,8 +70,8 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/memory_strategies.cpp
|
||||
src/rclcpp/memory_strategy.cpp
|
||||
src/rclcpp/message_info.cpp
|
||||
src/rclcpp/network_flow_endpoint.cpp
|
||||
src/rclcpp/node.cpp
|
||||
src/rclcpp/node_options.cpp
|
||||
src/rclcpp/node_interfaces/node_base.cpp
|
||||
src/rclcpp/node_interfaces/node_clock.cpp
|
||||
src/rclcpp/node_interfaces/node_graph.cpp
|
||||
@@ -70,15 +82,18 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/node_interfaces/node_timers.cpp
|
||||
src/rclcpp/node_interfaces/node_topics.cpp
|
||||
src/rclcpp/node_interfaces/node_waitables.cpp
|
||||
src/rclcpp/node_options.cpp
|
||||
src/rclcpp/parameter.cpp
|
||||
src/rclcpp/parameter_value.cpp
|
||||
src/rclcpp/parameter_client.cpp
|
||||
src/rclcpp/parameter_event_handler.cpp
|
||||
src/rclcpp/parameter_events_filter.cpp
|
||||
src/rclcpp/parameter_map.cpp
|
||||
src/rclcpp/parameter_service.cpp
|
||||
src/rclcpp/parameter_value.cpp
|
||||
src/rclcpp/publisher_base.cpp
|
||||
src/rclcpp/qos.cpp
|
||||
src/rclcpp/qos_event.cpp
|
||||
src/rclcpp/qos_overriding_options.cpp
|
||||
src/rclcpp/serialization.cpp
|
||||
src/rclcpp/serialized_message.cpp
|
||||
src/rclcpp/service.cpp
|
||||
@@ -89,11 +104,14 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/time_source.cpp
|
||||
src/rclcpp/timer.cpp
|
||||
src/rclcpp/type_support.cpp
|
||||
src/rclcpp/typesupport_helpers.cpp
|
||||
src/rclcpp/utilities.cpp
|
||||
src/rclcpp/wait_set_policies/detail/write_preferring_read_write_lock.cpp
|
||||
src/rclcpp/waitable.cpp
|
||||
)
|
||||
|
||||
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
||||
|
||||
# "watch" template for changes
|
||||
configure_file(
|
||||
"resource/logging.hpp.em"
|
||||
@@ -107,7 +125,7 @@ set(python_code_logging
|
||||
string(REPLACE ";" "$<SEMICOLON>" python_code_logging "${python_code_logging}")
|
||||
add_custom_command(OUTPUT include/rclcpp/logging.hpp
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "include/rclcpp"
|
||||
COMMAND ${PYTHON_EXECUTABLE} ARGS -c "${python_code_logging}"
|
||||
COMMAND Python3::Interpreter ARGS -c "${python_code_logging}"
|
||||
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/logging.hpp.em.watch"
|
||||
COMMENT "Expanding logging.hpp.em"
|
||||
VERBATIM
|
||||
@@ -131,7 +149,7 @@ foreach(interface_file ${interface_files})
|
||||
string(REPLACE ";" "$<SEMICOLON>" python_${interface_name}_traits "${python_${interface_name}_traits}")
|
||||
add_custom_command(OUTPUT include/rclcpp/node_interfaces/${interface_name}_traits.hpp
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "include/rclcpp/node_interfaces"
|
||||
COMMAND ${PYTHON_EXECUTABLE} ARGS -c "${python_${interface_name}_traits}"
|
||||
COMMAND Python3::Interpreter ARGS -c "${python_${interface_name}_traits}"
|
||||
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${interface_name}_traits.hpp.em.watch"
|
||||
COMMENT "Expanding interface_traits.hpp.em into ${interface_name}_traits.hpp"
|
||||
VERBATIM
|
||||
@@ -151,7 +169,7 @@ foreach(interface_file ${interface_files})
|
||||
string(REPLACE ";" "$<SEMICOLON>" python_get_${interface_name} "${python_get_${interface_name}}")
|
||||
add_custom_command(OUTPUT include/rclcpp/node_interfaces/get_${interface_name}.hpp
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "include/rclcpp/node_interfaces"
|
||||
COMMAND ${PYTHON_EXECUTABLE} ARGS -c "${python_get_${interface_name}}"
|
||||
COMMAND Python3::Interpreter ARGS -c "${python_get_${interface_name}}"
|
||||
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/get_${interface_name}.hpp.em.watch"
|
||||
COMMENT "Expanding get_interface.hpp.em into get_${interface_file}.hpp"
|
||||
VERBATIM
|
||||
@@ -160,16 +178,23 @@ foreach(interface_file ${interface_files})
|
||||
include/rclcpp/node_interfaces/get_${interface_name}.hpp)
|
||||
endforeach()
|
||||
|
||||
add_library(${PROJECT_NAME}
|
||||
${${PROJECT_NAME}_SRCS})
|
||||
add_library(${PROJECT_NAME} ${${PROJECT_NAME}_SRCS})
|
||||
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17)
|
||||
# TODO(wjwwood): address all deprecation warnings and then remove this
|
||||
if(WIN32)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS")
|
||||
endif()
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
|
||||
"$<INSTALL_INTERFACE:include>")
|
||||
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>")
|
||||
target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT})
|
||||
# specific order: dependents before dependencies
|
||||
ament_target_dependencies(${PROJECT_NAME}
|
||||
"ament_index_cpp"
|
||||
"libstatistics_collector"
|
||||
"rcl"
|
||||
"rcl_interfaces"
|
||||
"rcl_yaml_param_parser"
|
||||
"rcpputils"
|
||||
"rcutils"
|
||||
@@ -193,11 +218,15 @@ install(
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
|
||||
# specific order: dependents before dependencies
|
||||
ament_export_include_directories(include)
|
||||
# Export old-style CMake variables
|
||||
ament_export_include_directories("include/${PROJECT_NAME}")
|
||||
ament_export_libraries(${PROJECT_NAME})
|
||||
|
||||
# Export modern CMake targets
|
||||
ament_export_targets(${PROJECT_NAME})
|
||||
|
||||
# specific order: dependents before dependencies
|
||||
ament_export_dependencies(ament_index_cpp)
|
||||
ament_export_dependencies(libstatistics_collector)
|
||||
ament_export_dependencies(rcl)
|
||||
ament_export_dependencies(rcpputils)
|
||||
@@ -212,447 +241,22 @@ ament_export_dependencies(statistics_msgs)
|
||||
ament_export_dependencies(tracetools)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_cmake_gtest REQUIRED)
|
||||
find_package(ament_lint_auto REQUIRED)
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
|
||||
find_package(rmw_implementation_cmake REQUIRED)
|
||||
find_package(rosidl_default_generators REQUIRED)
|
||||
|
||||
find_package(test_msgs REQUIRED)
|
||||
|
||||
include(cmake/rclcpp_add_build_failure_test.cmake)
|
||||
|
||||
add_definitions(-DTEST_RESOURCES_DIRECTORY="${CMAKE_CURRENT_BINARY_DIR}/test/resources")
|
||||
|
||||
rosidl_generate_interfaces(${PROJECT_NAME}_test_msgs
|
||||
test/msg/Header.msg
|
||||
test/msg/MessageWithHeader.msg
|
||||
DEPENDENCIES builtin_interfaces
|
||||
LIBRARY_NAME ${PROJECT_NAME}
|
||||
SKIP_INSTALL
|
||||
)
|
||||
|
||||
ament_add_gtest(test_client test/test_client.cpp)
|
||||
if(TARGET test_client)
|
||||
ament_target_dependencies(test_client
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
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_runtime_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
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_expand_topic_or_service_name ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_function_traits test/test_function_traits.cpp)
|
||||
if(TARGET test_function_traits)
|
||||
target_include_directories(test_function_traits PUBLIC include)
|
||||
ament_target_dependencies(test_function_traits
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
endif()
|
||||
ament_add_gmock(test_intra_process_manager test/test_intra_process_manager.cpp)
|
||||
if(TARGET test_intra_process_manager)
|
||||
ament_target_dependencies(test_intra_process_manager
|
||||
"rcl"
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_intra_process_manager ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_ring_buffer_implementation test/test_ring_buffer_implementation.cpp)
|
||||
if(TARGET test_ring_buffer_implementation)
|
||||
ament_target_dependencies(test_ring_buffer_implementation
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_ring_buffer_implementation ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_intra_process_buffer test/test_intra_process_buffer.cpp)
|
||||
if(TARGET test_intra_process_buffer)
|
||||
ament_target_dependencies(test_intra_process_buffer
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_intra_process_buffer ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_loaned_message test/test_loaned_message.cpp)
|
||||
ament_target_dependencies(test_loaned_message
|
||||
"test_msgs"
|
||||
)
|
||||
target_link_libraries(test_loaned_message ${PROJECT_NAME})
|
||||
|
||||
ament_add_gtest(test_node test/test_node.cpp TIMEOUT 240)
|
||||
if(TARGET test_node)
|
||||
ament_target_dependencies(test_node
|
||||
"rcl_interfaces"
|
||||
"rcpputils"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
"test_msgs"
|
||||
)
|
||||
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
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_node_global_args ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_node_options test/test_node_options.cpp)
|
||||
if(TARGET test_node_options)
|
||||
ament_target_dependencies(test_node_options "rcl")
|
||||
target_link_libraries(test_node_options ${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
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_parameter_events_filter ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_parameter test/test_parameter.cpp)
|
||||
if(TARGET test_parameter)
|
||||
ament_target_dependencies(test_parameter
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_parameter ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_parameter_map test/test_parameter_map.cpp)
|
||||
if(TARGET test_parameter_map)
|
||||
target_link_libraries(test_parameter_map ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_publisher test/test_publisher.cpp)
|
||||
if(TARGET test_publisher)
|
||||
ament_target_dependencies(test_publisher
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
"test_msgs"
|
||||
)
|
||||
target_link_libraries(test_publisher ${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
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
"test_msgs"
|
||||
)
|
||||
target_link_libraries(test_publisher_subscription_count_api ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_qos test/test_qos.cpp)
|
||||
if(TARGET test_qos)
|
||||
ament_target_dependencies(test_qos
|
||||
"rmw"
|
||||
)
|
||||
target_link_libraries(test_qos
|
||||
${PROJECT_NAME}
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_qos_event test/test_qos_event.cpp)
|
||||
if(TARGET test_qos_event)
|
||||
ament_target_dependencies(test_qos_event
|
||||
"rmw"
|
||||
"test_msgs"
|
||||
)
|
||||
target_link_libraries(test_qos_event
|
||||
${PROJECT_NAME}
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_rate test/test_rate.cpp)
|
||||
if(TARGET test_rate)
|
||||
ament_target_dependencies(test_rate
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_rate
|
||||
${PROJECT_NAME}
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_serialized_message_allocator test/test_serialized_message_allocator.cpp)
|
||||
if(TARGET test_serialized_message_allocator)
|
||||
ament_target_dependencies(test_serialized_message_allocator
|
||||
test_msgs
|
||||
)
|
||||
target_link_libraries(test_serialized_message_allocator
|
||||
${PROJECT_NAME}
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_serialized_message test/test_serialized_message.cpp)
|
||||
if(TARGET test_serialized_message)
|
||||
ament_target_dependencies(test_serialized_message
|
||||
test_msgs
|
||||
)
|
||||
target_link_libraries(test_serialized_message
|
||||
${PROJECT_NAME}
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_service test/test_service.cpp)
|
||||
if(TARGET test_service)
|
||||
ament_target_dependencies(test_service
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_service ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_subscription test/test_subscription.cpp)
|
||||
if(TARGET test_subscription)
|
||||
ament_target_dependencies(test_subscription
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
"test_msgs"
|
||||
)
|
||||
target_link_libraries(test_subscription ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_subscription_publisher_count_api test/test_subscription_publisher_count_api.cpp)
|
||||
if(TARGET test_subscription_publisher_count_api)
|
||||
ament_target_dependencies(test_subscription_publisher_count_api
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
"test_msgs"
|
||||
)
|
||||
target_link_libraries(test_subscription_publisher_count_api ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_subscription_traits test/test_subscription_traits.cpp)
|
||||
if(TARGET test_subscription_traits)
|
||||
ament_target_dependencies(test_subscription_traits
|
||||
"rcl"
|
||||
"test_msgs"
|
||||
)
|
||||
target_link_libraries(test_subscription_traits ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_find_weak_nodes test/test_find_weak_nodes.cpp)
|
||||
if(TARGET test_find_weak_nodes)
|
||||
ament_target_dependencies(test_find_weak_nodes
|
||||
"rcl"
|
||||
)
|
||||
target_link_libraries(test_find_weak_nodes ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
if(WIN32)
|
||||
set(append_library_dirs "${append_library_dirs}/$<CONFIG>")
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_externally_defined_services test/test_externally_defined_services.cpp)
|
||||
ament_target_dependencies(test_externally_defined_services
|
||||
"rcl"
|
||||
"test_msgs"
|
||||
)
|
||||
target_link_libraries(test_externally_defined_services ${PROJECT_NAME})
|
||||
|
||||
ament_add_gtest(test_duration test/test_duration.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_duration)
|
||||
ament_target_dependencies(test_duration
|
||||
"rcl")
|
||||
target_link_libraries(test_duration ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_executor test/test_executor.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_executor)
|
||||
ament_target_dependencies(test_executor
|
||||
"rcl")
|
||||
target_link_libraries(test_executor ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_logger test/test_logger.cpp)
|
||||
target_link_libraries(test_logger ${PROJECT_NAME})
|
||||
|
||||
ament_add_gmock(test_logging test/test_logging.cpp)
|
||||
target_link_libraries(test_logging ${PROJECT_NAME})
|
||||
|
||||
ament_add_gtest(test_time test/test_time.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_time)
|
||||
ament_target_dependencies(test_time
|
||||
"rcl")
|
||||
target_link_libraries(test_time ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_timer test/test_timer.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_timer)
|
||||
ament_target_dependencies(test_timer
|
||||
"rcl")
|
||||
target_link_libraries(test_timer ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_time_source test/test_time_source.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_time_source)
|
||||
ament_target_dependencies(test_time_source
|
||||
"rcl")
|
||||
target_link_libraries(test_time_source ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_utilities test/test_utilities.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_utilities)
|
||||
ament_target_dependencies(test_utilities
|
||||
"rcl")
|
||||
target_link_libraries(test_utilities ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_init test/test_init.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_init)
|
||||
ament_target_dependencies(test_init
|
||||
"rcl")
|
||||
target_link_libraries(test_init ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_interface_traits test/test_interface_traits.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_interface_traits)
|
||||
ament_target_dependencies(test_interface_traits
|
||||
"rcl")
|
||||
target_link_libraries(test_interface_traits ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_multi_threaded_executor test/executors/test_multi_threaded_executor.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_multi_threaded_executor)
|
||||
ament_target_dependencies(test_multi_threaded_executor
|
||||
"rcl")
|
||||
target_link_libraries(test_multi_threaded_executor ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_guard_condition test/test_guard_condition.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_guard_condition)
|
||||
target_link_libraries(test_guard_condition ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_wait_set test/test_wait_set.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_wait_set)
|
||||
ament_target_dependencies(test_wait_set "test_msgs")
|
||||
target_link_libraries(test_wait_set ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_subscription_topic_statistics test/topic_statistics/test_subscription_topic_statistics.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
)
|
||||
if(TARGET test_subscription_topic_statistics)
|
||||
ament_target_dependencies(test_subscription_topic_statistics
|
||||
"builtin_interfaces"
|
||||
"libstatistics_collector"
|
||||
"rcl_interfaces"
|
||||
"rcutils"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
"statistics_msgs"
|
||||
"test_msgs")
|
||||
rosidl_target_interfaces(test_subscription_topic_statistics ${PROJECT_NAME}_test_msgs "rosidl_typesupport_cpp")
|
||||
target_link_libraries(test_subscription_topic_statistics ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_subscription_options test/test_subscription_options.cpp)
|
||||
if(TARGET test_subscription_options)
|
||||
ament_target_dependencies(test_subscription_options "rcl")
|
||||
target_link_libraries(test_subscription_options ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
# Install test resources
|
||||
install(
|
||||
DIRECTORY test/resources
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/test)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
ament_package()
|
||||
|
||||
install(
|
||||
DIRECTORY include/ ${CMAKE_CURRENT_BINARY_DIR}/include/
|
||||
DESTINATION include
|
||||
DESTINATION include/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
if(TEST cppcheck)
|
||||
# must set the property after ament_package()
|
||||
set_tests_properties(cppcheck PROPERTIES TIMEOUT 500)
|
||||
endif()
|
||||
|
||||
ament_generate_version_header(${PROJECT_NAME})
|
||||
|
||||
@@ -21,13 +21,22 @@ GENERATE_LATEX = NO
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = YES
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
PREDEFINED = RCLCPP_PUBLIC=
|
||||
PREDEFINED += DOXYGEN_ONLY
|
||||
PREDEFINED += RCLCPP_LOCAL=
|
||||
PREDEFINED += RCLCPP_PUBLIC=
|
||||
PREDEFINED += RCLCPP_PUBLIC_TYPE=
|
||||
PREDEFINED += RCUTILS_WARN_UNUSED=
|
||||
PREDEFINED += RCPPUTILS_TSA_GUARDED_BY(x)=
|
||||
PREDEFINED += RCPPUTILS_TSA_PT_GUARDED_BY(x)=
|
||||
PREDEFINED += RCPPUTILS_TSA_REQUIRES(x)=
|
||||
|
||||
DOT_GRAPH_MAX_NODES = 101
|
||||
|
||||
# Tag files that do not exist will produce a warning and cross-project linking will not work.
|
||||
TAGFILES += "../../../../doxygen_tag_files/cppreference-doxygen-web.tag.xml=http://en.cppreference.com/w/"
|
||||
#TAGFILES += "../../../../doxygen_tag_files/cppreference-doxygen-web.tag.xml=http://en.cppreference.com/w/"
|
||||
# Consider changing "latest" to the version you want to reference (e.g. beta1 or 1.0.0)
|
||||
TAGFILES += "../../../../doxygen_tag_files/rcl.tag=http://docs.ros2.org/latest/api/rcl/"
|
||||
TAGFILES += "../../../../doxygen_tag_files/rmw.tag=http://docs.ros2.org/latest/api/rmw/"
|
||||
TAGFILES += "../../../../doxygen_tag_files/rcutils.tag=http://docs.ros2.org/latest/api/rcutils/"
|
||||
#TAGFILES += "../../../../doxygen_tag_files/rcl.tag=http://docs.ros2.org/latest/api/rcl/"
|
||||
#TAGFILES += "../../../../doxygen_tag_files/rmw.tag=http://docs.ros2.org/latest/api/rmw/"
|
||||
#TAGFILES += "../../../../doxygen_tag_files/rcutils.tag=http://docs.ros2.org/latest/api/rcutils/"
|
||||
# Uncomment to generate tag files for cross-project linking.
|
||||
#GENERATE_TAGFILE = "../../../../doxygen_tag_files/rclcpp.tag"
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
This document is a declaration of software quality for the `rclcpp` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html).
|
||||
|
||||
# `rclcpp` Quality Declaration
|
||||
# rclcpp Quality Declaration
|
||||
|
||||
The package `rclcpp` claims to be in the **Quality Level 4** category.
|
||||
The package `rclcpp` claims to be in the **Quality Level 1** category when it is used with a **Quality Level 1** middleware.
|
||||
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#package-quality-categories) of the ROS2 developer guide.
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://www.ros.org/reps/rep-2004.html) of the ROS2 developer guide.
|
||||
|
||||
## Version Policy [1]
|
||||
|
||||
### Version Scheme [1.i]
|
||||
|
||||
`rclcpp` uses `semver` according to the recommendation for ROS Core packages in the [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#versioning)
|
||||
`rclcpp` uses `semver` according to the recommendation for ROS Core packages in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#versioning).
|
||||
|
||||
### Version Stability [1.ii]
|
||||
|
||||
@@ -39,11 +39,11 @@ Headers under the folder `detail` are not considered part of the public API and
|
||||
|
||||
## Change Control Process [2]
|
||||
|
||||
`rclcpp` follows the recommended guidelines for ROS Core packages in the [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#change-control-process).
|
||||
`rclcpp` follows the recommended guidelines for ROS Core packages in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#change-control-process).
|
||||
|
||||
### Change Requests [2.i]
|
||||
|
||||
All changes will occur through a pull request, check [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#change-control-process) for additional information.
|
||||
All changes will occur through a pull request, check [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#change-control-process) for additional information.
|
||||
|
||||
### Contributor Origin [2.ii]
|
||||
|
||||
@@ -51,7 +51,7 @@ This package uses DCO as its confirmation of contributor origin policy. More inf
|
||||
|
||||
### Peer Review Policy [2.iii]
|
||||
|
||||
All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#change-control-process) for additional information.
|
||||
All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#change-control-process) for additional information.
|
||||
|
||||
### Continuous Integration [2.iv]
|
||||
|
||||
@@ -76,19 +76,19 @@ All pull requests must resolve related documentation changes before merging.
|
||||
|
||||
### Public API Documentation [3.ii]
|
||||
|
||||
The API is publicly available in the [ROS 2 API documentation](http://docs.ros2.org/latest/api/rclcpp/).
|
||||
The API is publicly available in its [ROS 2 API documentation](http://docs.ros2.org/latest/api/rclcpp/).
|
||||
|
||||
### License [3.iii]
|
||||
|
||||
The license for `rclcpp` is Apache 2.0, and a summary is in each source file, the type is declared in the [`package.xml`](./package.xml) manifest file, and a full copy of the license is in the [`LICENSE`](../LICENSE) file.
|
||||
|
||||
There is an automated test which runs a linter that ensures each file has a license statement. [Here](http://build.ros2.org/view/Epr/job/Epr__rclcpp__ubuntu_bionic_amd64/lastBuild/testReport/rclcpp/) can be found a list with the latest results of the various linters being run on the package.
|
||||
There is an automated test which runs a linter that ensures each file has a license statement. [Here](http://build.ros2.org/view/Rpr/job/Rpr__rclcpp__ubuntu_focal_amd64/lastCompletedBuild/testReport/rclcpp/) can be found a list with the latest results of the various linters being run on the package.
|
||||
|
||||
### Copyright Statements [3.iv]
|
||||
|
||||
The copyright holders each provide a statement of copyright in each source code file in `rclcpp`.
|
||||
|
||||
There is an automated test which runs a linter that ensures each file has at least one copyright statement. Latest linter result report can be seen [here](http://build.ros2.org/view/Epr/job/Epr__rclcpp__ubuntu_bionic_amd64/lastBuild/testReport/rclcpp/copyright/).
|
||||
There is an automated test which runs a linter that ensures each file has at least one copyright statement. Latest linter result report can be seen [here](http://build.ros2.org/view/Rpr/job/Rpr__rclcpp__ubuntu_focal_amd64/lastCompletedBuild/testReport/rclcpp/copyright/).
|
||||
|
||||
## Testing [4]
|
||||
|
||||
@@ -111,7 +111,7 @@ The tests aim to cover both typical usage and corner cases, but are quantified b
|
||||
|
||||
### Coverage [4.iii]
|
||||
|
||||
`rclcpp` follows the recommendations for ROS Core packages in the [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#code-coverage), and opts to use line coverage instead of branch coverage.
|
||||
`rclcpp` follows the recommendations for ROS Core packages in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#code-coverage), and opts to use line coverage instead of branch coverage.
|
||||
|
||||
This includes:
|
||||
|
||||
@@ -121,13 +121,25 @@ This includes:
|
||||
|
||||
Changes are required to make a best effort to keep or increase coverage before being accepted, but decreases are allowed if properly justified and accepted by reviewers.
|
||||
|
||||
Current coverage statistics can be viewed [here](https://ci.ros2.org/job/nightly_linux_coverage/lastCompletedBuild/cobertura/src_ros2_rclcpp_rclcpp_src_rclcpp/). A description of how coverage statistics are calculated is summarized in this page ["ROS 2 Onboarding Guide"](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#note-on-coverage-runs).
|
||||
|
||||
`rclcpp` has a line coverage `>= 95%`, which is calculated over all directories within `rclcpp` with the exception of the `experimental` directory.
|
||||
|
||||
### Performance [4.iv]
|
||||
|
||||
It is not yet defined if this package requires performance testing and how addresses this topic.
|
||||
`rclcpp` follows the recommendations for performance testing of C/C++ code in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#performance), and opts to do performance analysis on each release rather than each change.
|
||||
|
||||
The performance tests of `rclcpp` are located in the [test/benchmark directory](https://github.com/ros2/rclcpp/tree/master/rclcpp/test/benchmark).
|
||||
|
||||
System level performance benchmarks that cover features of `rclcpp` can be found at:
|
||||
* [Benchmarks](http://build.ros2.org/view/Rci/job/Rci__benchmark_ubuntu_focal_amd64/BenchmarkTable/)
|
||||
* [Performance](http://build.ros2.org/view/Rci/job/Rci__nightly-performance_ubuntu_focal_amd64/lastCompletedBuild/)
|
||||
|
||||
Changes that introduce regressions in performance must be adequately justified in order to be accepted and merged.
|
||||
|
||||
### Linters and Static Analysis [4.v]
|
||||
|
||||
`rclcpp` uses and passes all the ROS2 standard linters and static analysis tools for a C++ package as described in the [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#linters-and-static-analysis). Passing implies there are no linter/static errors when testing against CI of supported platforms.
|
||||
`rclcpp` uses and passes all the ROS2 standard linters and static analysis tools for a C++ package as described in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#linters-and-static-analysis). Passing implies there are no linter/static errors when testing against CI of supported platforms.
|
||||
|
||||
Currently nightly test results can be seen here:
|
||||
* [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/testReport/rclcpp/)
|
||||
@@ -151,49 +163,49 @@ It also has several test dependencies, which do not affect the resulting quality
|
||||
|
||||
The `libstatistics_collector` package provides lightweight aggregation utilities to collect statistics and measure message metrics.
|
||||
|
||||
It is **Quality Level 4**, see its [Quality Declaration document](https://github.com/ros-tooling/libstatistics_collector/tree/master/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros-tooling/libstatistics_collector/tree/master/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `rcl`
|
||||
|
||||
`rcl` a library to support implementation of language specific ROS 2 Client Libraries.
|
||||
|
||||
It is **Quality Level 4**, see its [Quality Declaration document](https://github.com/ros2/rcl/blob/master/rcl/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcl/blob/master/rcl/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `rcl_yaml_param_parser`
|
||||
|
||||
The `rcl_yaml_param_parser` package provides an API that is used to parse YAML configuration files which may be used to configure ROS and specific nodes.
|
||||
|
||||
It is **Quality Level 4**, see its [Quality Declaration document](https://github.com/ros2/rcl/tree/master/rcl_yaml_param_parser/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcl/tree/master/rcl_yaml_param_parser/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `rcpputils`
|
||||
|
||||
The `rcpputils` package provides an API which contains common utilities and data structures useful when programming in C++.
|
||||
|
||||
It is **Quality Level 4**, see its [Quality Declaration document](https://github.com/ros2/rcpputils/blob/master/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcpputils/blob/master/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `rcutils`
|
||||
|
||||
The `rcutils` package provides an API which contains common utilities and data structures useful when programming in C.
|
||||
|
||||
It is **Quality Level 4**, see its [Quality Declaration document](https://github.com/ros2/rcutils/blob/master/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcutils/blob/master/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `rmw`
|
||||
|
||||
`rmw` is the ROS 2 middleware library.
|
||||
|
||||
It is **Quality Level 4**, see its [Quality Declaration document](https://github.com/ros2/rmw/blob/master/rmw/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rmw/blob/master/rmw/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `statistics_msgs`
|
||||
|
||||
The `statistics_msgs` package contains ROS 2 message definitions for reporting statistics for topics and system resources.
|
||||
|
||||
It is **Quality Level 4**, see its [Quality Declaration document](https://github.com/ros2/rcl_interfaces/blob/master/statistics_msgs/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcl_interfaces/blob/master/statistics_msgs/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `tracetools`
|
||||
|
||||
The `tracetools` package provides utilities for instrumenting the code in `rcl` so that it may be traced for debugging and performance analysis.
|
||||
The `tracetools` package provides utilities for instrumenting the code in `rclcpp` so that it may be traced for debugging and performance analysis.
|
||||
|
||||
It is **Quality Level 4**, see its [Quality Declaration document](https://gitlab.com/micro-ROS/ros_tracing/ros2_tracing/-/blob/master/tracetools/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://gitlab.com/ros-tracing/ros2_tracing/-/blob/master/tracetools/QUALITY_DECLARATION.md).
|
||||
|
||||
### Direct Runtime non-ROS Dependency [5.iii]
|
||||
|
||||
|
||||
@@ -6,4 +6,4 @@ Visit the [rclcpp API documentation](http://docs.ros2.org/latest/api/rclcpp/) fo
|
||||
|
||||
## Quality Declaration
|
||||
|
||||
This package claims to be in the **Quality Level 4** category, see the [Quality Declaration](QUALITY_DECLARATION.md) for more details.
|
||||
This package claims to be in the **Quality Level 1** category, see the [Quality Declaration](QUALITY_DECLARATION.md) for more details.
|
||||
|
||||
141
rclcpp/doc/notes_on_statically_typed_parameters.md
Normal file
141
rclcpp/doc/notes_on_statically_typed_parameters.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Notes on statically typed parameters
|
||||
|
||||
## Introduction
|
||||
|
||||
Until ROS 2 Foxy, all parameters could change type anytime, except if the user installed a parameter callback to reject a change.
|
||||
This could generate confusing errors, for example:
|
||||
|
||||
```
|
||||
$ ros2 run demo_nodes_py listener &
|
||||
$ ros2 param set /listener use_sim_time not_a_boolean
|
||||
[ERROR] [1614712713.233733147] [listener]: use_sim_time parameter set to something besides a bool
|
||||
Set parameter successful
|
||||
$ ros2 param get /listener use_sim_time
|
||||
String value is: not_a_boolean
|
||||
```
|
||||
|
||||
For most use cases, having static parameter types is enough.
|
||||
This article documents some of the decisions that were made when implementing static parameter types enforcement in:
|
||||
|
||||
* https://github.com/ros2/rclcpp/pull/1522
|
||||
* https://github.com/ros2/rclpy/pull/683
|
||||
|
||||
## Allowing dynamically typed parameters
|
||||
|
||||
There might be some scenarios where dynamic typing is desired, so a `dynamic_typing` field was added to the [parameter descriptor](https://github.com/ros2/rcl_interfaces/blob/09b5ed93a733adb9deddc47f9a4a8c6e9f584667/rcl_interfaces/msg/ParameterDescriptor.msg#L25).
|
||||
It defaults to `false`.
|
||||
|
||||
For example:
|
||||
|
||||
```cpp
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
|
||||
node->declare_parameter("dynamically_typed_parameter", rclcpp::ParameterValue{}, descriptor);
|
||||
```
|
||||
|
||||
```py
|
||||
rcl_interfaces.msg.ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = True;
|
||||
|
||||
node.declare_parameter("dynamically_typed_parameter", None, descriptor);
|
||||
```
|
||||
|
||||
## How is the parameter type specified?
|
||||
|
||||
The parameter type will be inferred from the default value provided when declaring it.
|
||||
|
||||
## Statically typed parameters when allowing undeclared parameters
|
||||
|
||||
When undeclared parameters are allowed and a parameter is set without a previous declaration, the parameter will be dynamically typed.
|
||||
This is consistent with other allowed behaviors when undeclared parameters are allowed, e.g. trying to get an undeclared parameter returns "NOT_SET".
|
||||
Parameter declarations will remain special and dynamic or static typing will be used based on the parameter descriptor (default to static).
|
||||
|
||||
## Declaring a parameter without a default value
|
||||
|
||||
There might be cases were a default value does not make sense and the user must always provide an override.
|
||||
In those cases, the parameter type can be specified explicitly:
|
||||
|
||||
```cpp
|
||||
// method signature
|
||||
template<typename T>
|
||||
Node::declare_parameter<T>(std::string name, rcl_interfaces::msg::ParameterDescriptor = rcl_interfaces::msg::ParameterDescriptor{});
|
||||
// or alternatively
|
||||
Node::declare_parameter(std::string name, rclcpp::ParameterType type, rcl_interfaces::msg::ParameterDescriptor = rcl_interfaces::msg::ParameterDescriptor{});
|
||||
|
||||
// examples
|
||||
node->declare_parameter<int64_t>("my_integer_parameter"); // declare an integer parameter
|
||||
node->declare_parameter("another_integer_parameter", rclcpp::ParameterType::PARAMETER_INTEGER); // another way to do the same
|
||||
```
|
||||
|
||||
```py
|
||||
# method signature
|
||||
Node.declare_parameter(name: str, param_type: rclpy.Parameter.Type, descriptor: rcl_interfaces.msg.ParameterDescriptor = rcl_interfaces.msg.ParameterDescriptor())
|
||||
|
||||
# example
|
||||
node.declare_parameter('my_integer_parameter', rclpy.Parameter.Type.INTEGER); # declare an integer parameter
|
||||
```
|
||||
|
||||
If the parameter may be unused, but when used requires a parameter override, then you could conditionally declare it:
|
||||
|
||||
```cpp
|
||||
auto mode = node->declare_parameter("mode", "modeA"); // "mode" parameter is an string
|
||||
if (mode == "modeB") {
|
||||
node->declare_parameter<int64_t>("param_needed_when_mode_b"); // when "modeB", the user must provide "param_needed_when_mode_b"
|
||||
}
|
||||
```
|
||||
|
||||
## Other migration notes
|
||||
|
||||
Declaring a parameter with only a name is deprecated:
|
||||
|
||||
```cpp
|
||||
node->declare_parameter("my_param"); // this generates a build warning
|
||||
```
|
||||
|
||||
```py
|
||||
node.declare_parameter("my_param"); # this generates a python user warning
|
||||
```
|
||||
|
||||
Before, you could initialize a parameter with the "NOT SET" value (in cpp `rclcpp::ParameterValue{}`, in python `None`).
|
||||
Now this will throw an exception in both cases:
|
||||
|
||||
```cpp
|
||||
node->declare_parameter("my_param", rclcpp::ParameterValue{}); // not valid, will throw exception
|
||||
```
|
||||
|
||||
```py
|
||||
node.declare_parameter("my_param", None); # not valid, will raise error
|
||||
```
|
||||
|
||||
## Possible improvements
|
||||
|
||||
### Easier way to declare dynamically typed parameters
|
||||
|
||||
Declaring a dynamically typed parameter in `rclcpp` could be considered to be a bit verbose:
|
||||
|
||||
```cpp
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
|
||||
node->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);
|
||||
```
|
||||
|
||||
the following ways could be supported to make it simpler:
|
||||
|
||||
```cpp
|
||||
node->declare_parameter(name, rclcpp::PARAMETER_DYNAMIC);
|
||||
node->declare_parameter(name, default_value, rclcpp::PARAMETER_DYNAMIC);
|
||||
```
|
||||
|
||||
or alternatively:
|
||||
|
||||
```cpp
|
||||
node->declare_parameter(name, default_value, rclcpp::ParameterDescriptor{}.dynamic_typing());
|
||||
```
|
||||
|
||||
For `rclpy`, there's already a short way to do it:
|
||||
|
||||
```py
|
||||
node.declare_parameter(name, default_value, rclpy.ParameterDescriptor(dynamic_typing=true));
|
||||
```
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef RCLCPP__ALLOCATOR__ALLOCATOR_COMMON_HPP_
|
||||
#define RCLCPP__ALLOCATOR__ALLOCATOR_COMMON_HPP_
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "rcl/allocator.h"
|
||||
@@ -39,6 +40,22 @@ void * retyped_allocate(size_t size, void * untyped_allocator)
|
||||
return std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
|
||||
}
|
||||
|
||||
template<typename Alloc>
|
||||
void * retyped_zero_allocate(size_t number_of_elem, size_t size_of_elem, void * untyped_allocator)
|
||||
{
|
||||
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
|
||||
if (!typed_allocator) {
|
||||
throw std::runtime_error("Received incorrect allocator type");
|
||||
}
|
||||
size_t size = number_of_elem * size_of_elem;
|
||||
void * allocated_memory =
|
||||
std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
|
||||
if (allocated_memory) {
|
||||
std::memset(allocated_memory, 0, size);
|
||||
}
|
||||
return allocated_memory;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
void retyped_deallocate(void * untyped_pointer, void * untyped_allocator)
|
||||
{
|
||||
@@ -73,6 +90,7 @@ rcl_allocator_t get_rcl_allocator(Alloc & allocator)
|
||||
rcl_allocator_t rcl_allocator = rcl_get_default_allocator();
|
||||
#ifndef _WIN32
|
||||
rcl_allocator.allocate = &retyped_allocate<Alloc>;
|
||||
rcl_allocator.zero_allocate = &retyped_zero_allocate<Alloc>;
|
||||
rcl_allocator.deallocate = &retyped_deallocate<T, Alloc>;
|
||||
rcl_allocator.reallocate = &retyped_reallocate<T, Alloc>;
|
||||
rcl_allocator.state = &allocator;
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
AllocatorDeleter(const AllocatorDeleter<T> & a)
|
||||
explicit AllocatorDeleter(const AllocatorDeleter<T> & a)
|
||||
{
|
||||
allocator_ = a.get_allocator();
|
||||
}
|
||||
@@ -95,7 +95,7 @@ void set_allocator_for_deleter(AllocatorDeleter<T> * deleter, Alloc * alloc)
|
||||
template<typename Alloc, typename T>
|
||||
using Deleter = typename std::conditional<
|
||||
std::is_same<typename std::allocator_traits<Alloc>::template rebind_alloc<T>,
|
||||
typename std::allocator<void>::template rebind<T>::other>::value,
|
||||
std::allocator<T>>::value,
|
||||
std::default_delete<T>,
|
||||
AllocatorDeleter<Alloc>
|
||||
>::type;
|
||||
|
||||
@@ -47,14 +47,9 @@ struct AnyExecutable
|
||||
// These are used to keep the scope on the containing items
|
||||
rclcpp::CallbackGroup::SharedPtr callback_group;
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base;
|
||||
std::shared_ptr<void> data;
|
||||
};
|
||||
|
||||
namespace executor
|
||||
{
|
||||
|
||||
using AnyExecutable [[deprecated("use rclcpp::AnyExecutable instead")]] = AnyExecutable;
|
||||
|
||||
} // namespace executor
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__ANY_EXECUTABLE_HPP_
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
#ifndef RCLCPP__ANY_SERVICE_CALLBACK_HPP_
|
||||
#define RCLCPP__ANY_SERVICE_CALLBACK_HPP_
|
||||
|
||||
#include <variant>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
@@ -29,93 +31,204 @@
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<typename T, typename = void>
|
||||
struct can_be_nullptr : std::false_type {};
|
||||
|
||||
// Some lambdas define a comparison with nullptr,
|
||||
// but we see a warning that they can never be null when using it.
|
||||
// We also test if `T &` can be assigned to `nullptr` to avoid the issue.
|
||||
template<typename T>
|
||||
#ifdef __QNXNTO__
|
||||
struct can_be_nullptr<T, std::void_t<
|
||||
decltype(std::declval<T>() == nullptr)>>: std::true_type {};
|
||||
#else
|
||||
struct can_be_nullptr<T, std::void_t<
|
||||
decltype(std::declval<T>() == nullptr), decltype(std::declval<T &>() = nullptr)>>
|
||||
: std::true_type {};
|
||||
#endif
|
||||
} // namespace detail
|
||||
|
||||
// Forward declare
|
||||
template<typename ServiceT>
|
||||
class Service;
|
||||
|
||||
template<typename ServiceT>
|
||||
class AnyServiceCallback
|
||||
{
|
||||
private:
|
||||
using SharedPtrCallback = std::function<
|
||||
void (
|
||||
const std::shared_ptr<typename ServiceT::Request>,
|
||||
std::shared_ptr<typename ServiceT::Response>
|
||||
)>;
|
||||
using SharedPtrWithRequestHeaderCallback = std::function<
|
||||
void (
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<typename ServiceT::Request>,
|
||||
std::shared_ptr<typename ServiceT::Response>
|
||||
)>;
|
||||
|
||||
SharedPtrCallback shared_ptr_callback_;
|
||||
SharedPtrWithRequestHeaderCallback shared_ptr_with_request_header_callback_;
|
||||
|
||||
public:
|
||||
AnyServiceCallback()
|
||||
: shared_ptr_callback_(nullptr), shared_ptr_with_request_header_callback_(nullptr)
|
||||
: callback_(std::monostate{})
|
||||
{}
|
||||
|
||||
AnyServiceCallback(const AnyServiceCallback &) = default;
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
typename std::enable_if_t<!detail::can_be_nullptr<CallbackT>::value, int> = 0>
|
||||
void
|
||||
set(CallbackT && callback)
|
||||
{
|
||||
// Workaround Windows issue with std::bind
|
||||
if constexpr (
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrCallback
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
void set(CallbackT callback)
|
||||
{
|
||||
shared_ptr_callback_ = callback;
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrCallback>(callback);
|
||||
} else if constexpr ( // NOLINT, can't satisfy both cpplint and uncrustify
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrWithRequestHeaderCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
|
||||
} else if constexpr ( // NOLINT
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrDeferResponseCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
|
||||
} else if constexpr ( // NOLINT
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrDeferResponseCallbackWithServiceHandle
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
|
||||
} else {
|
||||
// the else clause is not needed, but anyways we should only be doing this instead
|
||||
// of all the above workaround ...
|
||||
callback_ = std::forward<CallbackT>(callback);
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
typename std::enable_if_t<detail::can_be_nullptr<CallbackT>::value, int> = 0>
|
||||
void
|
||||
set(CallbackT && callback)
|
||||
{
|
||||
if (!callback) {
|
||||
throw std::invalid_argument("AnyServiceCallback::set(): callback cannot be nullptr");
|
||||
}
|
||||
// Workaround Windows issue with std::bind
|
||||
if constexpr (
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrCallback>(callback);
|
||||
} else if constexpr ( // NOLINT
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrWithRequestHeaderCallback
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
void set(CallbackT callback)
|
||||
{
|
||||
shared_ptr_with_request_header_callback_ = callback;
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
|
||||
} else if constexpr ( // NOLINT
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrDeferResponseCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
|
||||
} else if constexpr ( // NOLINT
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrDeferResponseCallbackWithServiceHandle
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
|
||||
} else {
|
||||
// the else clause is not needed, but anyways we should only be doing this instead
|
||||
// of all the above workaround ...
|
||||
callback_ = std::forward<CallbackT>(callback);
|
||||
}
|
||||
}
|
||||
|
||||
void dispatch(
|
||||
std::shared_ptr<rmw_request_id_t> request_header,
|
||||
std::shared_ptr<typename ServiceT::Request> request,
|
||||
std::shared_ptr<typename ServiceT::Response> response)
|
||||
// template<typename Allocator = std::allocator<typename ServiceT::Response>>
|
||||
std::shared_ptr<typename ServiceT::Response>
|
||||
dispatch(
|
||||
const std::shared_ptr<rclcpp::Service<ServiceT>> & service_handle,
|
||||
const std::shared_ptr<rmw_request_id_t> & request_header,
|
||||
std::shared_ptr<typename ServiceT::Request> request)
|
||||
{
|
||||
TRACEPOINT(callback_start, (const void *)this, false);
|
||||
if (shared_ptr_callback_ != nullptr) {
|
||||
(void)request_header;
|
||||
shared_ptr_callback_(request, response);
|
||||
} else if (shared_ptr_with_request_header_callback_ != nullptr) {
|
||||
shared_ptr_with_request_header_callback_(request_header, request, response);
|
||||
} else {
|
||||
throw std::runtime_error("unexpected request without any callback set");
|
||||
TRACEPOINT(callback_start, static_cast<const void *>(this), false);
|
||||
if (std::holds_alternative<std::monostate>(callback_)) {
|
||||
// TODO(ivanpauno): Remove the set method, and force the users of this class
|
||||
// to pass a callback at construnciton.
|
||||
throw std::runtime_error{"unexpected request without any callback set"};
|
||||
}
|
||||
TRACEPOINT(callback_end, (const void *)this);
|
||||
if (std::holds_alternative<SharedPtrDeferResponseCallback>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrDeferResponseCallback>(callback_);
|
||||
cb(request_header, std::move(request));
|
||||
return nullptr;
|
||||
}
|
||||
if (std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_);
|
||||
cb(service_handle, request_header, std::move(request));
|
||||
return nullptr;
|
||||
}
|
||||
// auto response = allocate_shared<typename ServiceT::Response, Allocator>();
|
||||
auto response = std::make_shared<typename ServiceT::Response>();
|
||||
if (std::holds_alternative<SharedPtrCallback>(callback_)) {
|
||||
(void)request_header;
|
||||
const auto & cb = std::get<SharedPtrCallback>(callback_);
|
||||
cb(std::move(request), response);
|
||||
} else if (std::holds_alternative<SharedPtrWithRequestHeaderCallback>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrWithRequestHeaderCallback>(callback_);
|
||||
cb(request_header, std::move(request), response);
|
||||
}
|
||||
TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
return response;
|
||||
}
|
||||
|
||||
void register_callback_for_tracing()
|
||||
{
|
||||
#ifndef TRACETOOLS_DISABLED
|
||||
if (shared_ptr_callback_) {
|
||||
TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
(const void *)this,
|
||||
get_symbol(shared_ptr_callback_));
|
||||
} else if (shared_ptr_with_request_header_callback_) {
|
||||
TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
(const void *)this,
|
||||
get_symbol(shared_ptr_with_request_header_callback_));
|
||||
}
|
||||
std::visit(
|
||||
[this](auto && arg) {
|
||||
TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
static_cast<const void *>(this),
|
||||
tracetools::get_symbol(arg));
|
||||
}, callback_);
|
||||
#endif // TRACETOOLS_DISABLED
|
||||
}
|
||||
|
||||
private:
|
||||
using SharedPtrCallback = std::function<
|
||||
void (
|
||||
std::shared_ptr<typename ServiceT::Request>,
|
||||
std::shared_ptr<typename ServiceT::Response>
|
||||
)>;
|
||||
using SharedPtrWithRequestHeaderCallback = std::function<
|
||||
void (
|
||||
std::shared_ptr<rmw_request_id_t>,
|
||||
std::shared_ptr<typename ServiceT::Request>,
|
||||
std::shared_ptr<typename ServiceT::Response>
|
||||
)>;
|
||||
using SharedPtrDeferResponseCallback = std::function<
|
||||
void (
|
||||
std::shared_ptr<rmw_request_id_t>,
|
||||
std::shared_ptr<typename ServiceT::Request>
|
||||
)>;
|
||||
using SharedPtrDeferResponseCallbackWithServiceHandle = std::function<
|
||||
void (
|
||||
std::shared_ptr<rclcpp::Service<ServiceT>>,
|
||||
std::shared_ptr<rmw_request_id_t>,
|
||||
std::shared_ptr<typename ServiceT::Request>
|
||||
)>;
|
||||
|
||||
std::variant<
|
||||
std::monostate,
|
||||
SharedPtrCallback,
|
||||
SharedPtrWithRequestHeaderCallback,
|
||||
SharedPtrDeferResponseCallback,
|
||||
SharedPtrDeferResponseCallbackWithServiceHandle> callback_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,11 +16,14 @@
|
||||
#define RCLCPP__CALLBACK_GROUP_HPP_
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/client.hpp"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/publisher_base.hpp"
|
||||
#include "rclcpp/service.hpp"
|
||||
#include "rclcpp/subscription_base.hpp"
|
||||
@@ -56,8 +59,48 @@ class CallbackGroup
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(CallbackGroup)
|
||||
|
||||
/// Constructor for CallbackGroup.
|
||||
/**
|
||||
* Callback Groups have a type, either 'Mutually Exclusive' or 'Reentrant'
|
||||
* and when creating one the type must be specified.
|
||||
*
|
||||
* Callbacks in Reentrant Callback Groups must be able to:
|
||||
* - run at the same time as themselves (reentrant)
|
||||
* - run at the same time as other callbacks in their group
|
||||
* - run at the same time as other callbacks in other groups
|
||||
*
|
||||
* Callbacks in Mutually Exclusive Callback Groups:
|
||||
* - will not be run multiple times simultaneously (non-reentrant)
|
||||
* - will not be run at the same time as other callbacks in their group
|
||||
* - but must run at the same time as callbacks in other groups
|
||||
*
|
||||
* Additionally, callback groups have a property which determines whether or
|
||||
* not they are added to an executor with their associated node automatically.
|
||||
* When creating a callback group the automatically_add_to_executor_with_node
|
||||
* argument determines this behavior, and if true it will cause the newly
|
||||
* created callback group to be added to an executor with the node when the
|
||||
* Executor::add_node method is used.
|
||||
* If false, this callback group will not be added automatically and would
|
||||
* have to be added to an executor manually using the
|
||||
* Executor::add_callback_group method.
|
||||
*
|
||||
* Whether the node was added to the executor before creating the callback
|
||||
* group, or after, is irrelevant; the callback group will be automatically
|
||||
* added to the executor in either case.
|
||||
*
|
||||
* \param[in] group_type The type of the callback group.
|
||||
* \param[in] automatically_add_to_executor_with_node A boolean that
|
||||
* determines whether a callback group is automatically added to an executor
|
||||
* with the node with which it is associated.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
explicit CallbackGroup(CallbackGroupType group_type);
|
||||
explicit CallbackGroup(
|
||||
CallbackGroupType group_type,
|
||||
bool automatically_add_to_executor_with_node = true);
|
||||
|
||||
/// Default destructor.
|
||||
RCLCPP_PUBLIC
|
||||
~CallbackGroup();
|
||||
|
||||
template<typename Function>
|
||||
rclcpp::SubscriptionBase::SharedPtr
|
||||
@@ -102,6 +145,49 @@ public:
|
||||
const CallbackGroupType &
|
||||
type() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void collect_all_ptrs(
|
||||
std::function<void(const rclcpp::SubscriptionBase::SharedPtr &)> sub_func,
|
||||
std::function<void(const rclcpp::ServiceBase::SharedPtr &)> service_func,
|
||||
std::function<void(const rclcpp::ClientBase::SharedPtr &)> client_func,
|
||||
std::function<void(const rclcpp::TimerBase::SharedPtr &)> timer_func,
|
||||
std::function<void(const rclcpp::Waitable::SharedPtr &)> waitable_func) const;
|
||||
|
||||
/// Return a reference to the 'associated with executor' atomic boolean.
|
||||
/**
|
||||
* When a callback group is added to an executor this boolean is checked
|
||||
* to ensure it has not already been added to another executor.
|
||||
* If it has not been, then this boolean is set to true to indicate it is
|
||||
* now associated with an executor.
|
||||
*
|
||||
* When the callback group is removed from the executor, this atomic boolean
|
||||
* is set back to false.
|
||||
*
|
||||
* \return the 'associated with executor' atomic boolean
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::atomic_bool &
|
||||
get_associated_with_executor_atomic();
|
||||
|
||||
/// Return true if this callback group should be automatically added to an executor by the node.
|
||||
/**
|
||||
* \return boolean true if this callback group should be automatically added
|
||||
* to an executor when the associated node is added, otherwise false.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
automatically_add_to_executor_with_node() const;
|
||||
|
||||
/// Defer creating the notify guard condition and return it.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::GuardCondition::SharedPtr
|
||||
get_notify_guard_condition(const rclcpp::Context::SharedPtr context_ptr);
|
||||
|
||||
/// Trigger the notify guard condition.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
trigger_notify_guard_condition();
|
||||
|
||||
protected:
|
||||
RCLCPP_DISABLE_COPY(CallbackGroup)
|
||||
|
||||
@@ -136,12 +222,17 @@ protected:
|
||||
CallbackGroupType type_;
|
||||
// Mutex to protect the subsequent vectors of pointers.
|
||||
mutable std::mutex mutex_;
|
||||
std::atomic_bool associated_with_executor_;
|
||||
std::vector<rclcpp::SubscriptionBase::WeakPtr> subscription_ptrs_;
|
||||
std::vector<rclcpp::TimerBase::WeakPtr> timer_ptrs_;
|
||||
std::vector<rclcpp::ServiceBase::WeakPtr> service_ptrs_;
|
||||
std::vector<rclcpp::ClientBase::WeakPtr> client_ptrs_;
|
||||
std::vector<rclcpp::Waitable::WeakPtr> waitable_ptrs_;
|
||||
std::atomic_bool can_be_taken_from_;
|
||||
const bool automatically_add_to_executor_with_node_;
|
||||
// defer the creation of the guard condition
|
||||
std::shared_ptr<rclcpp::GuardCondition> notify_guard_condition_ = nullptr;
|
||||
std::recursive_mutex notify_guard_condition_mutex_;
|
||||
|
||||
private:
|
||||
template<typename TypeT, typename Function>
|
||||
@@ -159,13 +250,6 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
namespace callback_group
|
||||
{
|
||||
|
||||
using CallbackGroupType [[deprecated("use rclcpp::CallbackGroupType instead")]] = CallbackGroupType;
|
||||
using CallbackGroup [[deprecated("use rclcpp::CallbackGroup instead")]] = CallbackGroup;
|
||||
|
||||
} // namespace callback_group
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__CALLBACK_GROUP_HPP_
|
||||
|
||||
@@ -17,34 +17,103 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional> // NOLINT, cpplint doesn't think this is a cpp std header
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <variant> // NOLINT
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/client.h"
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/event_callback.h"
|
||||
#include "rcl/wait.h"
|
||||
|
||||
#include "rclcpp/detail/cpp_callback_trampoline.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/expand_topic_or_service_name.hpp"
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/expand_topic_or_service_name.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "rcutils/logging_macros.h"
|
||||
|
||||
#include "rmw/error_handling.h"
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<typename FutureT>
|
||||
struct FutureAndRequestId
|
||||
{
|
||||
FutureT future;
|
||||
int64_t request_id;
|
||||
|
||||
FutureAndRequestId(FutureT impl, int64_t req_id)
|
||||
: future(std::move(impl)), request_id(req_id)
|
||||
{}
|
||||
|
||||
/// Allow implicit conversions to `std::future` by reference.
|
||||
operator FutureT &() {return this->future;}
|
||||
|
||||
/// Deprecated, use the `future` member variable instead.
|
||||
/**
|
||||
* Allow implicit conversions to `std::future` by value.
|
||||
* \deprecated
|
||||
*/
|
||||
[[deprecated("FutureAndRequestId: use .future instead of an implicit conversion")]]
|
||||
operator FutureT() {return this->future;}
|
||||
|
||||
// delegate future like methods in the std::future impl_
|
||||
|
||||
/// See std::future::get().
|
||||
auto get() {return this->future.get();}
|
||||
/// See std::future::valid().
|
||||
bool valid() const noexcept {return this->future.valid();}
|
||||
/// See std::future::wait().
|
||||
void wait() const {return this->future.wait();}
|
||||
/// See std::future::wait_for().
|
||||
template<class Rep, class Period>
|
||||
std::future_status wait_for(
|
||||
const std::chrono::duration<Rep, Period> & timeout_duration) const
|
||||
{
|
||||
return this->future.wait_for(timeout_duration);
|
||||
}
|
||||
/// See std::future::wait_until().
|
||||
template<class Clock, class Duration>
|
||||
std::future_status wait_until(
|
||||
const std::chrono::time_point<Clock, Duration> & timeout_time) const
|
||||
{
|
||||
return this->future.wait_until(timeout_time);
|
||||
}
|
||||
|
||||
// Rule of five, we could use the rule of zero here, but better be explicit as some of the
|
||||
// methods are deleted.
|
||||
|
||||
/// Move constructor.
|
||||
FutureAndRequestId(FutureAndRequestId && other) noexcept = default;
|
||||
/// Deleted copy constructor, each instance is a unique owner of the future.
|
||||
FutureAndRequestId(const FutureAndRequestId & other) = delete;
|
||||
/// Move assignment.
|
||||
FutureAndRequestId & operator=(FutureAndRequestId && other) noexcept = default;
|
||||
/// Deleted copy assignment, each instance is a unique owner of the future.
|
||||
FutureAndRequestId & operator=(const FutureAndRequestId & other) = delete;
|
||||
/// Destructor.
|
||||
~FutureAndRequestId() = default;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
namespace node_interfaces
|
||||
{
|
||||
class NodeBaseInterface;
|
||||
@@ -150,6 +219,122 @@ public:
|
||||
bool
|
||||
exchange_in_use_by_wait_set_state(bool in_use_state);
|
||||
|
||||
/// Get the actual request publsher 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 client, 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 request publsher qos settings.
|
||||
* \throws std::runtime_error if failed to get qos settings
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::QoS
|
||||
get_request_publisher_actual_qos() const;
|
||||
|
||||
/// Get the actual response subscription 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 client, 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 response subscription qos settings.
|
||||
* \throws std::runtime_error if failed to get qos settings
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::QoS
|
||||
get_response_subscription_actual_qos() const;
|
||||
|
||||
/// Set a callback to be called when each new response is received.
|
||||
/**
|
||||
* The callback receives a size_t which is the number of responses received
|
||||
* since the last time this callback was called.
|
||||
* Normally this is 1, but can be > 1 if responses were received before any
|
||||
* callback was set.
|
||||
*
|
||||
* Since this callback is called from the middleware, you should aim to make
|
||||
* it fast and not blocking.
|
||||
* If you need to do a lot of work or wait for some other event, you should
|
||||
* spin it off to another thread, otherwise you risk blocking the middleware.
|
||||
*
|
||||
* Calling it again will clear any previously set callback.
|
||||
*
|
||||
* An exception will be thrown if the callback is not callable.
|
||||
*
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* If you want more information available in the callback, like the client
|
||||
* or other information, you may use a lambda with captures or std::bind.
|
||||
*
|
||||
* \sa rmw_client_set_on_new_response_callback
|
||||
* \sa rcl_client_set_on_new_response_callback
|
||||
*
|
||||
* \param[in] callback functor to be called when a new response is received
|
||||
*/
|
||||
void
|
||||
set_on_new_response_callback(std::function<void(size_t)> callback)
|
||||
{
|
||||
if (!callback) {
|
||||
throw std::invalid_argument(
|
||||
"The callback passed to set_on_new_response_callback "
|
||||
"is not callable.");
|
||||
}
|
||||
|
||||
auto new_callback =
|
||||
[callback, this](size_t number_of_responses) {
|
||||
try {
|
||||
callback(number_of_responses);
|
||||
} catch (const std::exception & exception) {
|
||||
RCLCPP_ERROR_STREAM(
|
||||
node_logger_,
|
||||
"rclcpp::ClientBase@" << this <<
|
||||
" caught " << rmw::impl::cpp::demangle(exception) <<
|
||||
" exception in user-provided callback for the 'on new response' callback: " <<
|
||||
exception.what());
|
||||
} catch (...) {
|
||||
RCLCPP_ERROR_STREAM(
|
||||
node_logger_,
|
||||
"rclcpp::ClientBase@" << this <<
|
||||
" caught unhandled exception in user-provided callback " <<
|
||||
"for the 'on new response' callback");
|
||||
}
|
||||
};
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
|
||||
// Set it temporarily to the new callback, while we replace the old one.
|
||||
// This two-step setting, prevents a gap where the old std::function has
|
||||
// been replaced but the middleware hasn't been told about the new one yet.
|
||||
set_on_new_response_callback(
|
||||
rclcpp::detail::cpp_callback_trampoline<const void *, size_t>,
|
||||
static_cast<const void *>(&new_callback));
|
||||
|
||||
// Store the std::function to keep it in scope, also overwrites the existing one.
|
||||
on_new_response_callback_ = new_callback;
|
||||
|
||||
// Set it again, now using the permanent storage.
|
||||
set_on_new_response_callback(
|
||||
rclcpp::detail::cpp_callback_trampoline<const void *, size_t>,
|
||||
static_cast<const void *>(&on_new_response_callback_));
|
||||
}
|
||||
|
||||
/// Unset the callback registered for new responses, if any.
|
||||
void
|
||||
clear_on_new_response_callback()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
if (on_new_response_callback_) {
|
||||
set_on_new_response_callback(nullptr, nullptr);
|
||||
on_new_response_callback_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
RCLCPP_DISABLE_COPY(ClientBase)
|
||||
|
||||
@@ -165,19 +350,30 @@ protected:
|
||||
const rcl_node_t *
|
||||
get_rcl_node_handle() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_on_new_response_callback(rcl_event_callback_t callback, const void * user_data);
|
||||
|
||||
rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_;
|
||||
std::shared_ptr<rcl_node_t> node_handle_;
|
||||
std::shared_ptr<rclcpp::Context> context_;
|
||||
rclcpp::Logger node_logger_;
|
||||
|
||||
std::shared_ptr<rcl_client_t> client_handle_;
|
||||
|
||||
std::atomic<bool> in_use_by_wait_set_{false};
|
||||
|
||||
std::recursive_mutex callback_mutex_;
|
||||
std::function<void(size_t)> on_new_response_callback_{nullptr};
|
||||
};
|
||||
|
||||
template<typename ServiceT>
|
||||
class Client : public ClientBase
|
||||
{
|
||||
public:
|
||||
using Request = typename ServiceT::Request;
|
||||
using Response = typename ServiceT::Response;
|
||||
|
||||
using SharedRequest = typename ServiceT::Request::SharedPtr;
|
||||
using SharedResponse = typename ServiceT::Response::SharedPtr;
|
||||
|
||||
@@ -187,6 +383,7 @@ public:
|
||||
using SharedPromise = std::shared_ptr<Promise>;
|
||||
using SharedPromiseWithRequest = std::shared_ptr<PromiseWithRequest>;
|
||||
|
||||
using Future = std::future<SharedResponse>;
|
||||
using SharedFuture = std::shared_future<SharedResponse>;
|
||||
using SharedFutureWithRequest = std::shared_future<std::pair<SharedRequest, SharedResponse>>;
|
||||
|
||||
@@ -195,6 +392,64 @@ public:
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Client)
|
||||
|
||||
/// A convenient Client::Future and request id pair.
|
||||
/**
|
||||
* Public members:
|
||||
* - future: a std::future<SharedResponse>.
|
||||
* - request_id: the request id associated with the future.
|
||||
*
|
||||
* All the other methods are equivalent to the ones std::future provides.
|
||||
*/
|
||||
struct FutureAndRequestId
|
||||
: detail::FutureAndRequestId<std::future<SharedResponse>>
|
||||
{
|
||||
using detail::FutureAndRequestId<std::future<SharedResponse>>::FutureAndRequestId;
|
||||
|
||||
/// Deprecated, use `.future.share()` instead.
|
||||
/**
|
||||
* Allow implicit conversions to `std::shared_future` by value.
|
||||
* \deprecated
|
||||
*/
|
||||
[[deprecated(
|
||||
"FutureAndRequestId: use .future.share() instead of an implicit conversion")]]
|
||||
operator SharedFuture() {return this->future.share();}
|
||||
|
||||
// delegate future like methods in the std::future impl_
|
||||
|
||||
/// See std::future::share().
|
||||
SharedFuture share() noexcept {return this->future.share();}
|
||||
};
|
||||
|
||||
/// A convenient Client::SharedFuture and request id pair.
|
||||
/**
|
||||
* Public members:
|
||||
* - future: a std::shared_future<SharedResponse>.
|
||||
* - request_id: the request id associated with the future.
|
||||
*
|
||||
* All the other methods are equivalent to the ones std::shared_future provides.
|
||||
*/
|
||||
struct SharedFutureAndRequestId
|
||||
: detail::FutureAndRequestId<std::shared_future<SharedResponse>>
|
||||
{
|
||||
using detail::FutureAndRequestId<std::shared_future<SharedResponse>>::FutureAndRequestId;
|
||||
};
|
||||
|
||||
/// A convenient Client::SharedFutureWithRequest and request id pair.
|
||||
/**
|
||||
* Public members:
|
||||
* - future: a std::shared_future<SharedResponse>.
|
||||
* - request_id: the request id associated with the future.
|
||||
*
|
||||
* All the other methods are equivalent to the ones std::shared_future provides.
|
||||
*/
|
||||
struct SharedFutureWithRequestAndRequestId
|
||||
: detail::FutureAndRequestId<std::shared_future<std::pair<SharedRequest, SharedResponse>>>
|
||||
{
|
||||
using detail::FutureAndRequestId<
|
||||
std::shared_future<std::pair<SharedRequest, SharedResponse>>
|
||||
>::FutureAndRequestId;
|
||||
};
|
||||
|
||||
/// Default constructor.
|
||||
/**
|
||||
* The constructor for a Client is almost never called directly.
|
||||
@@ -292,34 +547,89 @@ public:
|
||||
std::shared_ptr<rmw_request_id_t> request_header,
|
||||
std::shared_ptr<void> response) override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(pending_requests_mutex_);
|
||||
auto typed_response = std::static_pointer_cast<typename ServiceT::Response>(response);
|
||||
int64_t sequence_number = request_header->sequence_number;
|
||||
// TODO(esteve) this should throw instead since it is not expected to happen in the first place
|
||||
if (this->pending_requests_.count(sequence_number) == 0) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Received invalid sequence number. Ignoring...");
|
||||
std::optional<CallbackInfoVariant>
|
||||
optional_pending_request = this->get_and_erase_pending_request(request_header->sequence_number);
|
||||
if (!optional_pending_request) {
|
||||
return;
|
||||
}
|
||||
auto tuple = this->pending_requests_[sequence_number];
|
||||
auto call_promise = std::get<0>(tuple);
|
||||
auto callback = std::get<1>(tuple);
|
||||
auto future = std::get<2>(tuple);
|
||||
this->pending_requests_.erase(sequence_number);
|
||||
// Unlock here to allow the service to be called recursively from one of its callbacks.
|
||||
lock.unlock();
|
||||
|
||||
call_promise->set_value(typed_response);
|
||||
callback(future);
|
||||
auto & value = *optional_pending_request;
|
||||
auto typed_response = std::static_pointer_cast<typename ServiceT::Response>(
|
||||
std::move(response));
|
||||
if (std::holds_alternative<Promise>(value)) {
|
||||
auto & promise = std::get<Promise>(value);
|
||||
promise.set_value(std::move(typed_response));
|
||||
} else if (std::holds_alternative<CallbackTypeValueVariant>(value)) {
|
||||
auto & inner = std::get<CallbackTypeValueVariant>(value);
|
||||
const auto & callback = std::get<CallbackType>(inner);
|
||||
auto & promise = std::get<Promise>(inner);
|
||||
auto & future = std::get<SharedFuture>(inner);
|
||||
promise.set_value(std::move(typed_response));
|
||||
callback(std::move(future));
|
||||
} else if (std::holds_alternative<CallbackWithRequestTypeValueVariant>(value)) {
|
||||
auto & inner = std::get<CallbackWithRequestTypeValueVariant>(value);
|
||||
const auto & callback = std::get<CallbackWithRequestType>(inner);
|
||||
auto & promise = std::get<PromiseWithRequest>(inner);
|
||||
auto & future = std::get<SharedFutureWithRequest>(inner);
|
||||
auto & request = std::get<SharedRequest>(inner);
|
||||
promise.set_value(std::make_pair(std::move(request), std::move(typed_response)));
|
||||
callback(std::move(future));
|
||||
}
|
||||
}
|
||||
|
||||
SharedFuture
|
||||
/// Send a request to the service server.
|
||||
/**
|
||||
* This method returns a `FutureAndRequestId` instance
|
||||
* that can be passed to Executor::spin_until_future_complete() to
|
||||
* wait until it has been completed.
|
||||
*
|
||||
* If the future never completes,
|
||||
* e.g. the call to Executor::spin_until_future_complete() times out,
|
||||
* Client::remove_pending_request() must be called to clean the client internal state.
|
||||
* Not doing so will make the `Client` instance to use more memory each time a response is not
|
||||
* received from the service server.
|
||||
*
|
||||
* ```cpp
|
||||
* auto future = client->async_send_request(my_request);
|
||||
* if (
|
||||
* rclcpp::FutureReturnCode::TIMEOUT ==
|
||||
* executor->spin_until_future_complete(future, timeout))
|
||||
* {
|
||||
* client->remove_pending_request(future);
|
||||
* // handle timeout
|
||||
* } else {
|
||||
* handle_response(future.get());
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* \param[in] request request to be send.
|
||||
* \return a FutureAndRequestId instance.
|
||||
*/
|
||||
FutureAndRequestId
|
||||
async_send_request(SharedRequest request)
|
||||
{
|
||||
return async_send_request(request, [](SharedFuture) {});
|
||||
Promise promise;
|
||||
auto future = promise.get_future();
|
||||
auto req_id = async_send_request_impl(
|
||||
*request,
|
||||
std::move(promise));
|
||||
return FutureAndRequestId(std::move(future), req_id);
|
||||
}
|
||||
|
||||
/// Send a request to the service server and schedule a callback in the executor.
|
||||
/**
|
||||
* Similar to the previous overload, but a callback will automatically be called when a response is received.
|
||||
*
|
||||
* If the callback is never called, because we never got a reply for the service server, remove_pending_request()
|
||||
* has to be called with the returned request id or prune_pending_requests().
|
||||
* Not doing so will make the `Client` instance use more memory each time a response is not
|
||||
* received from the service server.
|
||||
* In this case, it's convenient to setup a timer to cleanup the pending requests.
|
||||
* See for example the `examples_rclcpp_async_client` package in https://github.com/ros2/examples.
|
||||
*
|
||||
* \param[in] request request to be send.
|
||||
* \param[in] cb callback that will be called when we get a response for this request.
|
||||
* \return the request id representing the request just sent.
|
||||
*/
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
@@ -329,23 +639,28 @@ public:
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
SharedFuture
|
||||
SharedFutureAndRequestId
|
||||
async_send_request(SharedRequest request, CallbackT && cb)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
|
||||
int64_t sequence_number;
|
||||
rcl_ret_t ret = rcl_send_request(get_client_handle().get(), request.get(), &sequence_number);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send request");
|
||||
}
|
||||
|
||||
SharedPromise call_promise = std::make_shared<Promise>();
|
||||
SharedFuture f(call_promise->get_future());
|
||||
pending_requests_[sequence_number] =
|
||||
std::make_tuple(call_promise, std::forward<CallbackType>(cb), f);
|
||||
return f;
|
||||
Promise promise;
|
||||
auto shared_future = promise.get_future().share();
|
||||
auto req_id = async_send_request_impl(
|
||||
*request,
|
||||
std::make_tuple(
|
||||
CallbackType{std::forward<CallbackT>(cb)},
|
||||
shared_future,
|
||||
std::move(promise)));
|
||||
return SharedFutureAndRequestId{std::move(shared_future), req_id};
|
||||
}
|
||||
|
||||
/// Send a request to the service server and schedule a callback in the executor.
|
||||
/**
|
||||
* Similar to the previous method, but you can get both the request and response in the callback.
|
||||
*
|
||||
* \param[in] request request to be send.
|
||||
* \param[in] cb callback that will be called when we get a response for this request.
|
||||
* \return the request id representing the request just sent.
|
||||
*/
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
@@ -355,27 +670,165 @@ public:
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
SharedFutureWithRequest
|
||||
SharedFutureWithRequestAndRequestId
|
||||
async_send_request(SharedRequest request, CallbackT && cb)
|
||||
{
|
||||
SharedPromiseWithRequest promise = std::make_shared<PromiseWithRequest>();
|
||||
SharedFutureWithRequest future_with_request(promise->get_future());
|
||||
|
||||
auto wrapping_cb = [future_with_request, promise, request, &cb](SharedFuture future) {
|
||||
auto response = future.get();
|
||||
promise->set_value(std::make_pair(request, response));
|
||||
cb(future_with_request);
|
||||
};
|
||||
|
||||
async_send_request(request, wrapping_cb);
|
||||
|
||||
return future_with_request;
|
||||
PromiseWithRequest promise;
|
||||
auto shared_future = promise.get_future().share();
|
||||
auto req_id = async_send_request_impl(
|
||||
*request,
|
||||
std::make_tuple(
|
||||
CallbackWithRequestType{std::forward<CallbackT>(cb)},
|
||||
request,
|
||||
shared_future,
|
||||
std::move(promise)));
|
||||
return SharedFutureWithRequestAndRequestId{std::move(shared_future), req_id};
|
||||
}
|
||||
|
||||
/// Cleanup a pending request.
|
||||
/**
|
||||
* This notifies the client that we have waited long enough for a response from the server
|
||||
* to come, we have given up and we are not waiting for a response anymore.
|
||||
*
|
||||
* Not calling this will make the client start using more memory for each request
|
||||
* that never got a reply from the server.
|
||||
*
|
||||
* \param[in] request_id request id returned by async_send_request().
|
||||
* \return true when a pending request was removed, false if not (e.g. a response was received).
|
||||
*/
|
||||
bool
|
||||
remove_pending_request(int64_t request_id)
|
||||
{
|
||||
std::lock_guard guard(pending_requests_mutex_);
|
||||
return pending_requests_.erase(request_id) != 0u;
|
||||
}
|
||||
|
||||
/// Cleanup a pending request.
|
||||
/**
|
||||
* Convenient overload, same as:
|
||||
*
|
||||
* `Client::remove_pending_request(this, future.request_id)`.
|
||||
*/
|
||||
bool
|
||||
remove_pending_request(const FutureAndRequestId & future)
|
||||
{
|
||||
return this->remove_pending_request(future.request_id);
|
||||
}
|
||||
|
||||
/// Cleanup a pending request.
|
||||
/**
|
||||
* Convenient overload, same as:
|
||||
*
|
||||
* `Client::remove_pending_request(this, future.request_id)`.
|
||||
*/
|
||||
bool
|
||||
remove_pending_request(const SharedFutureAndRequestId & future)
|
||||
{
|
||||
return this->remove_pending_request(future.request_id);
|
||||
}
|
||||
|
||||
/// Cleanup a pending request.
|
||||
/**
|
||||
* Convenient overload, same as:
|
||||
*
|
||||
* `Client::remove_pending_request(this, future.request_id)`.
|
||||
*/
|
||||
bool
|
||||
remove_pending_request(const SharedFutureWithRequestAndRequestId & future)
|
||||
{
|
||||
return this->remove_pending_request(future.request_id);
|
||||
}
|
||||
|
||||
/// Clean all pending requests.
|
||||
/**
|
||||
* \return number of pending requests that were removed.
|
||||
*/
|
||||
size_t
|
||||
prune_pending_requests()
|
||||
{
|
||||
std::lock_guard guard(pending_requests_mutex_);
|
||||
auto ret = pending_requests_.size();
|
||||
pending_requests_.clear();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Clean all pending requests older than a time_point.
|
||||
/**
|
||||
* \param[in] time_point Requests that were sent before this point are going to be removed.
|
||||
* \param[inout] pruned_requests Removed requests id will be pushed to the vector
|
||||
* if a pointer is provided.
|
||||
* \return number of pending requests that were removed.
|
||||
*/
|
||||
template<typename AllocatorT = std::allocator<int64_t>>
|
||||
size_t
|
||||
prune_requests_older_than(
|
||||
std::chrono::time_point<std::chrono::system_clock> time_point,
|
||||
std::vector<int64_t, AllocatorT> * pruned_requests = nullptr)
|
||||
{
|
||||
std::lock_guard guard(pending_requests_mutex_);
|
||||
auto old_size = pending_requests_.size();
|
||||
for (auto it = pending_requests_.begin(), last = pending_requests_.end(); it != last; ) {
|
||||
if (it->second.first < time_point) {
|
||||
if (pruned_requests) {
|
||||
pruned_requests->push_back(it->first);
|
||||
}
|
||||
it = pending_requests_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return old_size - pending_requests_.size();
|
||||
}
|
||||
|
||||
protected:
|
||||
using CallbackTypeValueVariant = std::tuple<CallbackType, SharedFuture, Promise>;
|
||||
using CallbackWithRequestTypeValueVariant = std::tuple<
|
||||
CallbackWithRequestType, SharedRequest, SharedFutureWithRequest, PromiseWithRequest>;
|
||||
|
||||
using CallbackInfoVariant = std::variant<
|
||||
std::promise<SharedResponse>,
|
||||
CallbackTypeValueVariant,
|
||||
CallbackWithRequestTypeValueVariant>;
|
||||
|
||||
int64_t
|
||||
async_send_request_impl(const Request & request, CallbackInfoVariant value)
|
||||
{
|
||||
int64_t sequence_number;
|
||||
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
|
||||
rcl_ret_t ret = rcl_send_request(get_client_handle().get(), &request, &sequence_number);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send request");
|
||||
}
|
||||
pending_requests_.try_emplace(
|
||||
sequence_number,
|
||||
std::make_pair(std::chrono::system_clock::now(), std::move(value)));
|
||||
return sequence_number;
|
||||
}
|
||||
|
||||
std::optional<CallbackInfoVariant>
|
||||
get_and_erase_pending_request(int64_t request_number)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(pending_requests_mutex_);
|
||||
auto it = this->pending_requests_.find(request_number);
|
||||
if (it == this->pending_requests_.end()) {
|
||||
RCUTILS_LOG_DEBUG_NAMED(
|
||||
"rclcpp",
|
||||
"Received invalid sequence number. Ignoring...");
|
||||
return std::nullopt;
|
||||
}
|
||||
auto value = std::move(it->second.second);
|
||||
this->pending_requests_.erase(request_number);
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Client)
|
||||
|
||||
std::map<int64_t, std::tuple<SharedPromise, CallbackType, SharedFuture>> pending_requests_;
|
||||
std::unordered_map<
|
||||
int64_t,
|
||||
std::pair<
|
||||
std::chrono::time_point<std::chrono::system_clock>,
|
||||
CallbackInfoVariant>>
|
||||
pending_requests_;
|
||||
std::mutex pending_requests_mutex_;
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/time.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
@@ -78,6 +79,64 @@ public:
|
||||
Time
|
||||
now();
|
||||
|
||||
/**
|
||||
* Sleep until a specified Time, according to clock type.
|
||||
*
|
||||
* Notes for RCL_ROS_TIME clock type:
|
||||
* - Can sleep forever if ros time is active and received clock never reaches `until`
|
||||
* - If ROS time enabled state changes during the sleep, this method will immediately return
|
||||
* false. There is not a consistent choice of sleeping time when the time source changes,
|
||||
* so this is up to the caller to call again if needed.
|
||||
*
|
||||
* \warning When using gcc < 10 or when using gcc >= 10 and pthreads lacks the function
|
||||
* `pthread_cond_clockwait`, steady clocks may sleep using the system clock.
|
||||
* If so, steady clock sleep times can be affected by system clock time jumps.
|
||||
* Depending on the steady clock's epoch and resolution in comparison to the system clock's,
|
||||
* an overflow when converting steady clock durations to system clock times may cause
|
||||
* undefined behavior.
|
||||
* For more info see these issues:
|
||||
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=41861
|
||||
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58931
|
||||
*
|
||||
* \param until absolute time according to current clock type to sleep until.
|
||||
* \param context the rclcpp context the clock should use to check that ROS is still initialized.
|
||||
* \return true immediately if `until` is in the past
|
||||
* \return true when the time `until` is reached
|
||||
* \return false if time cannot be reached reliably, for example from shutdown or a change
|
||||
* of time source.
|
||||
* \throws std::runtime_error if the context is invalid
|
||||
* \throws std::runtime_error if `until` has a different clock type from this clock
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
sleep_until(
|
||||
Time until,
|
||||
Context::SharedPtr context = contexts::get_global_default_context());
|
||||
|
||||
/**
|
||||
* Sleep for a specified Duration.
|
||||
*
|
||||
* Equivalent to
|
||||
*
|
||||
* ```cpp
|
||||
* clock->sleep_until(clock->now() + rel_time, context)
|
||||
* ```
|
||||
*
|
||||
* The function will return immediately if `rel_time` is zero or negative.
|
||||
*
|
||||
* \param rel_time the length of time to sleep for.
|
||||
* \param context the rclcpp context the clock should use to check that ROS is still initialized.
|
||||
* \return true when the end time is reached
|
||||
* \return false if time cannot be reached reliably, for example from shutdown or a change
|
||||
* of time source.
|
||||
* \throws std::runtime_error if the context is invalid
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
sleep_for(
|
||||
Duration rel_time,
|
||||
Context::SharedPtr context = contexts::get_global_default_context());
|
||||
|
||||
/**
|
||||
* Returns the clock of the type `RCL_ROS_TIME` is active.
|
||||
*
|
||||
@@ -115,9 +174,9 @@ public:
|
||||
*
|
||||
* 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
|
||||
* \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.
|
||||
@@ -136,7 +195,7 @@ private:
|
||||
RCLCPP_PUBLIC
|
||||
static void
|
||||
on_time_jump(
|
||||
const struct rcl_time_jump_t * time_jump,
|
||||
const rcl_time_jump_t * time_jump,
|
||||
bool before_jump,
|
||||
void * user_data);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -47,6 +48,20 @@ public:
|
||||
/// Forward declare WeakContextsWrapper
|
||||
class WeakContextsWrapper;
|
||||
|
||||
class ShutdownCallbackHandle
|
||||
{
|
||||
friend class Context;
|
||||
|
||||
public:
|
||||
using ShutdownCallbackType = std::function<void ()>;
|
||||
|
||||
private:
|
||||
std::weak_ptr<ShutdownCallbackType> callback;
|
||||
};
|
||||
|
||||
using OnShutdownCallbackHandle = ShutdownCallbackHandle;
|
||||
using PreShutdownCallbackHandle = ShutdownCallbackHandle;
|
||||
|
||||
/// Context which encapsulates shared state between nodes and other similar entities.
|
||||
/**
|
||||
* A context also represents the lifecycle between init and shutdown of rclcpp.
|
||||
@@ -63,7 +78,7 @@ public:
|
||||
* Every context which is constructed is added to a global vector of contexts,
|
||||
* which is used by the signal handler to conditionally shutdown each context
|
||||
* on SIGINT.
|
||||
* See the shutdown_on_sigint option in the InitOptions class.
|
||||
* See the shutdown_on_signal option in the InitOptions class.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
Context();
|
||||
@@ -80,8 +95,8 @@ public:
|
||||
*
|
||||
* Note that this function does not setup any signal handlers, so if you want
|
||||
* it to be shutdown by the signal handler, then you need to either install
|
||||
* them manually with rclcpp::install_signal_handers() or use rclcpp::init().
|
||||
* In addition to installing the signal handlers, the shutdown_on_sigint
|
||||
* them manually with rclcpp::install_signal_handlers() or use rclcpp::init().
|
||||
* In addition to installing the signal handlers, the shutdown_on_signal
|
||||
* of the InitOptions needs to be `true` for this context to be shutdown by
|
||||
* the signal handler, otherwise it will be passed over.
|
||||
*
|
||||
@@ -112,7 +127,7 @@ public:
|
||||
void
|
||||
init(
|
||||
int argc,
|
||||
char const * const argv[],
|
||||
char const * const * argv,
|
||||
const rclcpp::InitOptions & init_options = rclcpp::InitOptions());
|
||||
|
||||
/// Return true if the context is valid, otherwise false.
|
||||
@@ -139,13 +154,18 @@ public:
|
||||
rclcpp::InitOptions
|
||||
get_init_options();
|
||||
|
||||
/// Return actual domain id.
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_domain_id() const;
|
||||
|
||||
/// Return the shutdown reason, or empty string if not shutdown.
|
||||
/**
|
||||
* This function is thread-safe.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
shutdown_reason();
|
||||
shutdown_reason() const;
|
||||
|
||||
/// Shutdown the context, making it uninitialized and therefore invalid for derived entities.
|
||||
/**
|
||||
@@ -172,7 +192,7 @@ public:
|
||||
bool
|
||||
shutdown(const std::string & reason);
|
||||
|
||||
using OnShutdownCallback = std::function<void ()>;
|
||||
using OnShutdownCallback = OnShutdownCallbackHandle::ShutdownCallbackType;
|
||||
|
||||
/// Add a on_shutdown callback to be called when shutdown is called for this context.
|
||||
/**
|
||||
@@ -180,7 +200,7 @@ public:
|
||||
* to last step in shutdown().
|
||||
*
|
||||
* When shutdown occurs due to the signal handler, these callbacks are run
|
||||
* asynchronoulsy in the dedicated singal handling thread.
|
||||
* asynchronously in the dedicated singal handling thread.
|
||||
*
|
||||
* Also, shutdown() may be called from the destructor of this function.
|
||||
* Therefore, it is not safe to throw exceptions from these callbacks.
|
||||
@@ -198,23 +218,82 @@ public:
|
||||
OnShutdownCallback
|
||||
on_shutdown(OnShutdownCallback callback);
|
||||
|
||||
/// Return the shutdown callbacks as const.
|
||||
/// Add a on_shutdown callback to be called when shutdown is called for this context.
|
||||
/**
|
||||
* Using the returned reference is not thread-safe with calls that modify
|
||||
* the list of "on shutdown" callbacks, i.e. on_shutdown().
|
||||
* These callbacks will be called in the order they are added as the second
|
||||
* to last step in shutdown().
|
||||
*
|
||||
* When shutdown occurs due to the signal handler, these callbacks are run
|
||||
* asynchronously in the dedicated signal handling thread.
|
||||
*
|
||||
* Also, shutdown() may be called from the destructor of this function.
|
||||
* Therefore, it is not safe to throw exceptions from these callbacks.
|
||||
* Instead, log errors or use some other mechanism to indicate an error has
|
||||
* occurred.
|
||||
*
|
||||
* On shutdown callbacks may be registered before init and after shutdown,
|
||||
* and persist on repeated init's.
|
||||
*
|
||||
* \param[in] callback the on_shutdown callback to be registered
|
||||
* \return the created callback handle
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<OnShutdownCallback> &
|
||||
get_on_shutdown_callbacks() const;
|
||||
virtual
|
||||
OnShutdownCallbackHandle
|
||||
add_on_shutdown_callback(OnShutdownCallback callback);
|
||||
|
||||
/// Remove an registered on_shutdown callbacks.
|
||||
/**
|
||||
* \param[in] callback_handle the on_shutdown callback handle to be removed.
|
||||
* \return true if the callback is found and removed, otherwise false.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
remove_on_shutdown_callback(const OnShutdownCallbackHandle & callback_handle);
|
||||
|
||||
using PreShutdownCallback = PreShutdownCallbackHandle::ShutdownCallbackType;
|
||||
|
||||
/// Add a pre_shutdown callback to be called before shutdown is called for this context.
|
||||
/**
|
||||
* These callbacks will be called in the order they are added.
|
||||
*
|
||||
* When shutdown occurs due to the signal handler, these callbacks are run
|
||||
* asynchronously in the dedicated signal handling thread.
|
||||
*
|
||||
* \param[in] callback the pre_shutdown callback to be registered
|
||||
* \return the created callback handle
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
PreShutdownCallbackHandle
|
||||
add_pre_shutdown_callback(PreShutdownCallback callback);
|
||||
|
||||
/// Remove an registered pre_shutdown callback.
|
||||
/**
|
||||
* \param[in] callback_handle the pre_shutdown callback handle to be removed.
|
||||
* \return true if the callback is found and removed, otherwise false.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
remove_pre_shutdown_callback(const PreShutdownCallbackHandle & callback_handle);
|
||||
|
||||
/// Return the shutdown callbacks.
|
||||
/**
|
||||
* Using the returned reference is not thread-safe with calls that modify
|
||||
* the list of "on shutdown" callbacks, i.e. on_shutdown().
|
||||
* Returned callbacks are a copy of the registered callbacks.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<OnShutdownCallback> &
|
||||
get_on_shutdown_callbacks();
|
||||
std::vector<OnShutdownCallback>
|
||||
get_on_shutdown_callbacks() const;
|
||||
|
||||
/// Return the pre-shutdown callbacks.
|
||||
/**
|
||||
* Returned callbacks are a copy of the registered callbacks.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<PreShutdownCallback>
|
||||
get_pre_shutdown_callbacks() const;
|
||||
|
||||
/// Return the internal rcl context.
|
||||
RCLCPP_PUBLIC
|
||||
@@ -227,7 +306,7 @@ public:
|
||||
*
|
||||
* - this context is shutdown()
|
||||
* - this context is destructed (resulting in shutdown)
|
||||
* - this context has shutdown_on_sigint=true and SIGINT occurs (resulting in shutdown)
|
||||
* - this context has shutdown_on_signal=true and SIGINT/SIGTERM occurs (resulting in shutdown)
|
||||
* - interrupt_all_sleep_for() is called
|
||||
*
|
||||
* \param[in] nanoseconds A std::chrono::duration representing how long to sleep for.
|
||||
@@ -243,67 +322,6 @@ public:
|
||||
void
|
||||
interrupt_all_sleep_for();
|
||||
|
||||
/// Get a handle to the guard condition which is triggered when interrupted.
|
||||
/**
|
||||
* This guard condition is triggered any time interrupt_all_wait_sets() is
|
||||
* called, which may be called by the user, or shutdown().
|
||||
* And in turn, shutdown() may be called by the user, the destructor of this
|
||||
* context, or the signal handler if installed and shutdown_on_sigint is true
|
||||
* for this context.
|
||||
*
|
||||
* The first time that this function is called for a given wait set a new guard
|
||||
* condition will be created and returned; thereafter the same guard condition
|
||||
* will be returned for the same wait set.
|
||||
* This mechanism is designed to ensure that the same guard condition is not
|
||||
* reused across wait sets (e.g., when using multiple executors in the same
|
||||
* process).
|
||||
* This method will throw an exception if initialization of the guard
|
||||
* condition fails.
|
||||
*
|
||||
* The returned guard condition needs to be released with the
|
||||
* release_interrupt_guard_condition() method in order to reclaim resources.
|
||||
*
|
||||
* \param[in] wait_set Pointer to the rcl_wait_set_t that will be using the
|
||||
* resulting guard condition.
|
||||
* \return Pointer to the guard condition.
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rcl_guard_condition_t *
|
||||
get_interrupt_guard_condition(rcl_wait_set_t * wait_set);
|
||||
|
||||
/// Release the previously allocated guard condition which is triggered when interrupted.
|
||||
/**
|
||||
* If you previously called get_interrupt_guard_condition() for a given wait
|
||||
* set to get a interrupt guard condition, then you should call
|
||||
* release_interrupt_guard_condition() when you're done, to free that
|
||||
* condition.
|
||||
* Will throw an exception if get_interrupt_guard_condition() wasn't
|
||||
* previously called for the given wait set.
|
||||
*
|
||||
* After calling this, the pointer returned by get_interrupt_guard_condition()
|
||||
* for the given wait_set is invalid.
|
||||
*
|
||||
* \param[in] wait_set Pointer to the rcl_wait_set_t that was using the
|
||||
* resulting guard condition.
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
* \throws std::runtime_error if a nonexistent wait set is trying to release sigint guard condition.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
release_interrupt_guard_condition(rcl_wait_set_t * wait_set);
|
||||
|
||||
/// Nothrow version of release_interrupt_guard_condition(), logs to RCLCPP_ERROR instead.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
release_interrupt_guard_condition(rcl_wait_set_t * wait_set, const std::nothrow_t &) noexcept;
|
||||
|
||||
/// Interrupt any blocking executors, or wait sets associated with this context.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
interrupt_all_wait_sets();
|
||||
|
||||
/// Return a singleton instance for the SubContext type, constructing one if necessary.
|
||||
template<typename SubContext, typename ... Args>
|
||||
std::shared_ptr<SubContext>
|
||||
@@ -342,7 +360,7 @@ private:
|
||||
|
||||
// This mutex is recursive so that the destructor can ensure atomicity
|
||||
// between is_initialized and shutdown.
|
||||
std::recursive_mutex init_mutex_;
|
||||
mutable std::recursive_mutex init_mutex_;
|
||||
std::shared_ptr<rcl_context_t> rcl_context_;
|
||||
rclcpp::InitOptions init_options_;
|
||||
std::string shutdown_reason_;
|
||||
@@ -355,21 +373,42 @@ private:
|
||||
// attempt to acquire another sub context.
|
||||
std::recursive_mutex sub_contexts_mutex_;
|
||||
|
||||
std::vector<OnShutdownCallback> on_shutdown_callbacks_;
|
||||
std::mutex on_shutdown_callbacks_mutex_;
|
||||
std::unordered_set<std::shared_ptr<OnShutdownCallback>> on_shutdown_callbacks_;
|
||||
mutable std::mutex on_shutdown_callbacks_mutex_;
|
||||
|
||||
std::unordered_set<std::shared_ptr<PreShutdownCallback>> pre_shutdown_callbacks_;
|
||||
mutable std::mutex pre_shutdown_callbacks_mutex_;
|
||||
|
||||
/// Condition variable for timed sleep (see sleep_for).
|
||||
std::condition_variable interrupt_condition_variable_;
|
||||
/// Mutex for protecting the global condition variable.
|
||||
std::mutex interrupt_mutex_;
|
||||
|
||||
/// Mutex to protect sigint_guard_cond_handles_.
|
||||
std::mutex interrupt_guard_cond_handles_mutex_;
|
||||
/// Guard conditions for interrupting of associated wait sets on interrupt_all_wait_sets().
|
||||
std::unordered_map<rcl_wait_set_t *, rcl_guard_condition_t> interrupt_guard_cond_handles_;
|
||||
|
||||
/// Keep shared ownership of global vector of weak contexts
|
||||
std::shared_ptr<WeakContextsWrapper> weak_contexts_;
|
||||
|
||||
enum class ShutdownType
|
||||
{
|
||||
pre_shutdown,
|
||||
on_shutdown
|
||||
};
|
||||
|
||||
using ShutdownCallback = ShutdownCallbackHandle::ShutdownCallbackType;
|
||||
|
||||
RCLCPP_LOCAL
|
||||
ShutdownCallbackHandle
|
||||
add_shutdown_callback(
|
||||
ShutdownType shutdown_type,
|
||||
ShutdownCallback callback);
|
||||
|
||||
RCLCPP_LOCAL
|
||||
bool
|
||||
remove_shutdown_callback(
|
||||
ShutdownType shutdown_type,
|
||||
const ShutdownCallbackHandle & callback_handle);
|
||||
|
||||
std::vector<rclcpp::Context::ShutdownCallback>
|
||||
get_shutdown_callback(ShutdownType shutdown_type) const;
|
||||
};
|
||||
|
||||
/// Return a copy of the list of context shared pointers.
|
||||
|
||||
@@ -36,22 +36,6 @@ RCLCPP_PUBLIC
|
||||
DefaultContext::SharedPtr
|
||||
get_global_default_context();
|
||||
|
||||
namespace default_context
|
||||
{
|
||||
|
||||
using DefaultContext
|
||||
[[deprecated("use rclcpp::contexts::DefaultContext instead")]] = DefaultContext;
|
||||
|
||||
[[deprecated("use rclcpp::contexts::get_global_default_context() instead")]]
|
||||
RCLCPP_PUBLIC
|
||||
inline
|
||||
DefaultContext::SharedPtr
|
||||
get_global_default_context()
|
||||
{
|
||||
return rclcpp::contexts::get_global_default_context();
|
||||
}
|
||||
|
||||
} // namespace default_context
|
||||
} // namespace contexts
|
||||
} // namespace rclcpp
|
||||
|
||||
|
||||
69
rclcpp/include/rclcpp/create_generic_publisher.hpp
Normal file
69
rclcpp/include/rclcpp/create_generic_publisher.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright 2020, Apex.AI 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_GENERIC_PUBLISHER_HPP_
|
||||
#define RCLCPP__CREATE_GENERIC_PUBLISHER_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/generic_publisher.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/publisher_options.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/typesupport_helpers.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Create and return a GenericPublisher.
|
||||
/**
|
||||
* The returned pointer will never be empty, but this function can throw various exceptions, for
|
||||
* instance when the message's package can not be found on the AMENT_PREFIX_PATH.
|
||||
*
|
||||
* \param topics_interface NodeTopicsInterface pointer used in parts of the setup
|
||||
* \param topic_name Topic name
|
||||
* \param topic_type Topic type
|
||||
* \param qos %QoS settings
|
||||
* \param options %Publisher options.
|
||||
* Not all publisher options are currently respected, the only relevant options for this
|
||||
* publisher are `event_callbacks`, `use_default_callbacks`, and `%callback_group`.
|
||||
*/
|
||||
template<typename AllocatorT = std::allocator<void>>
|
||||
std::shared_ptr<GenericPublisher> create_generic_publisher(
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr topics_interface,
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::PublisherOptionsWithAllocator<AllocatorT>()
|
||||
)
|
||||
)
|
||||
{
|
||||
auto ts_lib = rclcpp::get_typesupport_library(topic_type, "rosidl_typesupport_cpp");
|
||||
auto pub = std::make_shared<GenericPublisher>(
|
||||
topics_interface->get_node_base_interface(),
|
||||
std::move(ts_lib),
|
||||
topic_name,
|
||||
topic_type,
|
||||
qos,
|
||||
options);
|
||||
topics_interface->add_publisher(pub, options.callback_group);
|
||||
return pub;
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__CREATE_GENERIC_PUBLISHER_HPP_
|
||||
79
rclcpp/include/rclcpp/create_generic_subscription.hpp
Normal file
79
rclcpp/include/rclcpp/create_generic_subscription.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright 2020, Apex.AI 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_GENERIC_SUBSCRIPTION_HPP_
|
||||
#define RCLCPP__CREATE_GENERIC_SUBSCRIPTION_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rcl/subscription.h"
|
||||
#include "rclcpp/generic_subscription.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/serialized_message.hpp"
|
||||
#include "rclcpp/subscription_options.hpp"
|
||||
#include "rclcpp/typesupport_helpers.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Create and return a GenericSubscription.
|
||||
/**
|
||||
* The returned pointer will never be empty, but this function can throw various exceptions, for
|
||||
* instance when the message's package can not be found on the AMENT_PREFIX_PATH.
|
||||
*
|
||||
* \param topics_interface NodeTopicsInterface pointer used in parts of the setup.
|
||||
* \param topic_name Topic name
|
||||
* \param topic_type Topic type
|
||||
* \param qos %QoS settings
|
||||
* \param callback Callback for new messages of serialized form
|
||||
* \param options %Publisher options.
|
||||
* Not all publisher options are currently respected, the only relevant options for this
|
||||
* publisher are `event_callbacks`, `use_default_callbacks`, and `%callback_group`.
|
||||
*/
|
||||
template<typename AllocatorT = std::allocator<void>>
|
||||
std::shared_ptr<GenericSubscription> create_generic_subscription(
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr topics_interface,
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
|
||||
)
|
||||
)
|
||||
{
|
||||
auto ts_lib = rclcpp::get_typesupport_library(
|
||||
topic_type, "rosidl_typesupport_cpp");
|
||||
|
||||
auto subscription = std::make_shared<GenericSubscription>(
|
||||
topics_interface->get_node_base_interface(),
|
||||
std::move(ts_lib),
|
||||
topic_name,
|
||||
topic_type,
|
||||
qos,
|
||||
callback,
|
||||
options);
|
||||
|
||||
topics_interface->add_subscription(subscription, options.callback_group);
|
||||
|
||||
return subscription;
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__CREATE_GENERIC_SUBSCRIPTION_HPP_
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
@@ -24,15 +25,65 @@
|
||||
#include "rclcpp/publisher_factory.hpp"
|
||||
#include "rclcpp/publisher_options.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/qos_overriding_options.hpp"
|
||||
#include "rclcpp/detail/qos_parameters.hpp"
|
||||
|
||||
#include "rmw/qos_profiles.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
/// Create and return a publisher of the given MessageT type.
|
||||
template<
|
||||
typename MessageT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename PublisherT = rclcpp::Publisher<MessageT, AllocatorT>,
|
||||
typename NodeParametersT,
|
||||
typename NodeTopicsT>
|
||||
std::shared_ptr<PublisherT>
|
||||
create_publisher(
|
||||
NodeParametersT & node_parameters,
|
||||
NodeTopicsT & node_topics,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::PublisherOptionsWithAllocator<AllocatorT>()
|
||||
)
|
||||
)
|
||||
{
|
||||
auto node_topics_interface = rclcpp::node_interfaces::get_node_topics_interface(node_topics);
|
||||
const rclcpp::QoS & actual_qos = options.qos_overriding_options.get_policy_kinds().size() ?
|
||||
rclcpp::detail::declare_qos_parameters(
|
||||
options.qos_overriding_options, node_parameters,
|
||||
node_topics_interface->resolve_topic_name(topic_name),
|
||||
qos, rclcpp::detail::PublisherQosParametersTraits{}) :
|
||||
qos;
|
||||
|
||||
// Create the publisher.
|
||||
auto pub = node_topics_interface->create_publisher(
|
||||
topic_name,
|
||||
rclcpp::create_publisher_factory<MessageT, AllocatorT, PublisherT>(options),
|
||||
actual_qos
|
||||
);
|
||||
|
||||
// Add the publisher to the node topics interface.
|
||||
node_topics_interface->add_publisher(pub, options.callback_group);
|
||||
|
||||
return std::dynamic_pointer_cast<PublisherT>(pub);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
|
||||
/// 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.
|
||||
*
|
||||
* In case `options.qos_overriding_options` is enabling qos parameter overrides,
|
||||
* NodeT must also have a method called get_node_parameters_interface()
|
||||
* which returns a shared_ptr to a NodeParametersInterface.
|
||||
*/
|
||||
template<
|
||||
typename MessageT,
|
||||
@@ -41,7 +92,7 @@ template<
|
||||
typename NodeT>
|
||||
std::shared_ptr<PublisherT>
|
||||
create_publisher(
|
||||
NodeT & node,
|
||||
NodeT && node,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (
|
||||
@@ -49,21 +100,28 @@ create_publisher(
|
||||
)
|
||||
)
|
||||
{
|
||||
// Extract the NodeTopicsInterface from the NodeT.
|
||||
using rclcpp::node_interfaces::get_node_topics_interface;
|
||||
auto node_topics = get_node_topics_interface(node);
|
||||
return detail::create_publisher<MessageT, AllocatorT, PublisherT>(
|
||||
node, node, topic_name, qos, options);
|
||||
}
|
||||
|
||||
// Create the publisher.
|
||||
auto pub = node_topics->create_publisher(
|
||||
topic_name,
|
||||
rclcpp::create_publisher_factory<MessageT, AllocatorT, PublisherT>(options),
|
||||
qos
|
||||
);
|
||||
|
||||
// Add the publisher to the node topics interface.
|
||||
node_topics->add_publisher(pub, options.callback_group);
|
||||
|
||||
return std::dynamic_pointer_cast<PublisherT>(pub);
|
||||
/// Create and return a publisher of the given MessageT type.
|
||||
template<
|
||||
typename MessageT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename PublisherT = rclcpp::Publisher<MessageT, AllocatorT>>
|
||||
std::shared_ptr<PublisherT>
|
||||
create_publisher(
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr & node_parameters,
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr & node_topics,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::PublisherOptionsWithAllocator<AllocatorT>()
|
||||
)
|
||||
)
|
||||
{
|
||||
return detail::create_publisher<MessageT, AllocatorT, PublisherT>(
|
||||
node_parameters, node_topics, topic_name, qos, options);
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
@@ -28,6 +29,7 @@
|
||||
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
|
||||
#include "rclcpp/create_publisher.hpp"
|
||||
#include "rclcpp/create_timer.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/subscription_factory.hpp"
|
||||
@@ -39,27 +41,21 @@
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// 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.
|
||||
*/
|
||||
namespace detail
|
||||
{
|
||||
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 MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
AllocatorT
|
||||
>,
|
||||
typename NodeT>
|
||||
typename AllocatorT,
|
||||
typename SubscriptionT,
|
||||
typename MessageMemoryStrategyT,
|
||||
typename NodeParametersT,
|
||||
typename NodeTopicsT,
|
||||
typename ROSMessageType = typename SubscriptionT::ROSMessageType>
|
||||
typename std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
NodeT && node,
|
||||
NodeParametersT & node_parameters,
|
||||
NodeTopicsT & node_topics,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
CallbackT && callback,
|
||||
@@ -72,37 +68,51 @@ create_subscription(
|
||||
)
|
||||
{
|
||||
using rclcpp::node_interfaces::get_node_topics_interface;
|
||||
auto node_topics = get_node_topics_interface(std::forward<NodeT>(node));
|
||||
auto node_topics_interface = get_node_topics_interface(node_topics);
|
||||
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>>
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>
|
||||
subscription_topic_stats = nullptr;
|
||||
|
||||
if (rclcpp::detail::resolve_enable_topic_statistics(
|
||||
options,
|
||||
*node_topics->get_node_base_interface()))
|
||||
*node_topics_interface->get_node_base_interface()))
|
||||
{
|
||||
std::shared_ptr<Publisher<statistics_msgs::msg::MetricsMessage>> publisher =
|
||||
create_publisher<statistics_msgs::msg::MetricsMessage>(
|
||||
node,
|
||||
if (options.topic_stats_options.publish_period <= std::chrono::milliseconds(0)) {
|
||||
throw std::invalid_argument(
|
||||
"topic_stats_options.publish_period must be greater than 0, specified value of " +
|
||||
std::to_string(options.topic_stats_options.publish_period.count()) +
|
||||
" ms");
|
||||
}
|
||||
|
||||
std::shared_ptr<Publisher<statistics_msgs::msg::MetricsMessage>>
|
||||
publisher = rclcpp::detail::create_publisher<statistics_msgs::msg::MetricsMessage>(
|
||||
node_parameters,
|
||||
node_topics_interface,
|
||||
options.topic_stats_options.publish_topic,
|
||||
qos);
|
||||
|
||||
subscription_topic_stats = std::make_shared<
|
||||
rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>
|
||||
>(node_topics->get_node_base_interface()->get_name(), publisher);
|
||||
rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>
|
||||
>(node_topics_interface->get_node_base_interface()->get_name(), publisher);
|
||||
|
||||
auto sub_call_back = [subscription_topic_stats]() {
|
||||
subscription_topic_stats->publish_message();
|
||||
std::weak_ptr<
|
||||
rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>
|
||||
> weak_subscription_topic_stats(subscription_topic_stats);
|
||||
auto sub_call_back = [weak_subscription_topic_stats]() {
|
||||
auto subscription_topic_stats = weak_subscription_topic_stats.lock();
|
||||
if (subscription_topic_stats) {
|
||||
subscription_topic_stats->publish_message_and_reset_measurements();
|
||||
}
|
||||
};
|
||||
|
||||
auto node_timer_interface = node_topics->get_node_timers_interface();
|
||||
auto node_timer_interface = node_topics_interface->get_node_timers_interface();
|
||||
|
||||
auto timer = create_wall_timer(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
options.topic_stats_options.publish_period),
|
||||
sub_call_back,
|
||||
options.callback_group,
|
||||
node_topics->get_node_base_interface(),
|
||||
node_topics_interface->get_node_base_interface(),
|
||||
node_timer_interface
|
||||
);
|
||||
|
||||
@@ -116,11 +126,102 @@ create_subscription(
|
||||
subscription_topic_stats
|
||||
);
|
||||
|
||||
auto sub = node_topics->create_subscription(topic_name, factory, qos);
|
||||
node_topics->add_subscription(sub, options.callback_group);
|
||||
const rclcpp::QoS & actual_qos = options.qos_overriding_options.get_policy_kinds().size() ?
|
||||
rclcpp::detail::declare_qos_parameters(
|
||||
options.qos_overriding_options, node_parameters,
|
||||
node_topics_interface->resolve_topic_name(topic_name),
|
||||
qos, rclcpp::detail::SubscriptionQosParametersTraits{}) :
|
||||
qos;
|
||||
|
||||
auto sub = node_topics_interface->create_subscription(topic_name, factory, actual_qos);
|
||||
node_topics_interface->add_subscription(sub, options.callback_group);
|
||||
|
||||
return std::dynamic_pointer_cast<SubscriptionT>(sub);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/// 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.
|
||||
*
|
||||
* In case `options.qos_overriding_options` is enabling qos parameter overrides,
|
||||
* NodeT must also have a method called get_node_parameters_interface()
|
||||
* which returns a shared_ptr to a NodeParametersInterface.
|
||||
*
|
||||
* \tparam MessageT
|
||||
* \tparam CallbackT
|
||||
* \tparam AllocatorT
|
||||
* \tparam SubscriptionT
|
||||
* \tparam MessageMemoryStrategyT
|
||||
* \tparam NodeT
|
||||
* \param node
|
||||
* \param topic_name
|
||||
* \param qos
|
||||
* \param callback
|
||||
* \param options
|
||||
* \param msg_mem_strat
|
||||
* \return the created subscription
|
||||
* \throws std::invalid_argument if topic statistics is enabled and the publish period is
|
||||
* less than or equal to zero.
|
||||
*/
|
||||
template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType,
|
||||
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 MessageMemoryStrategyT::SharedPtr msg_mem_strat = (
|
||||
MessageMemoryStrategyT::create_default()
|
||||
)
|
||||
)
|
||||
{
|
||||
return rclcpp::detail::create_subscription<
|
||||
MessageT, CallbackT, AllocatorT, SubscriptionT, MessageMemoryStrategyT>(
|
||||
node, node, topic_name, qos, std::forward<CallbackT>(callback), options, msg_mem_strat);
|
||||
}
|
||||
|
||||
/// Create and return a subscription of the given MessageT type.
|
||||
/**
|
||||
* See \ref create_subscription().
|
||||
*/
|
||||
template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType>
|
||||
typename std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr & node_parameters,
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr & node_topics,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
CallbackT && callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
|
||||
),
|
||||
typename MessageMemoryStrategyT::SharedPtr msg_mem_strat = (
|
||||
MessageMemoryStrategyT::create_default()
|
||||
)
|
||||
)
|
||||
{
|
||||
return rclcpp::detail::create_subscription<
|
||||
MessageT, CallbackT, AllocatorT, SubscriptionT, MessageMemoryStrategyT>(
|
||||
node_parameters, node_topics, topic_name, qos,
|
||||
std::forward<CallbackT>(callback), options, msg_mem_strat);
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
|
||||
@@ -76,14 +76,14 @@ create_timer(
|
||||
* \tparam DurationRepT
|
||||
* \tparam DurationT
|
||||
* \tparam CallbackT
|
||||
* \param period period to exectute callback
|
||||
* \param period period to execute callback. This duration must be 0 <= period < nanoseconds::max()
|
||||
* \param callback callback to execute via the timer period
|
||||
* \param group
|
||||
* \param node_base
|
||||
* \param node_timers
|
||||
* \return
|
||||
* \throws std::invalid argument if either node_base or node_timers
|
||||
* are null
|
||||
* are null, or period is negative or too large
|
||||
*/
|
||||
template<typename DurationRepT, typename DurationT, typename CallbackT>
|
||||
typename rclcpp::WallTimer<CallbackT>::SharedPtr
|
||||
@@ -102,10 +102,38 @@ create_wall_timer(
|
||||
throw std::invalid_argument{"input node_timers cannot be null"};
|
||||
}
|
||||
|
||||
if (period < std::chrono::duration<DurationRepT, DurationT>::zero()) {
|
||||
throw std::invalid_argument{"timer period cannot be negative"};
|
||||
}
|
||||
|
||||
// Casting to a double representation might lose precision and allow the check below to succeed
|
||||
// but the actual cast to nanoseconds fail. Using 1 DurationT worth of nanoseconds less than max.
|
||||
constexpr auto maximum_safe_cast_ns =
|
||||
std::chrono::nanoseconds::max() - std::chrono::duration<DurationRepT, DurationT>(1);
|
||||
|
||||
// If period is greater than nanoseconds::max(), the duration_cast to nanoseconds will overflow
|
||||
// a signed integer, which is undefined behavior. Checking whether any std::chrono::duration is
|
||||
// greater than nanoseconds::max() is a difficult general problem. This is a more conservative
|
||||
// version of Howard Hinnant's (the <chrono> guy>) response here:
|
||||
// https://stackoverflow.com/a/44637334/2089061
|
||||
// However, this doesn't solve the issue for all possible duration types of period.
|
||||
// Follow-up issue: https://github.com/ros2/rclcpp/issues/1177
|
||||
constexpr auto ns_max_as_double =
|
||||
std::chrono::duration_cast<std::chrono::duration<double, std::chrono::nanoseconds::period>>(
|
||||
maximum_safe_cast_ns);
|
||||
if (period > ns_max_as_double) {
|
||||
throw std::invalid_argument{
|
||||
"timer period must be less than std::chrono::nanoseconds::max()"};
|
||||
}
|
||||
|
||||
const auto period_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(period);
|
||||
if (period_ns < std::chrono::nanoseconds::zero()) {
|
||||
throw std::runtime_error{
|
||||
"Casting timer period to nanoseconds resulted in integer overflow."};
|
||||
}
|
||||
|
||||
auto timer = rclcpp::WallTimer<CallbackT>::make_shared(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(period),
|
||||
std::move(callback),
|
||||
node_base->get_context());
|
||||
period_ns, std::move(callback), node_base->get_context());
|
||||
node_timers->add_timer(timer, group);
|
||||
return timer;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2021 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__DETAIL__ADD_GUARD_CONDITION_TO_RCL_WAIT_SET_HPP_
|
||||
#define RCLCPP__DETAIL__ADD_GUARD_CONDITION_TO_RCL_WAIT_SET_HPP_
|
||||
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// Adds the guard condition to a waitset
|
||||
/**
|
||||
* \param[in] wait_set reference to a wait set where to add the guard condition
|
||||
* \param[in] guard_condition reference to the guard_condition to be added
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_guard_condition_to_rcl_wait_set(
|
||||
rcl_wait_set_t & wait_set,
|
||||
const rclcpp::GuardCondition & guard_condition);
|
||||
|
||||
} // namespace detail
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DETAIL__ADD_GUARD_CONDITION_TO_RCL_WAIT_SET_HPP_
|
||||
68
rclcpp/include/rclcpp/detail/cpp_callback_trampoline.hpp
Normal file
68
rclcpp/include/rclcpp/detail/cpp_callback_trampoline.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2021 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__DETAIL__CPP_CALLBACK_TRAMPOLINE_HPP_
|
||||
#define RCLCPP__DETAIL__CPP_CALLBACK_TRAMPOLINE_HPP_
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// Trampoline pattern for wrapping std::function into C-style callbacks.
|
||||
/**
|
||||
* A common pattern in C is for a function to take a function pointer and a
|
||||
* void pointer for "user data" which is passed to the function pointer when it
|
||||
* is called from within C.
|
||||
*
|
||||
* It works by using the user data pointer to store a pointer to a
|
||||
* std::function instance.
|
||||
* So when called from C, this function will cast the user data to the right
|
||||
* std::function type and call it.
|
||||
*
|
||||
* This should allow you to use free functions, lambdas with and without
|
||||
* captures, and various kinds of std::bind instances.
|
||||
*
|
||||
* The interior of this function is likely to be executed within a C runtime,
|
||||
* so no exceptions should be thrown at this point, and doing so results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* \tparam UserDataT Deduced type based on what is passed for user data,
|
||||
* usually this type is either `void *` or `const void *`.
|
||||
* \tparam Args the arguments being passed to the callback
|
||||
* \tparam ReturnT the return type of this function and the callback, default void
|
||||
* \param user_data the function pointer, possibly type erased
|
||||
* \param args the arguments to be forwarded to the callback
|
||||
* \returns whatever the callback returns, if anything
|
||||
*/
|
||||
template<
|
||||
typename UserDataT,
|
||||
typename ... Args,
|
||||
typename ReturnT = void
|
||||
>
|
||||
ReturnT
|
||||
cpp_callback_trampoline(UserDataT user_data, Args ... args) noexcept
|
||||
{
|
||||
auto & actual_callback = *reinterpret_cast<const std::function<ReturnT(Args...)> *>(user_data);
|
||||
return actual_callback(args ...);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DETAIL__CPP_CALLBACK_TRAMPOLINE_HPP_
|
||||
340
rclcpp/include/rclcpp/detail/qos_parameters.hpp
Normal file
340
rclcpp/include/rclcpp/detail/qos_parameters.hpp
Normal file
@@ -0,0 +1,340 @@
|
||||
// Copyright 2020 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__DETAIL__QOS_PARAMETERS_HPP_
|
||||
#define RCLCPP__DETAIL__QOS_PARAMETERS_HPP_
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
|
||||
#include "rcpputils/pointer_traits.hpp"
|
||||
#include "rmw/qos_string_conversions.h"
|
||||
|
||||
#include "rclcpp/duration.hpp"
|
||||
#include "rclcpp/node_interfaces/get_node_parameters_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
|
||||
#include "rclcpp/qos_overriding_options.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// \internal Trait used to specialize `declare_qos_parameters()` for publishers.
|
||||
struct PublisherQosParametersTraits
|
||||
{
|
||||
static constexpr const char * entity_type() {return "publisher";}
|
||||
static constexpr auto allowed_policies()
|
||||
{
|
||||
return std::array<::rclcpp::QosPolicyKind, 9> {
|
||||
QosPolicyKind::AvoidRosNamespaceConventions,
|
||||
QosPolicyKind::Deadline,
|
||||
QosPolicyKind::Durability,
|
||||
QosPolicyKind::History,
|
||||
QosPolicyKind::Depth,
|
||||
QosPolicyKind::Lifespan,
|
||||
QosPolicyKind::Liveliness,
|
||||
QosPolicyKind::LivelinessLeaseDuration,
|
||||
QosPolicyKind::Reliability,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// \internal Trait used to specialize `declare_qos_parameters()` for subscriptions.
|
||||
struct SubscriptionQosParametersTraits
|
||||
{
|
||||
static constexpr const char * entity_type() {return "subscription";}
|
||||
static constexpr auto allowed_policies()
|
||||
{
|
||||
return std::array<::rclcpp::QosPolicyKind, 8> {
|
||||
QosPolicyKind::AvoidRosNamespaceConventions,
|
||||
QosPolicyKind::Deadline,
|
||||
QosPolicyKind::Durability,
|
||||
QosPolicyKind::History,
|
||||
QosPolicyKind::Depth,
|
||||
QosPolicyKind::Liveliness,
|
||||
QosPolicyKind::LivelinessLeaseDuration,
|
||||
QosPolicyKind::Reliability,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// \internal Returns the given `policy` of the profile `qos` converted to a parameter value.
|
||||
inline
|
||||
::rclcpp::ParameterValue
|
||||
get_default_qos_param_value(rclcpp::QosPolicyKind policy, const rclcpp::QoS & qos);
|
||||
|
||||
/// \internal Modify the given `policy` in `qos` to be `value`.
|
||||
inline
|
||||
void
|
||||
apply_qos_override(
|
||||
rclcpp::QosPolicyKind policy, rclcpp::ParameterValue value, rclcpp::QoS & qos);
|
||||
|
||||
inline
|
||||
rclcpp::ParameterValue
|
||||
declare_parameter_or_get(
|
||||
rclcpp::node_interfaces::NodeParametersInterface & parameters_interface,
|
||||
const std::string & param_name,
|
||||
rclcpp::ParameterValue param_value,
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor)
|
||||
{
|
||||
try {
|
||||
return parameters_interface.declare_parameter(
|
||||
param_name, param_value, descriptor);
|
||||
} catch (const rclcpp::exceptions::ParameterAlreadyDeclaredException &) {
|
||||
return parameters_interface.get_parameter(param_name).get_parameter_value();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DOXYGEN_ONLY
|
||||
/// \internal Declare QoS parameters for the given entity.
|
||||
/**
|
||||
* \tparam NodeT Node pointer or reference type.
|
||||
* \tparam EntityQosParametersTraits A class with two static methods: `entity_type()` and
|
||||
* `allowed_policies()`. See `PublisherQosParametersTraits` and `SubscriptionQosParametersTraits`.
|
||||
* \param options User provided options that indicate if QoS parameter overrides should be
|
||||
* declared or not, which policy can have overrides, and optionally a callback to validate the profile.
|
||||
* \param node Parameters will be declared using this node.
|
||||
* \param topic_name Name of the topic of the entity.
|
||||
* \param default_qos User provided qos. It will be used as a default for the parameters declared.
|
||||
* \return qos profile based on the user provided parameter overrides.
|
||||
*/
|
||||
template<typename NodeT, typename EntityQosParametersTraits>
|
||||
rclcpp::QoS
|
||||
declare_qos_parameters(
|
||||
const ::rclcpp::QosOverridingOptions & options,
|
||||
NodeT & node,
|
||||
const std::string & topic_name,
|
||||
const ::rclcpp::QoS & default_qos,
|
||||
EntityQosParametersTraits);
|
||||
|
||||
#else
|
||||
|
||||
template<typename NodeT, typename EntityQosParametersTraits>
|
||||
std::enable_if_t<
|
||||
(rclcpp::node_interfaces::has_node_parameters_interface<
|
||||
decltype(std::declval<typename rcpputils::remove_pointer<NodeT>::type>())>::value ||
|
||||
std::is_same<typename std::decay_t<NodeT>,
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr>::value),
|
||||
rclcpp::QoS>
|
||||
declare_qos_parameters(
|
||||
const ::rclcpp::QosOverridingOptions & options,
|
||||
NodeT & node,
|
||||
const std::string & topic_name,
|
||||
const ::rclcpp::QoS & default_qos,
|
||||
EntityQosParametersTraits)
|
||||
{
|
||||
auto & parameters_interface = *rclcpp::node_interfaces::get_node_parameters_interface(node);
|
||||
std::string param_prefix;
|
||||
const auto & id = options.get_id();
|
||||
{
|
||||
std::ostringstream oss{"qos_overrides.", std::ios::ate};
|
||||
oss << topic_name << "." << EntityQosParametersTraits::entity_type();
|
||||
if (!id.empty()) {
|
||||
oss << "_" << id;
|
||||
}
|
||||
oss << ".";
|
||||
param_prefix = oss.str();
|
||||
}
|
||||
std::string param_description_suffix;
|
||||
{
|
||||
std::ostringstream oss{"} for ", std::ios::ate};
|
||||
oss << EntityQosParametersTraits::entity_type() << " {" << topic_name << "}";
|
||||
if (!id.empty()) {
|
||||
oss << " with id {" << id << "}";
|
||||
}
|
||||
param_description_suffix = oss.str();
|
||||
}
|
||||
rclcpp::QoS qos = default_qos;
|
||||
for (auto policy : EntityQosParametersTraits::allowed_policies()) {
|
||||
if (
|
||||
std::count(options.get_policy_kinds().begin(), options.get_policy_kinds().end(), policy))
|
||||
{
|
||||
std::ostringstream param_name{param_prefix, std::ios::ate};
|
||||
param_name << qos_policy_kind_to_cstr(policy);
|
||||
std::ostringstream param_desciption{"qos policy {", std::ios::ate};
|
||||
param_desciption << qos_policy_kind_to_cstr(policy) << param_description_suffix;
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor{};
|
||||
descriptor.description = param_desciption.str();
|
||||
descriptor.read_only = true;
|
||||
auto value = declare_parameter_or_get(
|
||||
parameters_interface, param_name.str(),
|
||||
get_default_qos_param_value(policy, qos), descriptor);
|
||||
::rclcpp::detail::apply_qos_override(policy, value, qos);
|
||||
}
|
||||
}
|
||||
const auto & validation_callback = options.get_validation_callback();
|
||||
if (validation_callback) {
|
||||
auto result = validation_callback(qos);
|
||||
if (!result.successful) {
|
||||
throw rclcpp::exceptions::InvalidQosOverridesException{
|
||||
"validation callback failed: " + result.reason};
|
||||
}
|
||||
}
|
||||
return qos;
|
||||
}
|
||||
|
||||
// TODO(ivanpauno): This overload cannot declare the QoS parameters, as a node parameters interface
|
||||
// was not provided.
|
||||
template<typename NodeT, typename EntityQosParametersTraits>
|
||||
std::enable_if_t<
|
||||
!(rclcpp::node_interfaces::has_node_parameters_interface<
|
||||
decltype(std::declval<typename rcpputils::remove_pointer<NodeT>::type>())>::value ||
|
||||
std::is_same<typename std::decay_t<NodeT>,
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr>::value),
|
||||
rclcpp::QoS>
|
||||
declare_qos_parameters(
|
||||
const ::rclcpp::QosOverridingOptions & options,
|
||||
NodeT &,
|
||||
const std::string &,
|
||||
const ::rclcpp::QoS & default_qos,
|
||||
EntityQosParametersTraits)
|
||||
{
|
||||
if (options.get_policy_kinds().size()) {
|
||||
std::runtime_error exc{
|
||||
"passed non-default qos overriding options without providing a parameters interface"};
|
||||
throw exc;
|
||||
}
|
||||
return default_qos;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// \internal Helper function to get a rmw qos policy value from a string.
|
||||
#define RCLCPP_DETAIL_APPLY_QOS_OVERRIDE_FROM_PARAMETER_STRING( \
|
||||
kind_lower, kind_upper, parameter_value, rclcpp_qos) \
|
||||
do { \
|
||||
auto policy_string = (parameter_value).get<std::string>(); \
|
||||
auto policy_value = rmw_qos_ ## kind_lower ## _policy_from_str(policy_string.c_str()); \
|
||||
if (RMW_QOS_POLICY_ ## kind_upper ## _UNKNOWN == policy_value) { \
|
||||
throw std::invalid_argument{"unknown QoS policy " #kind_lower " value: " + policy_string}; \
|
||||
} \
|
||||
((rclcpp_qos).kind_lower)(policy_value); \
|
||||
} while (0)
|
||||
|
||||
inline
|
||||
void
|
||||
apply_qos_override(
|
||||
rclcpp::QosPolicyKind policy, rclcpp::ParameterValue value, rclcpp::QoS & qos)
|
||||
{
|
||||
switch (policy) {
|
||||
case QosPolicyKind::AvoidRosNamespaceConventions:
|
||||
qos.avoid_ros_namespace_conventions(value.get<bool>());
|
||||
break;
|
||||
case QosPolicyKind::Deadline:
|
||||
qos.deadline(::rclcpp::Duration::from_nanoseconds(value.get<int64_t>()));
|
||||
break;
|
||||
case QosPolicyKind::Durability:
|
||||
RCLCPP_DETAIL_APPLY_QOS_OVERRIDE_FROM_PARAMETER_STRING(
|
||||
durability, DURABILITY, value, qos);
|
||||
break;
|
||||
case QosPolicyKind::History:
|
||||
RCLCPP_DETAIL_APPLY_QOS_OVERRIDE_FROM_PARAMETER_STRING(
|
||||
history, HISTORY, value, qos);
|
||||
break;
|
||||
case QosPolicyKind::Depth:
|
||||
qos.get_rmw_qos_profile().depth = static_cast<size_t>(value.get<int64_t>());
|
||||
break;
|
||||
case QosPolicyKind::Lifespan:
|
||||
qos.lifespan(::rclcpp::Duration::from_nanoseconds(value.get<int64_t>()));
|
||||
break;
|
||||
case QosPolicyKind::Liveliness:
|
||||
RCLCPP_DETAIL_APPLY_QOS_OVERRIDE_FROM_PARAMETER_STRING(
|
||||
liveliness, LIVELINESS, value, qos);
|
||||
break;
|
||||
case QosPolicyKind::LivelinessLeaseDuration:
|
||||
qos.liveliness_lease_duration(::rclcpp::Duration::from_nanoseconds(value.get<int64_t>()));
|
||||
break;
|
||||
case QosPolicyKind::Reliability:
|
||||
RCLCPP_DETAIL_APPLY_QOS_OVERRIDE_FROM_PARAMETER_STRING(
|
||||
reliability, RELIABILITY, value, qos);
|
||||
break;
|
||||
default:
|
||||
throw std::invalid_argument{"unknown QosPolicyKind"};
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert `rmw_time_t` to `int64_t` that can be used as a parameter value.
|
||||
inline
|
||||
int64_t
|
||||
rmw_duration_to_int64_t(rmw_time_t rmw_duration)
|
||||
{
|
||||
return ::rclcpp::Duration(
|
||||
static_cast<int32_t>(rmw_duration.sec),
|
||||
static_cast<uint32_t>(rmw_duration.nsec)
|
||||
).nanoseconds();
|
||||
}
|
||||
|
||||
/// \internal Throw an exception if `policy_value_stringified` is NULL.
|
||||
inline
|
||||
const char *
|
||||
check_if_stringified_policy_is_null(const char * policy_value_stringified, QosPolicyKind kind)
|
||||
{
|
||||
if (!policy_value_stringified) {
|
||||
std::ostringstream oss{"unknown value for policy kind {", std::ios::ate};
|
||||
oss << kind << "}";
|
||||
throw std::invalid_argument{oss.str()};
|
||||
}
|
||||
return policy_value_stringified;
|
||||
}
|
||||
|
||||
inline
|
||||
::rclcpp::ParameterValue
|
||||
get_default_qos_param_value(rclcpp::QosPolicyKind kind, const rclcpp::QoS & qos)
|
||||
{
|
||||
using ParameterValue = ::rclcpp::ParameterValue;
|
||||
const auto & rmw_qos = qos.get_rmw_qos_profile();
|
||||
switch (kind) {
|
||||
case QosPolicyKind::AvoidRosNamespaceConventions:
|
||||
return ParameterValue(rmw_qos.avoid_ros_namespace_conventions);
|
||||
case QosPolicyKind::Deadline:
|
||||
return ParameterValue(rmw_duration_to_int64_t(rmw_qos.deadline));
|
||||
case QosPolicyKind::Durability:
|
||||
return ParameterValue(
|
||||
check_if_stringified_policy_is_null(
|
||||
rmw_qos_durability_policy_to_str(rmw_qos.durability), kind));
|
||||
case QosPolicyKind::History:
|
||||
return ParameterValue(
|
||||
check_if_stringified_policy_is_null(
|
||||
rmw_qos_history_policy_to_str(rmw_qos.history), kind));
|
||||
case QosPolicyKind::Depth:
|
||||
return ParameterValue(static_cast<int64_t>(rmw_qos.depth));
|
||||
case QosPolicyKind::Lifespan:
|
||||
return ParameterValue(rmw_duration_to_int64_t(rmw_qos.lifespan));
|
||||
case QosPolicyKind::Liveliness:
|
||||
return ParameterValue(
|
||||
check_if_stringified_policy_is_null(
|
||||
rmw_qos_liveliness_policy_to_str(rmw_qos.liveliness), kind));
|
||||
case QosPolicyKind::LivelinessLeaseDuration:
|
||||
return ParameterValue(rmw_duration_to_int64_t(rmw_qos.liveliness_lease_duration));
|
||||
case QosPolicyKind::Reliability:
|
||||
return ParameterValue(
|
||||
check_if_stringified_policy_is_null(
|
||||
rmw_qos_reliability_policy_to_str(rmw_qos.reliability), kind));
|
||||
default:
|
||||
throw std::invalid_argument{"unknown QoS policy kind"};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DETAIL__QOS_PARAMETERS_HPP_
|
||||
@@ -0,0 +1,166 @@
|
||||
// Copyright 2021 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__DETAIL__SUBSCRIPTION_CALLBACK_TYPE_HELPER_HPP_
|
||||
#define RCLCPP__DETAIL__SUBSCRIPTION_CALLBACK_TYPE_HELPER_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rclcpp/message_info.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// Template metaprogramming helper used to resolve the callback argument into a std::function.
|
||||
/**
|
||||
* Sometimes the CallbackT is a std::function already, but it could also be a
|
||||
* function pointer, lambda, bind, or some variant of those.
|
||||
* In some cases, like a lambda where the arguments can be converted between one
|
||||
* another, e.g. std::function<void (shared_ptr<...>)> and
|
||||
* std::function<void (unique_ptr<...>)>, you need to make that not ambiguous
|
||||
* by checking the arguments independently using function traits rather than
|
||||
* rely on overloading the two std::function types.
|
||||
*
|
||||
* This issue, with the lambda's, can be demonstrated with this minimal program:
|
||||
*
|
||||
* \code{.cpp}
|
||||
* #include <functional>
|
||||
* #include <memory>
|
||||
*
|
||||
* void f(std::function<void (std::shared_ptr<int>)>) {}
|
||||
* void f(std::function<void (std::unique_ptr<int>)>) {}
|
||||
*
|
||||
* int main() {
|
||||
* // Fails to compile with an "ambiguous call" error.
|
||||
* f([](std::shared_ptr<int>){});
|
||||
*
|
||||
* // Works.
|
||||
* std::function<void (std::shared_ptr<int>)> cb = [](std::shared_ptr<int>){};
|
||||
* f(cb);
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* If this program ever starts working in a future version of C++, this class
|
||||
* may become redundant.
|
||||
*
|
||||
* This helper works by using SFINAE with rclcpp::function_traits::same_arguments<>
|
||||
* to narrow down the exact std::function<> type for the given CallbackT.
|
||||
*/
|
||||
template<typename MessageT, typename CallbackT, typename Enable = void>
|
||||
struct SubscriptionCallbackTypeHelper
|
||||
{
|
||||
using callback_type = typename rclcpp::function_traits::as_std_function<CallbackT>::type;
|
||||
};
|
||||
|
||||
template<typename MessageT, typename CallbackT>
|
||||
struct SubscriptionCallbackTypeHelper<
|
||||
MessageT,
|
||||
CallbackT,
|
||||
typename std::enable_if_t<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
std::function<void(std::shared_ptr<const MessageT>)>
|
||||
>::value
|
||||
>
|
||||
>
|
||||
{
|
||||
using callback_type = std::function<void (std::shared_ptr<const MessageT>)>;
|
||||
};
|
||||
|
||||
template<typename MessageT, typename CallbackT>
|
||||
struct SubscriptionCallbackTypeHelper<
|
||||
MessageT,
|
||||
CallbackT,
|
||||
typename std::enable_if_t<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
std::function<void(std::shared_ptr<const MessageT>, const rclcpp::MessageInfo &)>
|
||||
>::value
|
||||
>
|
||||
>
|
||||
{
|
||||
using callback_type =
|
||||
std::function<void (std::shared_ptr<const MessageT>, const rclcpp::MessageInfo &)>;
|
||||
};
|
||||
|
||||
template<typename MessageT, typename CallbackT>
|
||||
struct SubscriptionCallbackTypeHelper<
|
||||
MessageT,
|
||||
CallbackT,
|
||||
typename std::enable_if_t<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
std::function<void(const std::shared_ptr<const MessageT> &)>
|
||||
>::value
|
||||
>
|
||||
>
|
||||
{
|
||||
using callback_type = std::function<void (const std::shared_ptr<const MessageT> &)>;
|
||||
};
|
||||
|
||||
template<typename MessageT, typename CallbackT>
|
||||
struct SubscriptionCallbackTypeHelper<
|
||||
MessageT,
|
||||
CallbackT,
|
||||
typename std::enable_if_t<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
std::function<void(const std::shared_ptr<const MessageT> &, const rclcpp::MessageInfo &)>
|
||||
>::value
|
||||
>
|
||||
>
|
||||
{
|
||||
using callback_type =
|
||||
std::function<void (const std::shared_ptr<const MessageT> &, const rclcpp::MessageInfo &)>;
|
||||
};
|
||||
|
||||
template<typename MessageT, typename CallbackT>
|
||||
struct SubscriptionCallbackTypeHelper<
|
||||
MessageT,
|
||||
CallbackT,
|
||||
typename std::enable_if_t<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
std::function<void(std::shared_ptr<MessageT>)>
|
||||
>::value
|
||||
>
|
||||
>
|
||||
{
|
||||
using callback_type = std::function<void (std::shared_ptr<MessageT>)>;
|
||||
};
|
||||
|
||||
template<typename MessageT, typename CallbackT>
|
||||
struct SubscriptionCallbackTypeHelper<
|
||||
MessageT,
|
||||
CallbackT,
|
||||
typename std::enable_if_t<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
std::function<void(std::shared_ptr<MessageT>, const rclcpp::MessageInfo &)>
|
||||
>::value
|
||||
>
|
||||
>
|
||||
{
|
||||
using callback_type =
|
||||
std::function<void (std::shared_ptr<MessageT>, const rclcpp::MessageInfo &)>;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DETAIL__SUBSCRIPTION_CALLBACK_TYPE_HELPER_HPP_
|
||||
@@ -30,7 +30,7 @@ namespace detail
|
||||
|
||||
std::vector<std::string>
|
||||
get_unparsed_ros_arguments(
|
||||
int argc, char const * const argv[],
|
||||
int argc, char const * const * argv,
|
||||
rcl_arguments_t * arguments,
|
||||
rcl_allocator_t allocator);
|
||||
|
||||
|
||||
@@ -38,10 +38,7 @@ public:
|
||||
*/
|
||||
Duration(int32_t seconds, uint32_t nanoseconds);
|
||||
|
||||
// This constructor matches any numeric value - ints or floats.
|
||||
explicit Duration(rcl_duration_value_t nanoseconds);
|
||||
|
||||
// This constructor matches std::chrono::nanoseconds.
|
||||
/// Construct duration from the specified std::chrono::nanoseconds.
|
||||
explicit Duration(std::chrono::nanoseconds nanoseconds);
|
||||
|
||||
// This constructor matches any std::chrono value other than nanoseconds
|
||||
@@ -72,11 +69,14 @@ public:
|
||||
operator=(const Duration & rhs);
|
||||
|
||||
Duration &
|
||||
operator=(const builtin_interfaces::msg::Duration & Duration_msg);
|
||||
operator=(const builtin_interfaces::msg::Duration & duration_msg);
|
||||
|
||||
bool
|
||||
operator==(const rclcpp::Duration & rhs) const;
|
||||
|
||||
bool
|
||||
operator!=(const rclcpp::Duration & rhs) const;
|
||||
|
||||
bool
|
||||
operator<(const rclcpp::Duration & rhs) const;
|
||||
|
||||
@@ -126,6 +126,13 @@ public:
|
||||
static Duration
|
||||
from_seconds(double seconds);
|
||||
|
||||
/// Create a duration object from an integer number representing nanoseconds
|
||||
static Duration
|
||||
from_nanoseconds(rcl_duration_value_t nanoseconds);
|
||||
|
||||
static Duration
|
||||
from_rmw_time(rmw_time_t duration);
|
||||
|
||||
/// Convert Duration into a std::chrono::Duration.
|
||||
template<class DurationT>
|
||||
DurationT
|
||||
@@ -140,6 +147,8 @@ public:
|
||||
|
||||
private:
|
||||
rcl_duration_t rcl_duration_;
|
||||
|
||||
Duration() = default;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -100,6 +100,17 @@ public:
|
||||
{}
|
||||
};
|
||||
|
||||
class UnimplementedError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
UnimplementedError()
|
||||
: std::runtime_error("This code is unimplemented.") {}
|
||||
explicit UnimplementedError(const std::string & msg)
|
||||
: std::runtime_error(msg) {}
|
||||
};
|
||||
|
||||
typedef void (* reset_error_function_t)();
|
||||
|
||||
/// Throw a C++ std::exception which was created based on an rcl error.
|
||||
/**
|
||||
* Passing nullptr for reset_error is safe and will avoid calling any function
|
||||
@@ -120,7 +131,7 @@ 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);
|
||||
reset_error_function_t reset_error = rcl_reset_error);
|
||||
/* *INDENT-ON* */
|
||||
|
||||
class RCLErrorBase
|
||||
@@ -245,6 +256,23 @@ public:
|
||||
{}
|
||||
};
|
||||
|
||||
/// Thrown if user attempts to create an uninitialized statically typed parameter
|
||||
/**
|
||||
* (see https://github.com/ros2/rclcpp/issues/1691)
|
||||
*/
|
||||
class UninitializedStaticallyTypedParameterException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
/// Construct an instance.
|
||||
/**
|
||||
* \param[in] name the name of the parameter.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
explicit UninitializedStaticallyTypedParameterException(const std::string & name)
|
||||
: std::runtime_error("Statically typed parameter '" + name + "' must be initialized.")
|
||||
{}
|
||||
};
|
||||
|
||||
/// Thrown if parameter is already declared.
|
||||
class ParameterAlreadyDeclaredException : public std::runtime_error
|
||||
{
|
||||
@@ -273,6 +301,33 @@ class ParameterModifiedInCallbackException : public std::runtime_error
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
/// Thrown when an uninitialized parameter is accessed.
|
||||
class ParameterUninitializedException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
/// Construct an instance.
|
||||
/**
|
||||
* \param[in] name the name of the parameter.
|
||||
*/
|
||||
explicit ParameterUninitializedException(const std::string & name)
|
||||
: std::runtime_error("parameter '" + name + "' is not initialized")
|
||||
{}
|
||||
};
|
||||
|
||||
/// Thrown if the QoS overrides provided aren't valid.
|
||||
class InvalidQosOverridesException : public std::runtime_error
|
||||
{
|
||||
// Inherit constructors from runtime_error.
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
/// Thrown if a QoS compatibility check fails.
|
||||
class QoSCheckCompatibleException : public std::runtime_error
|
||||
{
|
||||
// Inherit constructors from runtime_error.
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
} // namespace exceptions
|
||||
} // namespace rclcpp
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
@@ -28,8 +29,11 @@
|
||||
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/wait.h"
|
||||
#include "rcpputils/scope_exit.hpp"
|
||||
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/executor_options.hpp"
|
||||
#include "rclcpp/future_return_code.hpp"
|
||||
#include "rclcpp/memory_strategies.hpp"
|
||||
@@ -41,6 +45,10 @@
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
typedef std::map<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
|
||||
std::owner_less<rclcpp::CallbackGroup::WeakPtr>> WeakCallbackGroupsToNodesMap;
|
||||
|
||||
// Forward declaration is used in convenience method signature.
|
||||
class Node;
|
||||
|
||||
@@ -75,13 +83,118 @@ public:
|
||||
virtual void
|
||||
spin() = 0;
|
||||
|
||||
/// Add a callback group to an executor.
|
||||
/**
|
||||
* An executor can have zero or more callback groups which provide work during `spin` functions.
|
||||
* When an executor attempts to add a callback group, the executor checks to see if it is already
|
||||
* associated with another executor, and if it has been, then an exception is thrown.
|
||||
* Otherwise, the callback group is added to the executor.
|
||||
*
|
||||
* Adding a callback group with this method does not associate its node with this executor
|
||||
* in any way
|
||||
*
|
||||
* \param[in] group_ptr a shared ptr that points to a callback group
|
||||
* \param[in] node_ptr a shared pointer that points to a node base interface
|
||||
* \param[in] notify True to trigger the interrupt guard condition during this function. If
|
||||
* the executor is blocked at the rmw layer while waiting for work and it is notified that a new
|
||||
* callback group was added, it will wake up.
|
||||
* \throw std::runtime_error if the callback group is associated to an executor
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
add_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
bool notify = true);
|
||||
|
||||
/// Get callback groups that belong to executor.
|
||||
/**
|
||||
* This function returns a vector of weak pointers that point to callback groups that were
|
||||
* associated with the executor.
|
||||
* The callback groups associated with this executor may have been added with
|
||||
* `add_callback_group`, or added when a node was added to the executor with `add_node`, or
|
||||
* automatically added when it created by a node already associated with this executor and the
|
||||
* automatically_add_to_executor_with_node parameter was true.
|
||||
*
|
||||
* \return a vector of weak pointers that point to callback groups that are associated with
|
||||
* the executor
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_all_callback_groups();
|
||||
|
||||
/// Get callback groups that belong to executor.
|
||||
/**
|
||||
* This function returns a vector of weak pointers that point to callback groups that were
|
||||
* associated with the executor.
|
||||
* The callback groups associated with this executor have been added with
|
||||
* `add_callback_group`.
|
||||
*
|
||||
* \return a vector of weak pointers that point to callback groups that are associated with
|
||||
* the executor
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_manually_added_callback_groups();
|
||||
|
||||
/// Get callback groups that belong to executor.
|
||||
/**
|
||||
* This function returns a vector of weak pointers that point to callback groups that were
|
||||
* added from a node that is associated with the executor.
|
||||
* The callback groups are added when a node is added to the executor with `add_node`, or
|
||||
* automatically if they are created in the future by that node and have the
|
||||
* automatically_add_to_executor_with_node argument set to true.
|
||||
*
|
||||
* \return a vector of weak pointers that point to callback groups from a node associated with
|
||||
* the executor
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_automatically_added_callback_groups_from_nodes();
|
||||
|
||||
/// Remove a callback group from the executor.
|
||||
/**
|
||||
* The callback group is removed from and disassociated with the executor.
|
||||
* If the callback group removed was the last callback group from the node
|
||||
* that is associated with the executor, the interrupt guard condition
|
||||
* is triggered and node's guard condition is removed from the executor.
|
||||
*
|
||||
* This function only removes a callback group that was manually added with
|
||||
* rclcpp::Executor::add_callback_group.
|
||||
* To remove callback groups that were added from a node using
|
||||
* rclcpp::Executor::add_node, use rclcpp::Executor::remove_node instead.
|
||||
*
|
||||
* \param[in] group_ptr Shared pointer to the callback group to be added.
|
||||
* \param[in] notify True to trigger the interrupt guard condition during this function. If
|
||||
* the executor is blocked at the rmw layer while waiting for work and it is notified that a
|
||||
* callback group was removed, it will wake up.
|
||||
* \throw std::runtime_error if node is deleted before callback group
|
||||
* \throw std::runtime_error if the callback group is not associated with the executor
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
remove_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
bool notify = true);
|
||||
|
||||
/// Add a node to the executor.
|
||||
/**
|
||||
* An executor can have zero or more nodes which provide work during `spin` functions.
|
||||
* Nodes have associated callback groups, and this method adds any of those callback groups
|
||||
* to this executor which have their automatically_add_to_executor_with_node parameter true.
|
||||
* The node is also associated with the executor so that future callback groups which are
|
||||
* created on the node with the automatically_add_to_executor_with_node parameter set to true
|
||||
* are also automatically associated with this executor.
|
||||
*
|
||||
* Callback groups with the automatically_add_to_executor_with_node parameter set to false must
|
||||
* be manually added to an executor using the rclcpp::Executor::add_callback_group method.
|
||||
*
|
||||
* If a node is already associated with an executor, this method throws an exception.
|
||||
*
|
||||
* \param[in] node_ptr Shared pointer to the node to be added.
|
||||
* \param[in] notify True to trigger the interrupt guard condition during this function. If
|
||||
* the executor is blocked at the rmw layer while waiting for work and it is notified that a new
|
||||
* node was added, it will wake up.
|
||||
* \throw std::runtime_error if a node is already associated to an executor
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
@@ -97,10 +210,19 @@ public:
|
||||
|
||||
/// Remove a node from the executor.
|
||||
/**
|
||||
* Any callback groups automatically added when this node was added with
|
||||
* rclcpp::Executor::add_node are automatically removed, and the node is no longer associated
|
||||
* with this executor.
|
||||
*
|
||||
* This also means that future callback groups created by the given node are no longer
|
||||
* automatically added to this executor.
|
||||
*
|
||||
* \param[in] node_ptr Shared pointer to the node to remove.
|
||||
* \param[in] notify True to trigger the interrupt guard condition and wake up the executor.
|
||||
* This is useful if the last node was removed from the executor while the executor was blocked
|
||||
* waiting for work in another thread, because otherwise the executor would never be notified.
|
||||
* \throw std::runtime_error if the node is not associated with an executor.
|
||||
* \throw std::runtime_error if the node is not associated with this executor.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
@@ -159,7 +281,7 @@ public:
|
||||
void
|
||||
spin_node_some(std::shared_ptr<rclcpp::Node> node);
|
||||
|
||||
/// Complete all available queued work without blocking.
|
||||
/// Collect work once and execute all available work, optionally within a duration.
|
||||
/**
|
||||
* This function can be overridden. The default implementation is suitable for a
|
||||
* single-threaded model of execution.
|
||||
@@ -174,6 +296,25 @@ public:
|
||||
virtual void
|
||||
spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0));
|
||||
|
||||
/// Collect and execute work repeatedly within a duration or until no more work is available.
|
||||
/**
|
||||
* This function can be overridden. The default implementation is suitable for a
|
||||
* single-threaded model of execution.
|
||||
* Adding subscriptions, timers, services, etc. with blocking callbacks will cause this function
|
||||
* to block (which may have unintended consequences).
|
||||
* If the time that waitables take to be executed is longer than the period on which new waitables
|
||||
* become ready, this method will execute work repeatedly until `max_duration` has elapsed.
|
||||
*
|
||||
* \param[in] max_duration The maximum amount of time to spend executing work, must be >= 0.
|
||||
* `0` is potentially block forever until no more work is available.
|
||||
* \throw std::invalid_argument if max_duration is less than 0.
|
||||
* Note that spin_all() may take longer than this time as it only returns once max_duration has
|
||||
* been exceeded.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
spin_all(std::chrono::nanoseconds max_duration);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
spin_once(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
|
||||
@@ -188,10 +329,10 @@ public:
|
||||
* code.
|
||||
* \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
|
||||
*/
|
||||
template<typename ResponseT, typename TimeRepT = int64_t, typename TimeT = std::milli>
|
||||
template<typename FutureT, typename TimeRepT = int64_t, typename TimeT = std::milli>
|
||||
FutureReturnCode
|
||||
spin_until_future_complete(
|
||||
const std::shared_future<ResponseT> & future,
|
||||
const FutureT & future,
|
||||
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
|
||||
{
|
||||
// TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
|
||||
@@ -212,9 +353,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");
|
||||
}
|
||||
RCPPUTILS_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) {
|
||||
@@ -257,6 +403,15 @@ public:
|
||||
void
|
||||
set_memory_strategy(memory_strategy::MemoryStrategy::SharedPtr memory_strategy);
|
||||
|
||||
/// Returns true if the executor is currently spinning.
|
||||
/**
|
||||
* This function can be called asynchronously from any thread.
|
||||
* \return True if the executor is currently spinning.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
is_spinning();
|
||||
|
||||
protected:
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
@@ -264,6 +419,10 @@ protected:
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
|
||||
std::chrono::nanoseconds timeout);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive);
|
||||
|
||||
/// Find the next available executable and do the work associated with it.
|
||||
/**
|
||||
* \param[in] any_exec Union structure that can hold any executable type (timer, subscription,
|
||||
@@ -300,52 +459,136 @@ protected:
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
|
||||
get_node_by_group(rclcpp::CallbackGroup::SharedPtr group);
|
||||
get_node_by_group(
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
|
||||
rclcpp::CallbackGroup::SharedPtr group);
|
||||
|
||||
/// Return true if the node has been added to this executor.
|
||||
/**
|
||||
* \param[in] node_ptr a shared pointer that points to a node base interface
|
||||
* \param[in] weak_groups_to_nodes map to nodes to lookup
|
||||
* \return true if the node is associated with the executor, otherwise false
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
has_node(
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::CallbackGroup::SharedPtr
|
||||
get_group_by_timer(rclcpp::TimerBase::SharedPtr timer);
|
||||
|
||||
/// Add a callback group to an executor
|
||||
/**
|
||||
* \see rclcpp::Executor::add_callback_group
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
add_callback_group_to_map(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
|
||||
bool notify = true) RCPPUTILS_TSA_REQUIRES(mutex_);
|
||||
|
||||
/// Remove a callback group from the executor.
|
||||
/**
|
||||
* \see rclcpp::Executor::remove_callback_group
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
remove_callback_group_from_map(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
|
||||
bool notify = true) RCPPUTILS_TSA_REQUIRES(mutex_);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
get_next_ready_executable(AnyExecutable & any_executable);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
get_next_ready_executable_from_map(
|
||||
AnyExecutable & any_executable,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
get_next_executable(
|
||||
AnyExecutable & any_executable,
|
||||
std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
|
||||
|
||||
/// Add all callback groups that can be automatically added from associated nodes.
|
||||
/**
|
||||
* The executor, before collecting entities, verifies if any callback group from
|
||||
* nodes associated with the executor, which is not already associated to an executor,
|
||||
* can be automatically added to this executor.
|
||||
* This takes care of any callback group that has been added to a node but not explicitly added
|
||||
* to the executor.
|
||||
* It is important to note that in order for the callback groups to be automatically added to an
|
||||
* executor through this function, the node of the callback groups needs to have been added
|
||||
* through the `add_node` method.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
add_callback_groups_from_nodes_associated_to_executor() RCPPUTILS_TSA_REQUIRES(mutex_);
|
||||
|
||||
/// Spinning state, used to prevent multi threaded calls to spin and to cancel blocking spins.
|
||||
std::atomic_bool spinning;
|
||||
|
||||
/// Guard condition for signaling the rmw layer to wake up for special events.
|
||||
rcl_guard_condition_t interrupt_guard_condition_ = rcl_get_zero_initialized_guard_condition();
|
||||
rclcpp::GuardCondition interrupt_guard_condition_;
|
||||
|
||||
std::shared_ptr<rclcpp::GuardCondition> shutdown_guard_condition_;
|
||||
|
||||
/// 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_;
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
/// The memory strategy: an interface for handling user-defined memory allocation strategies.
|
||||
memory_strategy::MemoryStrategy::SharedPtr memory_strategy_;
|
||||
memory_strategy::MemoryStrategy::SharedPtr
|
||||
memory_strategy_ RCPPUTILS_TSA_PT_GUARDED_BY(mutex_);
|
||||
|
||||
/// The context associated with this executor.
|
||||
std::shared_ptr<rclcpp::Context> context_;
|
||||
|
||||
RCLCPP_DISABLE_COPY(Executor)
|
||||
|
||||
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> weak_nodes_;
|
||||
std::list<const rcl_guard_condition_t *> guard_conditions_;
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
spin_once_impl(std::chrono::nanoseconds timeout);
|
||||
|
||||
typedef std::map<rclcpp::CallbackGroup::WeakPtr,
|
||||
const rclcpp::GuardCondition *,
|
||||
std::owner_less<rclcpp::CallbackGroup::WeakPtr>>
|
||||
WeakCallbackGroupsToGuardConditionsMap;
|
||||
|
||||
/// maps callback groups to guard conditions
|
||||
WeakCallbackGroupsToGuardConditionsMap
|
||||
weak_groups_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
||||
|
||||
/// maps callback groups associated to nodes
|
||||
WeakCallbackGroupsToNodesMap
|
||||
weak_groups_associated_with_executor_to_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
||||
|
||||
/// maps callback groups to nodes associated with executor
|
||||
WeakCallbackGroupsToNodesMap
|
||||
weak_groups_to_nodes_associated_with_executor_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
||||
|
||||
/// maps all callback groups to nodes
|
||||
WeakCallbackGroupsToNodesMap
|
||||
weak_groups_to_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
||||
|
||||
/// nodes that are associated with the executor
|
||||
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>
|
||||
weak_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
||||
|
||||
/// shutdown callback handle registered to Context
|
||||
rclcpp::OnShutdownCallbackHandle shutdown_callback_handle_;
|
||||
};
|
||||
|
||||
namespace executor
|
||||
{
|
||||
|
||||
using Executor [[deprecated("use rclcpp::Executor instead")]] = rclcpp::Executor;
|
||||
|
||||
} // namespace executor
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXECUTOR_HPP_
|
||||
|
||||
@@ -38,20 +38,6 @@ struct ExecutorOptions
|
||||
size_t max_conditions;
|
||||
};
|
||||
|
||||
namespace executor
|
||||
{
|
||||
|
||||
using ExecutorArgs [[deprecated("use rclcpp::ExecutorOptions instead")]] = ExecutorOptions;
|
||||
|
||||
[[deprecated("use rclcpp::ExecutorOptions() instead")]]
|
||||
inline
|
||||
rclcpp::ExecutorOptions
|
||||
create_default_executor_arguments()
|
||||
{
|
||||
return rclcpp::ExecutorOptions();
|
||||
}
|
||||
|
||||
} // namespace executor
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXECUTOR_OPTIONS_HPP_
|
||||
|
||||
@@ -66,12 +66,12 @@ using rclcpp::executors::SingleThreadedExecutor;
|
||||
* If the time spent inside the blocking loop exceeds this timeout, return a `TIMEOUT` return code.
|
||||
* \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
|
||||
*/
|
||||
template<typename ResponseT, typename TimeRepT = int64_t, typename TimeT = std::milli>
|
||||
template<typename FutureT, typename TimeRepT = int64_t, typename TimeT = std::milli>
|
||||
rclcpp::FutureReturnCode
|
||||
spin_node_until_future_complete(
|
||||
rclcpp::Executor & executor,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
const std::shared_future<ResponseT> & future,
|
||||
const FutureT & future,
|
||||
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
|
||||
{
|
||||
// TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
|
||||
@@ -82,13 +82,13 @@ spin_node_until_future_complete(
|
||||
return retcode;
|
||||
}
|
||||
|
||||
template<typename NodeT = rclcpp::Node, typename ResponseT, typename TimeRepT = int64_t,
|
||||
template<typename NodeT = rclcpp::Node, typename FutureT, typename TimeRepT = int64_t,
|
||||
typename TimeT = std::milli>
|
||||
rclcpp::FutureReturnCode
|
||||
spin_node_until_future_complete(
|
||||
rclcpp::Executor & executor,
|
||||
std::shared_ptr<NodeT> node_ptr,
|
||||
const std::shared_future<ResponseT> & future,
|
||||
const FutureT & future,
|
||||
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
|
||||
{
|
||||
return rclcpp::executors::spin_node_until_future_complete(
|
||||
@@ -104,7 +104,7 @@ template<typename FutureT, typename TimeRepT = int64_t, typename TimeT = std::mi
|
||||
rclcpp::FutureReturnCode
|
||||
spin_until_future_complete(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
const std::shared_future<FutureT> & future,
|
||||
const FutureT & future,
|
||||
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
@@ -116,7 +116,7 @@ template<typename NodeT = rclcpp::Node, typename FutureT, typename TimeRepT = in
|
||||
rclcpp::FutureReturnCode
|
||||
spin_until_future_complete(
|
||||
std::shared_ptr<NodeT> node_ptr,
|
||||
const std::shared_future<FutureT> & future,
|
||||
const FutureT & future,
|
||||
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
|
||||
{
|
||||
return rclcpp::spin_until_future_complete(node_ptr->get_node_base_interface(), future, timeout);
|
||||
|
||||
@@ -49,9 +49,10 @@ 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(
|
||||
explicit MultiThreadedExecutor(
|
||||
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions(),
|
||||
size_t number_of_threads = 0,
|
||||
bool yield_before_execute = false,
|
||||
@@ -84,8 +85,6 @@ private:
|
||||
size_t number_of_threads_;
|
||||
bool yield_before_execute_;
|
||||
std::chrono::nanoseconds next_exec_timeout_;
|
||||
|
||||
std::set<TimerBase::SharedPtr> scheduled_timers_;
|
||||
};
|
||||
|
||||
} // namespace executors
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/wait.h"
|
||||
@@ -32,6 +34,9 @@ namespace rclcpp
|
||||
{
|
||||
namespace executors
|
||||
{
|
||||
typedef std::map<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
|
||||
std::owner_less<rclcpp::CallbackGroup::WeakPtr>> WeakCallbackGroupsToNodesMap;
|
||||
|
||||
class StaticExecutorEntitiesCollector final
|
||||
: public rclcpp::Waitable,
|
||||
@@ -45,47 +50,49 @@ public:
|
||||
StaticExecutorEntitiesCollector() = default;
|
||||
|
||||
// Destructor
|
||||
RCLCPP_PUBLIC
|
||||
~StaticExecutorEntitiesCollector();
|
||||
|
||||
/// Initialize StaticExecutorEntitiesCollector
|
||||
/**
|
||||
* \param p_wait_set A reference to the wait set to be used in the executor
|
||||
* \param memory_strategy Shared pointer to the memory strategy to set.
|
||||
* \param executor_guard_condition executor's guard condition
|
||||
* \throws std::runtime_error if memory strategy is null
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init(
|
||||
rcl_wait_set_t * p_wait_set,
|
||||
rclcpp::memory_strategy::MemoryStrategy::SharedPtr & memory_strategy,
|
||||
rcl_guard_condition_t * executor_guard_condition);
|
||||
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy);
|
||||
|
||||
/// Finalize StaticExecutorEntitiesCollector to clear resources
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
is_init() {return initialized_;}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
execute() override;
|
||||
fini();
|
||||
|
||||
/// Execute the waitable.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
fill_memory_strategy();
|
||||
execute(std::shared_ptr<void> & data) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
fill_executable_list();
|
||||
|
||||
/// Function to reallocate space for entities in the wait set.
|
||||
/// Take the data so that it can be consumed with `execute`.
|
||||
/**
|
||||
* \throws std::runtime_error if wait set couldn't be cleared or resized.
|
||||
* For `StaticExecutorEntitiesCollector`, this always return `nullptr`.
|
||||
* \sa rclcpp::Waitable::take_data()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
prepare_wait_set();
|
||||
std::shared_ptr<void>
|
||||
take_data() override;
|
||||
|
||||
/// Function to add_handles_to_wait_set and wait for work and
|
||||
/**
|
||||
* block until the wait set is ready or until the timeout has been exceeded.
|
||||
* \throws std::runtime_error if wait set couldn't be cleared or filled.
|
||||
* \throws any rcl errors from rcl_wait, \sa rclcpp::exceptions::throw_from_rcl_error()
|
||||
* \throws any rcl errors from rcl_wait, \see rclcpp::exceptions::throw_from_rcl_error()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
@@ -95,24 +102,65 @@ public:
|
||||
* \throws std::runtime_error if it couldn't add guard condition to wait set
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
void
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_number_of_ready_guard_conditions() override;
|
||||
|
||||
/// Add a callback group to an executor.
|
||||
/**
|
||||
* \sa rclcpp::Executor::add_node()
|
||||
* \see rclcpp::Executor::add_callback_group
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
add_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
|
||||
|
||||
/// Add a callback group to an executor.
|
||||
/**
|
||||
* \see rclcpp::Executor::add_callback_group
|
||||
* \return boolean whether the node from the callback group is new
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
add_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
/// Remove a callback group from the executor.
|
||||
/**
|
||||
* \see rclcpp::Executor::remove_callback_group
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
remove_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr);
|
||||
|
||||
/// Remove a callback group from the executor.
|
||||
/**
|
||||
* \see rclcpp::Executor::remove_callback_group_from_map
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
remove_callback_group_from_map(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
/**
|
||||
* \see rclcpp::Executor::add_node()
|
||||
* \throw std::runtime_error if node was already added
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
bool
|
||||
add_node(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
|
||||
|
||||
/**
|
||||
* \sa rclcpp::Executor::remove_node()
|
||||
* \see rclcpp::Executor::remove_node()
|
||||
* \throw std::runtime_error if no guard condition is associated with node.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
@@ -120,6 +168,26 @@ public:
|
||||
remove_node(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_all_callback_groups();
|
||||
|
||||
/// Get callback groups that belong to executor.
|
||||
/**
|
||||
* \see rclcpp::Executor::get_manually_added_callback_groups()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_manually_added_callback_groups();
|
||||
|
||||
/// Get callback groups that belong to executor.
|
||||
/**
|
||||
* \see rclcpp::Executor::get_automatically_added_callback_groups_from_nodes()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_automatically_added_callback_groups_from_nodes();
|
||||
|
||||
/// Complete all available queued work without blocking.
|
||||
/**
|
||||
* This function checks if after the guard condition was triggered
|
||||
@@ -216,20 +284,71 @@ public:
|
||||
get_waitable(size_t i) {return exec_list_.waitable[i];}
|
||||
|
||||
private:
|
||||
/// Nodes guard conditions which trigger this waitable
|
||||
std::list<const rcl_guard_condition_t *> guard_conditions_;
|
||||
/// Function to reallocate space for entities in the wait set.
|
||||
/**
|
||||
* \throws std::runtime_error if wait set couldn't be cleared or resized.
|
||||
*/
|
||||
void
|
||||
prepare_wait_set();
|
||||
|
||||
void
|
||||
fill_executable_list();
|
||||
|
||||
void
|
||||
fill_memory_strategy();
|
||||
|
||||
/// Return true if the node belongs to the collector
|
||||
/**
|
||||
* \param[in] node_ptr a node base interface shared pointer
|
||||
* \param[in] weak_groups_to_nodes map to nodes to lookup
|
||||
* \return boolean whether a node belongs the collector
|
||||
*/
|
||||
bool
|
||||
has_node(
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) const;
|
||||
|
||||
/// Add all callback groups that can be automatically added by any executor
|
||||
/// and is not already associated with an executor from nodes
|
||||
/// that are associated with executor
|
||||
/**
|
||||
* \see rclcpp::Executor::add_callback_groups_from_nodes_associated_to_executor()
|
||||
*/
|
||||
void
|
||||
add_callback_groups_from_nodes_associated_to_executor();
|
||||
|
||||
void
|
||||
fill_executable_list_from_map(const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
/// Memory strategy: an interface for handling user-defined memory allocation strategies.
|
||||
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy_;
|
||||
|
||||
// maps callback groups to nodes.
|
||||
WeakCallbackGroupsToNodesMap weak_groups_associated_with_executor_to_nodes_;
|
||||
// maps callback groups to nodes.
|
||||
WeakCallbackGroupsToNodesMap weak_groups_to_nodes_associated_with_executor_;
|
||||
|
||||
typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
|
||||
const rclcpp::GuardCondition *,
|
||||
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>
|
||||
WeakNodesToGuardConditionsMap;
|
||||
WeakNodesToGuardConditionsMap weak_nodes_to_guard_conditions_;
|
||||
|
||||
/// List of weak nodes registered in the static executor
|
||||
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> weak_nodes_;
|
||||
|
||||
// Mutex to protect vector of new nodes.
|
||||
std::mutex new_nodes_mutex_;
|
||||
std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> new_nodes_;
|
||||
|
||||
/// Wait set for managing entities that the rmw layer waits on.
|
||||
rcl_wait_set_t * p_wait_set_ = nullptr;
|
||||
|
||||
/// Executable list: timers, subscribers, clients, services and waitables
|
||||
rclcpp::experimental::ExecutableList exec_list_;
|
||||
|
||||
/// Bool to check if the entities collector has been initialized
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace executors
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef RCLCPP__EXECUTORS__STATIC_SINGLE_THREADED_EXECUTOR_HPP_
|
||||
#define RCLCPP__EXECUTORS__STATIC_SINGLE_THREADED_EXECUTOR_HPP_
|
||||
|
||||
#include <chrono>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
@@ -78,15 +79,67 @@ public:
|
||||
void
|
||||
spin() override;
|
||||
|
||||
/// Static executor implementation of spin some
|
||||
/**
|
||||
* This non-blocking function will execute entities that
|
||||
* were ready when this API was called, until timeout or no
|
||||
* more work available. Entities that got ready while
|
||||
* executing work, won't be taken into account here.
|
||||
*
|
||||
* Example:
|
||||
* while(condition) {
|
||||
* spin_some();
|
||||
* sleep(); // User should have some sync work or
|
||||
* // sleep to avoid a 100% CPU usage
|
||||
* }
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0)) override;
|
||||
|
||||
/// Static executor implementation of spin all
|
||||
/**
|
||||
* This non-blocking function will execute entities until timeout (must be >= 0)
|
||||
* or no more work available.
|
||||
* If timeout is `0`, potentially it blocks forever until no more work is available.
|
||||
* If new entities get ready while executing work available, they will be executed
|
||||
* as long as the timeout hasn't expired.
|
||||
*
|
||||
* Example:
|
||||
* while(condition) {
|
||||
* spin_all();
|
||||
* sleep(); // User should have some sync work or
|
||||
* // sleep to avoid a 100% CPU usage
|
||||
* }
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_all(std::chrono::nanoseconds max_duration) override;
|
||||
|
||||
/// Add a callback group to an executor.
|
||||
/**
|
||||
* \sa rclcpp::Executor::add_callback_group
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
bool notify = true) override;
|
||||
|
||||
/// Remove callback group from the executor
|
||||
/**
|
||||
* \sa rclcpp::Executor::remove_callback_group
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
bool notify = true) override;
|
||||
|
||||
/// Add a node to the executor.
|
||||
/**
|
||||
* An executor can have zero or more nodes which provide work during `spin` functions.
|
||||
* \param[in] node_ptr Shared pointer to the node to be added.
|
||||
* \param[in] notify True to trigger the interrupt guard condition during this function. If
|
||||
* the executor is blocked at the rmw layer while waiting for work and it is notified that a new
|
||||
* node was added, it will wake up.
|
||||
* \throw std::runtime_error if node was already added or if rcl_trigger_guard_condition
|
||||
* return an error
|
||||
* \sa rclcpp::Executor::add_node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
@@ -96,8 +149,7 @@ public:
|
||||
|
||||
/// Convenience function which takes Node and forwards NodeBaseInterface.
|
||||
/**
|
||||
* \throw std::runtime_error if node was already added or if rcl_trigger_guard_condition
|
||||
* returns an error
|
||||
* \sa rclcpp::StaticSingleThreadedExecutor::add_node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
@@ -105,11 +157,7 @@ public:
|
||||
|
||||
/// Remove a node from the executor.
|
||||
/**
|
||||
* \param[in] node_ptr Shared pointer to the node to remove.
|
||||
* \param[in] notify True to trigger the interrupt guard condition and wake up the executor.
|
||||
* This is useful if the last node was removed from the executor while the executor was blocked
|
||||
* waiting for work in another thread, because otherwise the executor would never be notified.
|
||||
* \throw std::runtime_error if rcl_trigger_guard_condition returns an error
|
||||
* \sa rclcpp::Executor::remove_node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
@@ -119,87 +167,49 @@ public:
|
||||
|
||||
/// Convenience function which takes Node and forwards NodeBaseInterface.
|
||||
/**
|
||||
* \throw std::runtime_error if rcl_trigger_guard_condition returns an error
|
||||
* \sa rclcpp::Executor::remove_node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
|
||||
|
||||
/// Spin (blocking) until the future is complete, it times out waiting, or rclcpp is interrupted.
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_all_callback_groups() override;
|
||||
|
||||
/// Get callback groups that belong to executor.
|
||||
/**
|
||||
* \param[in] future The future to wait on. If this function returns SUCCESS, the future can be
|
||||
* accessed without blocking (though it may still throw an exception).
|
||||
* \param[in] timeout Optional timeout parameter, which gets passed to
|
||||
* Executor::execute_ready_executables.
|
||||
* `-1` is block forever, `0` is non-blocking.
|
||||
* If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return
|
||||
* code.
|
||||
* \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
|
||||
*
|
||||
* Example usage:
|
||||
* rclcpp::executors::StaticSingleThreadedExecutor exec;
|
||||
* // ... other part of code like creating node
|
||||
* // define future
|
||||
* exec.add_node(node);
|
||||
* exec.spin_until_future_complete(future);
|
||||
*/
|
||||
template<typename ResponseT, typename TimeRepT = int64_t, typename TimeT = std::milli>
|
||||
rclcpp::FutureReturnCode
|
||||
spin_until_future_complete(
|
||||
std::shared_future<ResponseT> & future,
|
||||
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
|
||||
{
|
||||
std::future_status status = future.wait_for(std::chrono::seconds(0));
|
||||
if (status == std::future_status::ready) {
|
||||
return rclcpp::FutureReturnCode::SUCCESS;
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
std::chrono::nanoseconds timeout_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
timeout);
|
||||
if (timeout_ns > std::chrono::nanoseconds::zero()) {
|
||||
end_time += timeout_ns;
|
||||
}
|
||||
std::chrono::nanoseconds timeout_left = timeout_ns;
|
||||
|
||||
entities_collector_ = std::make_shared<StaticExecutorEntitiesCollector>();
|
||||
entities_collector_->init(&wait_set_, memory_strategy_, &interrupt_guard_condition_);
|
||||
|
||||
while (rclcpp::ok(this->context_)) {
|
||||
// Do one set of work.
|
||||
entities_collector_->refresh_wait_set(timeout_left);
|
||||
execute_ready_executables();
|
||||
// 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) {
|
||||
return rclcpp::FutureReturnCode::SUCCESS;
|
||||
}
|
||||
// If the original timeout is < 0, then this is blocking, never TIMEOUT.
|
||||
if (timeout_ns < std::chrono::nanoseconds::zero()) {
|
||||
continue;
|
||||
}
|
||||
// Otherwise check if we still have time to wait, return TIMEOUT if not.
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (now >= end_time) {
|
||||
return rclcpp::FutureReturnCode::TIMEOUT;
|
||||
}
|
||||
// Subtract the elapsed time from the original timeout.
|
||||
timeout_left = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - now);
|
||||
}
|
||||
|
||||
// The future did not complete before ok() returned false, return INTERRUPTED.
|
||||
return rclcpp::FutureReturnCode::INTERRUPTED;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Check which executables in ExecutableList struct are ready from wait_set and execute them.
|
||||
/**
|
||||
* \param[in] exec_list Structure that can hold subscriptionbases, timerbases, etc
|
||||
* \param[in] timeout Optional timeout parameter.
|
||||
* \sa rclcpp::Executor::get_manually_added_callback_groups()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_manually_added_callback_groups() override;
|
||||
|
||||
/// Get callback groups that belong to executor.
|
||||
/**
|
||||
* \sa rclcpp::Executor::get_automatically_added_callback_groups_from_nodes()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_automatically_added_callback_groups_from_nodes() override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Executes ready executables from wait set.
|
||||
* @param spin_once if true executes only the first ready executable.
|
||||
* @return true if any executable was ready.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
execute_ready_executables(bool spin_once = false);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
execute_ready_executables();
|
||||
spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_once_impl(std::chrono::nanoseconds timeout) override;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(StaticSingleThreadedExecutor)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define RCLCPP__EXPERIMENTAL__BUFFERS__INTRA_PROCESS_BUFFER_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
#ifndef RCLCPP__EXPERIMENTAL__BUFFERS__RING_BUFFER_IMPLEMENTATION_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__BUFFERS__RING_BUFFER_IMPLEMENTATION_HPP_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
@@ -37,6 +33,10 @@ namespace experimental
|
||||
namespace buffers
|
||||
{
|
||||
|
||||
/// Store elements in a fixed-size, FIFO buffer
|
||||
/**
|
||||
* All public member functions are thread-safe.
|
||||
*/
|
||||
template<typename BufferT>
|
||||
class RingBufferImplementation : public BufferImplementationBase<BufferT>
|
||||
{
|
||||
@@ -55,55 +55,124 @@ public:
|
||||
|
||||
virtual ~RingBufferImplementation() {}
|
||||
|
||||
/// Add a new element to store in the ring buffer
|
||||
/**
|
||||
* This member function is thread-safe.
|
||||
*
|
||||
* \param request the element to be stored in the ring buffer
|
||||
*/
|
||||
void enqueue(BufferT request)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
write_index_ = next(write_index_);
|
||||
write_index_ = next_(write_index_);
|
||||
ring_buffer_[write_index_] = std::move(request);
|
||||
|
||||
if (is_full()) {
|
||||
read_index_ = next(read_index_);
|
||||
if (is_full_()) {
|
||||
read_index_ = next_(read_index_);
|
||||
} else {
|
||||
size_++;
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove the oldest element from ring buffer
|
||||
/**
|
||||
* This member function is thread-safe.
|
||||
*
|
||||
* \return the element that is being removed from the ring buffer
|
||||
*/
|
||||
BufferT dequeue()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (!has_data()) {
|
||||
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Calling dequeue on empty intra-process buffer");
|
||||
throw std::runtime_error("Calling dequeue on empty intra-process buffer");
|
||||
if (!has_data_()) {
|
||||
return BufferT();
|
||||
}
|
||||
|
||||
auto request = std::move(ring_buffer_[read_index_]);
|
||||
read_index_ = next(read_index_);
|
||||
read_index_ = next_(read_index_);
|
||||
|
||||
size_--;
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
/// Get the next index value for the ring buffer
|
||||
/**
|
||||
* This member function is thread-safe.
|
||||
*
|
||||
* \param val the current index value
|
||||
* \return the next index value
|
||||
*/
|
||||
inline size_t next(size_t val)
|
||||
{
|
||||
return (val + 1) % capacity_;
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return next_(val);
|
||||
}
|
||||
|
||||
/// Get if the ring buffer has at least one element stored
|
||||
/**
|
||||
* This member function is thread-safe.
|
||||
*
|
||||
* \return `true` if there is data and `false` otherwise
|
||||
*/
|
||||
inline bool has_data() const
|
||||
{
|
||||
return size_ != 0;
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return has_data_();
|
||||
}
|
||||
|
||||
inline bool is_full()
|
||||
/// Get if the size of the buffer is equal to its capacity
|
||||
/**
|
||||
* This member function is thread-safe.
|
||||
*
|
||||
* \return `true` if the size of the buffer is equal is capacity
|
||||
* and `false` otherwise
|
||||
*/
|
||||
inline bool is_full() const
|
||||
{
|
||||
return size_ == capacity_;
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return is_full_();
|
||||
}
|
||||
|
||||
void clear() {}
|
||||
|
||||
private:
|
||||
/// Get the next index value for the ring buffer
|
||||
/**
|
||||
* This member function is not thread-safe.
|
||||
*
|
||||
* \param val the current index value
|
||||
* \return the next index value
|
||||
*/
|
||||
inline size_t next_(size_t val)
|
||||
{
|
||||
return (val + 1) % capacity_;
|
||||
}
|
||||
|
||||
/// Get if the ring buffer has at least one element stored
|
||||
/**
|
||||
* This member function is not thread-safe.
|
||||
*
|
||||
* \return `true` if there is data and `false` otherwise
|
||||
*/
|
||||
inline bool has_data_() const
|
||||
{
|
||||
return size_ != 0;
|
||||
}
|
||||
|
||||
/// Get if the size of the buffer is equal to its capacity
|
||||
/**
|
||||
* This member function is not thread-safe.
|
||||
*
|
||||
* \return `true` if the size of the buffer is equal is capacity
|
||||
* and `false` otherwise
|
||||
*/
|
||||
inline bool is_full_() const
|
||||
{
|
||||
return size_ == capacity_;
|
||||
}
|
||||
|
||||
size_t capacity_;
|
||||
|
||||
std::vector<BufferT> ring_buffer_;
|
||||
@@ -112,7 +181,7 @@ private:
|
||||
size_t read_index_;
|
||||
size_t size_;
|
||||
|
||||
std::mutex mutex_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
} // namespace buffers
|
||||
|
||||
@@ -16,14 +16,13 @@
|
||||
#define RCLCPP__EXPERIMENTAL__CREATE_INTRA_PROCESS_BUFFER_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include "rcl/subscription.h"
|
||||
|
||||
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/buffers/ring_buffer_implementation.hpp"
|
||||
#include "rclcpp/intra_process_buffer_type.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -37,13 +36,13 @@ template<
|
||||
typename rclcpp::experimental::buffers::IntraProcessBuffer<MessageT, Alloc, Deleter>::UniquePtr
|
||||
create_intra_process_buffer(
|
||||
IntraProcessBufferType buffer_type,
|
||||
rmw_qos_profile_t qos,
|
||||
const rclcpp::QoS & qos,
|
||||
std::shared_ptr<Alloc> allocator)
|
||||
{
|
||||
using MessageSharedPtr = std::shared_ptr<const MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
|
||||
|
||||
size_t buffer_size = qos.depth;
|
||||
size_t buffer_size = qos.depth();
|
||||
|
||||
using rclcpp::experimental::buffers::IntraProcessBuffer;
|
||||
typename IntraProcessBuffer<MessageT, Alloc, Deleter>::UniquePtr buffer;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#ifndef RCLCPP__EXPERIMENTAL__EXECUTABLE_LIST_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__EXECUTABLE_LIST_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/client.hpp"
|
||||
|
||||
@@ -19,24 +19,24 @@
|
||||
|
||||
#include <shared_mutex>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <map>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <typeinfo>
|
||||
|
||||
#include "rclcpp/allocator/allocator_deleter.hpp"
|
||||
#include "rclcpp/experimental/ros_message_intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_buffer.hpp"
|
||||
#include "rclcpp/logger.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/publisher_base.hpp"
|
||||
#include "rclcpp/type_adapter.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -172,16 +172,19 @@ public:
|
||||
*
|
||||
* \param intra_process_publisher_id the id of the publisher of this message.
|
||||
* \param message the message that is being stored.
|
||||
* \param allocator for allocations when buffering messages.
|
||||
*/
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<MessageT>>
|
||||
typename ROSMessageType,
|
||||
typename Alloc,
|
||||
typename Deleter = std::default_delete<MessageT>
|
||||
>
|
||||
void
|
||||
do_intra_process_publish(
|
||||
uint64_t intra_process_publisher_id,
|
||||
std::unique_ptr<MessageT, Deleter> message,
|
||||
std::shared_ptr<typename allocator::AllocRebind<MessageT, Alloc>::allocator_type> allocator)
|
||||
typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
|
||||
{
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageAllocatorT = typename MessageAllocTraits::allocator_type;
|
||||
@@ -202,12 +205,13 @@ public:
|
||||
// None of the buffers require ownership, so we promote the pointer
|
||||
std::shared_ptr<MessageT> msg = std::move(message);
|
||||
|
||||
this->template add_shared_msg_to_buffers<MessageT>(msg, sub_ids.take_shared_subscriptions);
|
||||
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
|
||||
msg, sub_ids.take_shared_subscriptions);
|
||||
} else if (!sub_ids.take_ownership_subscriptions.empty() && // NOLINT
|
||||
sub_ids.take_shared_subscriptions.size() <= 1)
|
||||
{
|
||||
// There is at maximum 1 buffer that does not require ownership.
|
||||
// So we this case is equivalent to all the buffers requiring ownership
|
||||
// So this case is equivalent to all the buffers requiring ownership
|
||||
|
||||
// Merge the two vector of ids into a unique one
|
||||
std::vector<uint64_t> concatenated_vector(sub_ids.take_shared_subscriptions);
|
||||
@@ -215,8 +219,7 @@ public:
|
||||
concatenated_vector.end(),
|
||||
sub_ids.take_ownership_subscriptions.begin(),
|
||||
sub_ids.take_ownership_subscriptions.end());
|
||||
|
||||
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter>(
|
||||
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
|
||||
std::move(message),
|
||||
concatenated_vector,
|
||||
allocator);
|
||||
@@ -225,24 +228,26 @@ public:
|
||||
{
|
||||
// Construct a new shared pointer from the message
|
||||
// for the buffers that do not require ownership
|
||||
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(*allocator, *message);
|
||||
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(allocator, *message);
|
||||
|
||||
this->template add_shared_msg_to_buffers<MessageT>(
|
||||
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
|
||||
shared_msg, sub_ids.take_shared_subscriptions);
|
||||
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter>(
|
||||
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
|
||||
std::move(message), sub_ids.take_ownership_subscriptions, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<MessageT>>
|
||||
typename ROSMessageType,
|
||||
typename Alloc,
|
||||
typename Deleter = std::default_delete<MessageT>
|
||||
>
|
||||
std::shared_ptr<const MessageT>
|
||||
do_intra_process_publish_and_return_shared(
|
||||
uint64_t intra_process_publisher_id,
|
||||
std::unique_ptr<MessageT, Deleter> message,
|
||||
std::shared_ptr<typename allocator::AllocRebind<MessageT, Alloc>::allocator_type> allocator)
|
||||
typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
|
||||
{
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageAllocatorT = typename MessageAllocTraits::allocator_type;
|
||||
@@ -263,27 +268,26 @@ public:
|
||||
// If there are no owning, just convert to shared.
|
||||
std::shared_ptr<MessageT> shared_msg = std::move(message);
|
||||
if (!sub_ids.take_shared_subscriptions.empty()) {
|
||||
this->template add_shared_msg_to_buffers<MessageT>(
|
||||
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
|
||||
shared_msg, sub_ids.take_shared_subscriptions);
|
||||
}
|
||||
return shared_msg;
|
||||
} else {
|
||||
// Construct a new shared pointer from the message for the buffers that
|
||||
// do not require ownership and to return.
|
||||
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(*allocator, *message);
|
||||
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(allocator, *message);
|
||||
|
||||
if (!sub_ids.take_shared_subscriptions.empty()) {
|
||||
this->template add_shared_msg_to_buffers<MessageT>(
|
||||
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
|
||||
shared_msg,
|
||||
sub_ids.take_shared_subscriptions);
|
||||
}
|
||||
if (!sub_ids.take_ownership_subscriptions.empty()) {
|
||||
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter>(
|
||||
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
|
||||
std::move(message),
|
||||
sub_ids.take_ownership_subscriptions,
|
||||
allocator);
|
||||
}
|
||||
|
||||
return shared_msg;
|
||||
}
|
||||
}
|
||||
@@ -303,25 +307,6 @@ public:
|
||||
get_subscription_intra_process(uint64_t intra_process_subscription_id);
|
||||
|
||||
private:
|
||||
struct SubscriptionInfo
|
||||
{
|
||||
SubscriptionInfo() = default;
|
||||
|
||||
rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr subscription;
|
||||
rmw_qos_profile_t qos;
|
||||
const char * topic_name;
|
||||
bool use_take_shared_method;
|
||||
};
|
||||
|
||||
struct PublisherInfo
|
||||
{
|
||||
PublisherInfo() = default;
|
||||
|
||||
rclcpp::PublisherBase::WeakPtr publisher;
|
||||
rmw_qos_profile_t qos;
|
||||
const char * topic_name;
|
||||
};
|
||||
|
||||
struct SplittedSubscriptions
|
||||
{
|
||||
std::vector<uint64_t> take_shared_subscriptions;
|
||||
@@ -329,10 +314,10 @@ private:
|
||||
};
|
||||
|
||||
using SubscriptionMap =
|
||||
std::unordered_map<uint64_t, SubscriptionInfo>;
|
||||
std::unordered_map<uint64_t, rclcpp::experimental::SubscriptionIntraProcessBase::WeakPtr>;
|
||||
|
||||
using PublisherMap =
|
||||
std::unordered_map<uint64_t, PublisherInfo>;
|
||||
std::unordered_map<uint64_t, rclcpp::PublisherBase::WeakPtr>;
|
||||
|
||||
using PublisherToSubscriptionIdsMap =
|
||||
std::unordered_map<uint64_t, SplittedSubscriptions>;
|
||||
@@ -348,65 +333,177 @@ private:
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
can_communicate(PublisherInfo pub_info, SubscriptionInfo sub_info) const;
|
||||
can_communicate(
|
||||
rclcpp::PublisherBase::SharedPtr pub,
|
||||
rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr sub) const;
|
||||
|
||||
template<typename MessageT>
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc,
|
||||
typename Deleter,
|
||||
typename ROSMessageType>
|
||||
void
|
||||
add_shared_msg_to_buffers(
|
||||
std::shared_ptr<const MessageT> message,
|
||||
std::vector<uint64_t> subscription_ids)
|
||||
{
|
||||
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, Alloc>;
|
||||
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
|
||||
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
|
||||
|
||||
using PublishedType = typename rclcpp::TypeAdapter<MessageT>::custom_type;
|
||||
using PublishedTypeAllocatorTraits = allocator::AllocRebind<PublishedType, Alloc>;
|
||||
using PublishedTypeAllocator = typename PublishedTypeAllocatorTraits::allocator_type;
|
||||
using PublishedTypeDeleter = allocator::Deleter<PublishedTypeAllocator, PublishedType>;
|
||||
|
||||
for (auto id : subscription_ids) {
|
||||
auto subscription_it = subscriptions_.find(id);
|
||||
if (subscription_it == subscriptions_.end()) {
|
||||
throw std::runtime_error("subscription has unexpectedly gone out of scope");
|
||||
}
|
||||
auto subscription_base = subscription_it->second.subscription;
|
||||
auto subscription_base = subscription_it->second.lock();
|
||||
if (subscription_base == nullptr) {
|
||||
subscriptions_.erase(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto subscription = std::static_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcess<MessageT>
|
||||
auto subscription = std::dynamic_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcessBuffer<PublishedType,
|
||||
PublishedTypeAllocator, PublishedTypeDeleter, ROSMessageType>
|
||||
>(subscription_base);
|
||||
if (subscription != nullptr) {
|
||||
subscription->provide_intra_process_data(message);
|
||||
continue;
|
||||
}
|
||||
|
||||
subscription->provide_intra_process_message(message);
|
||||
auto ros_message_subscription = std::dynamic_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionROSMsgIntraProcessBuffer<ROSMessageType,
|
||||
ROSMessageTypeAllocator, ROSMessageTypeDeleter>
|
||||
>(subscription_base);
|
||||
if (nullptr == ros_message_subscription) {
|
||||
throw std::runtime_error(
|
||||
"failed to dynamic cast SubscriptionIntraProcessBase to "
|
||||
"SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>, or to "
|
||||
"SubscriptionROSMsgIntraProcessBuffer<ROSMessageType,ROSMessageTypeAllocator,"
|
||||
"ROSMessageTypeDeleter> which can happen when the publisher and "
|
||||
"subscription use different allocator types, which is not supported");
|
||||
}
|
||||
|
||||
if constexpr (rclcpp::TypeAdapter<MessageT>::is_specialized::value) {
|
||||
ROSMessageType ros_msg;
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*message, ros_msg);
|
||||
ros_message_subscription->provide_intra_process_message(
|
||||
std::make_shared<ROSMessageType>(ros_msg));
|
||||
} else {
|
||||
if constexpr (std::is_same<MessageT, ROSMessageType>::value) {
|
||||
ros_message_subscription->provide_intra_process_message(message);
|
||||
} else {
|
||||
if constexpr (std::is_same<typename rclcpp::TypeAdapter<MessageT,
|
||||
ROSMessageType>::ros_message_type, ROSMessageType>::value)
|
||||
{
|
||||
ROSMessageType ros_msg;
|
||||
rclcpp::TypeAdapter<MessageT, ROSMessageType>::convert_to_ros_message(
|
||||
*message, ros_msg);
|
||||
ros_message_subscription->provide_intra_process_message(
|
||||
std::make_shared<ROSMessageType>(ros_msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<MessageT>>
|
||||
typename Alloc,
|
||||
typename Deleter,
|
||||
typename ROSMessageType>
|
||||
void
|
||||
add_owned_msg_to_buffers(
|
||||
std::unique_ptr<MessageT, Deleter> message,
|
||||
std::vector<uint64_t> subscription_ids,
|
||||
std::shared_ptr<typename allocator::AllocRebind<MessageT, Alloc>::allocator_type> allocator)
|
||||
typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
|
||||
{
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
|
||||
|
||||
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, Alloc>;
|
||||
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
|
||||
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
|
||||
|
||||
using PublishedType = typename rclcpp::TypeAdapter<MessageT>::custom_type;
|
||||
using PublishedTypeAllocatorTraits = allocator::AllocRebind<PublishedType, Alloc>;
|
||||
using PublishedTypeAllocator = typename PublishedTypeAllocatorTraits::allocator_type;
|
||||
using PublishedTypeDeleter = allocator::Deleter<PublishedTypeAllocator, PublishedType>;
|
||||
|
||||
for (auto it = subscription_ids.begin(); it != subscription_ids.end(); it++) {
|
||||
auto subscription_it = subscriptions_.find(*it);
|
||||
if (subscription_it == subscriptions_.end()) {
|
||||
throw std::runtime_error("subscription has unexpectedly gone out of scope");
|
||||
}
|
||||
auto subscription_base = subscription_it->second.subscription;
|
||||
auto subscription_base = subscription_it->second.lock();
|
||||
if (subscription_base == nullptr) {
|
||||
subscriptions_.erase(subscription_it);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto subscription = std::static_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcess<MessageT>
|
||||
auto subscription = std::dynamic_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcessBuffer<PublishedType,
|
||||
PublishedTypeAllocator, PublishedTypeDeleter, ROSMessageType>
|
||||
>(subscription_base);
|
||||
if (subscription != nullptr) {
|
||||
if (std::next(it) == subscription_ids.end()) {
|
||||
// If this is the last subscription, give up ownership
|
||||
subscription->provide_intra_process_data(std::move(message));
|
||||
} else {
|
||||
// Copy the message since we have additional subscriptions to serve
|
||||
Deleter deleter = message.get_deleter();
|
||||
auto ptr = MessageAllocTraits::allocate(allocator, 1);
|
||||
MessageAllocTraits::construct(allocator, ptr, *message);
|
||||
|
||||
if (std::next(it) == subscription_ids.end()) {
|
||||
// If this is the last subscription, give up ownership
|
||||
subscription->provide_intra_process_message(std::move(message));
|
||||
subscription->provide_intra_process_data(std::move(MessageUniquePtr(ptr, deleter)));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
auto ros_message_subscription = std::dynamic_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionROSMsgIntraProcessBuffer<ROSMessageType,
|
||||
ROSMessageTypeAllocator, ROSMessageTypeDeleter>
|
||||
>(subscription_base);
|
||||
if (nullptr == ros_message_subscription) {
|
||||
throw std::runtime_error(
|
||||
"failed to dynamic cast SubscriptionIntraProcessBase to "
|
||||
"SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>, or to "
|
||||
"SubscriptionROSMsgIntraProcessBuffer<ROSMessageType,ROSMessageTypeAllocator,"
|
||||
"ROSMessageTypeDeleter> which can happen when the publisher and "
|
||||
"subscription use different allocator types, which is not supported");
|
||||
}
|
||||
|
||||
if constexpr (rclcpp::TypeAdapter<MessageT>::is_specialized::value) {
|
||||
ROSMessageTypeAllocator ros_message_alloc(allocator);
|
||||
auto ptr = ros_message_alloc.allocate(1);
|
||||
ros_message_alloc.construct(ptr);
|
||||
ROSMessageTypeDeleter deleter;
|
||||
allocator::set_allocator_for_deleter(&deleter, &allocator);
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*message, *ptr);
|
||||
auto ros_msg = std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, deleter);
|
||||
ros_message_subscription->provide_intra_process_message(std::move(ros_msg));
|
||||
} else {
|
||||
// Copy the message since we have additional subscriptions to serve
|
||||
MessageUniquePtr copy_message;
|
||||
Deleter deleter = message.get_deleter();
|
||||
auto ptr = MessageAllocTraits::allocate(*allocator.get(), 1);
|
||||
MessageAllocTraits::construct(*allocator.get(), ptr, *message);
|
||||
copy_message = MessageUniquePtr(ptr, deleter);
|
||||
if constexpr (std::is_same<MessageT, ROSMessageType>::value) {
|
||||
if (std::next(it) == subscription_ids.end()) {
|
||||
// If this is the last subscription, give up ownership
|
||||
ros_message_subscription->provide_intra_process_message(std::move(message));
|
||||
} else {
|
||||
// Copy the message since we have additional subscriptions to serve
|
||||
Deleter deleter = message.get_deleter();
|
||||
allocator::set_allocator_for_deleter(&deleter, &allocator);
|
||||
auto ptr = MessageAllocTraits::allocate(allocator, 1);
|
||||
MessageAllocTraits::construct(allocator, ptr, *message);
|
||||
|
||||
subscription->provide_intra_process_message(std::move(copy_message));
|
||||
ros_message_subscription->provide_intra_process_message(
|
||||
std::move(MessageUniquePtr(ptr, deleter)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright 2021 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__EXPERIMENTAL__ROS_MESSAGE_INTRA_PROCESS_BUFFER_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__ROS_MESSAGE_INTRA_PROCESS_BUFFER_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
|
||||
#include "rclcpp/any_subscription_callback.hpp"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
|
||||
#include "tracetools/tracetools.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace experimental
|
||||
{
|
||||
|
||||
template<
|
||||
typename RosMessageT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<void>
|
||||
>
|
||||
class SubscriptionROSMsgIntraProcessBuffer : public SubscriptionIntraProcessBase
|
||||
{
|
||||
public:
|
||||
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<RosMessageT, Alloc>;
|
||||
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
|
||||
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, RosMessageT>;
|
||||
|
||||
using ConstMessageSharedPtr = std::shared_ptr<const RosMessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<RosMessageT, ROSMessageTypeDeleter>;
|
||||
|
||||
SubscriptionROSMsgIntraProcessBuffer(
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos_profile)
|
||||
: SubscriptionIntraProcessBase(context, topic_name, qos_profile)
|
||||
{}
|
||||
|
||||
virtual ~SubscriptionROSMsgIntraProcessBuffer()
|
||||
{}
|
||||
|
||||
virtual void
|
||||
provide_intra_process_message(ConstMessageSharedPtr message) = 0;
|
||||
|
||||
virtual void
|
||||
provide_intra_process_message(MessageUniquePtr message) = 0;
|
||||
};
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXPERIMENTAL__ROS_MESSAGE_INTRA_PROCESS_BUFFER_HPP_
|
||||
@@ -15,21 +15,22 @@
|
||||
#ifndef RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
|
||||
|
||||
#include <rmw/rmw.h>
|
||||
#include <rmw/types.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/types.h"
|
||||
|
||||
#include "rclcpp/any_subscription_callback.hpp"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/create_intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_buffer.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
#include "tracetools/tracetools.h"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -39,61 +40,57 @@ namespace experimental
|
||||
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<MessageT>,
|
||||
typename CallbackMessageT = MessageT>
|
||||
class SubscriptionIntraProcess : public SubscriptionIntraProcessBase
|
||||
typename SubscribedType,
|
||||
typename SubscribedTypeAlloc = std::allocator<SubscribedType>,
|
||||
typename SubscribedTypeDeleter = std::default_delete<SubscribedType>,
|
||||
typename ROSMessageType = SubscribedType,
|
||||
typename Alloc = std::allocator<void>
|
||||
>
|
||||
class SubscriptionIntraProcess
|
||||
: public SubscriptionIntraProcessBuffer<
|
||||
SubscribedType,
|
||||
SubscribedTypeAlloc,
|
||||
SubscribedTypeDeleter,
|
||||
ROSMessageType
|
||||
>
|
||||
{
|
||||
using SubscriptionIntraProcessBufferT = SubscriptionIntraProcessBuffer<
|
||||
SubscribedType,
|
||||
SubscribedTypeAlloc,
|
||||
SubscribedTypeDeleter,
|
||||
ROSMessageType
|
||||
>;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
|
||||
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageAlloc = typename MessageAllocTraits::allocator_type;
|
||||
using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
|
||||
|
||||
using BufferUniquePtr = typename rclcpp::experimental::buffers::IntraProcessBuffer<
|
||||
MessageT,
|
||||
Alloc,
|
||||
Deleter
|
||||
>::UniquePtr;
|
||||
using MessageAllocTraits =
|
||||
typename SubscriptionIntraProcessBufferT::SubscribedTypeAllocatorTraits;
|
||||
using MessageAlloc = typename SubscriptionIntraProcessBufferT::SubscribedTypeAllocator;
|
||||
using ConstMessageSharedPtr = typename SubscriptionIntraProcessBufferT::ConstDataSharedPtr;
|
||||
using MessageUniquePtr = typename SubscriptionIntraProcessBufferT::SubscribedTypeUniquePtr;
|
||||
using BufferUniquePtr = typename SubscriptionIntraProcessBufferT::BufferUniquePtr;
|
||||
|
||||
SubscriptionIntraProcess(
|
||||
AnySubscriptionCallback<CallbackMessageT, Alloc> callback,
|
||||
AnySubscriptionCallback<MessageT, Alloc> callback,
|
||||
std::shared_ptr<Alloc> allocator,
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::string & topic_name,
|
||||
rmw_qos_profile_t qos_profile,
|
||||
const rclcpp::QoS & qos_profile,
|
||||
rclcpp::IntraProcessBufferType buffer_type)
|
||||
: SubscriptionIntraProcessBase(topic_name, qos_profile),
|
||||
: SubscriptionIntraProcessBuffer<SubscribedType, SubscribedTypeAlloc,
|
||||
SubscribedTypeDeleter, ROSMessageType>(
|
||||
std::make_shared<SubscribedTypeAlloc>(*allocator),
|
||||
context,
|
||||
topic_name,
|
||||
qos_profile,
|
||||
buffer_type),
|
||||
any_callback_(callback)
|
||||
{
|
||||
if (!std::is_same<MessageT, CallbackMessageT>::value) {
|
||||
throw std::runtime_error("SubscriptionIntraProcess wrong callback type");
|
||||
}
|
||||
|
||||
// Create the intra-process buffer.
|
||||
buffer_ = rclcpp::experimental::create_intra_process_buffer<MessageT, Alloc, Deleter>(
|
||||
buffer_type,
|
||||
qos_profile,
|
||||
allocator);
|
||||
|
||||
// Create the guard condition.
|
||||
rcl_guard_condition_options_t guard_condition_options =
|
||||
rcl_guard_condition_get_default_options();
|
||||
|
||||
gc_ = rcl_get_zero_initialized_guard_condition();
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
&gc_, context->get_rcl_context().get(), guard_condition_options);
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw std::runtime_error("SubscriptionIntraProcess init error initializing guard condition");
|
||||
}
|
||||
|
||||
TRACEPOINT(
|
||||
rclcpp_subscription_callback_added,
|
||||
(const void *)this,
|
||||
(const void *)&any_callback_);
|
||||
static_cast<const void *>(this),
|
||||
static_cast<const void *>(&any_callback_));
|
||||
// The callback object gets copied, so if registration is done too early/before this point
|
||||
// (e.g. in `AnySubscriptionCallback::set()`), its address won't match any address used later
|
||||
// in subsequent tracepoints.
|
||||
@@ -102,72 +99,72 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set)
|
||||
virtual ~SubscriptionIntraProcess() = default;
|
||||
|
||||
std::shared_ptr<void>
|
||||
take_data() override
|
||||
{
|
||||
(void)wait_set;
|
||||
return buffer_->has_data();
|
||||
ConstMessageSharedPtr shared_msg;
|
||||
MessageUniquePtr unique_msg;
|
||||
|
||||
if (any_callback_.use_take_shared_method()) {
|
||||
shared_msg = this->buffer_->consume_shared();
|
||||
if (!shared_msg) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
unique_msg = this->buffer_->consume_unique();
|
||||
if (!unique_msg) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::pair<ConstMessageSharedPtr, MessageUniquePtr>>(
|
||||
std::pair<ConstMessageSharedPtr, MessageUniquePtr>(
|
||||
shared_msg, std::move(unique_msg)))
|
||||
);
|
||||
}
|
||||
|
||||
void execute()
|
||||
void execute(std::shared_ptr<void> & data) override
|
||||
{
|
||||
execute_impl<CallbackMessageT>();
|
||||
}
|
||||
|
||||
void
|
||||
provide_intra_process_message(ConstMessageSharedPtr message)
|
||||
{
|
||||
buffer_->add_shared(std::move(message));
|
||||
trigger_guard_condition();
|
||||
}
|
||||
|
||||
void
|
||||
provide_intra_process_message(MessageUniquePtr message)
|
||||
{
|
||||
buffer_->add_unique(std::move(message));
|
||||
trigger_guard_condition();
|
||||
}
|
||||
|
||||
bool
|
||||
use_take_shared_method() const
|
||||
{
|
||||
return buffer_->use_take_shared_method();
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
trigger_guard_condition()
|
||||
{
|
||||
rcl_ret_t ret = rcl_trigger_guard_condition(&gc_);
|
||||
(void)ret;
|
||||
execute_impl<SubscribedType>(data);
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
typename std::enable_if<std::is_same<T, rcl_serialized_message_t>::value, void>::type
|
||||
execute_impl()
|
||||
execute_impl(std::shared_ptr<void> & data)
|
||||
{
|
||||
(void)data;
|
||||
throw std::runtime_error("Subscription intra-process can't handle serialized messages");
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<!std::is_same<T, rcl_serialized_message_t>::value, void>::type
|
||||
execute_impl()
|
||||
execute_impl(std::shared_ptr<void> & data)
|
||||
{
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
rmw_message_info_t msg_info;
|
||||
msg_info.publisher_gid = {0, {0}};
|
||||
msg_info.from_intra_process = true;
|
||||
|
||||
auto shared_ptr = std::static_pointer_cast<std::pair<ConstMessageSharedPtr, MessageUniquePtr>>(
|
||||
data);
|
||||
|
||||
if (any_callback_.use_take_shared_method()) {
|
||||
ConstMessageSharedPtr msg = buffer_->consume_shared();
|
||||
any_callback_.dispatch_intra_process(msg, msg_info);
|
||||
ConstMessageSharedPtr shared_msg = shared_ptr->first;
|
||||
any_callback_.dispatch_intra_process(shared_msg, msg_info);
|
||||
} else {
|
||||
MessageUniquePtr msg = buffer_->consume_unique();
|
||||
any_callback_.dispatch_intra_process(std::move(msg), msg_info);
|
||||
MessageUniquePtr unique_msg = std::move(shared_ptr->second);
|
||||
any_callback_.dispatch_intra_process(std::move(unique_msg), msg_info);
|
||||
}
|
||||
shared_ptr.reset();
|
||||
}
|
||||
|
||||
AnySubscriptionCallback<CallbackMessageT, Alloc> any_callback_;
|
||||
BufferUniquePtr buffer_;
|
||||
AnySubscriptionCallback<MessageT, Alloc> any_callback_;
|
||||
};
|
||||
|
||||
} // namespace experimental
|
||||
|
||||
@@ -15,17 +15,17 @@
|
||||
#ifndef RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BASE_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BASE_HPP_
|
||||
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/wait.h"
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -38,28 +38,48 @@ class SubscriptionIntraProcessBase : public rclcpp::Waitable
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(SubscriptionIntraProcessBase)
|
||||
|
||||
enum class EntityType : std::size_t
|
||||
{
|
||||
Subscription,
|
||||
};
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
SubscriptionIntraProcessBase(const std::string & topic_name, rmw_qos_profile_t qos_profile)
|
||||
: topic_name_(topic_name), qos_profile_(qos_profile)
|
||||
SubscriptionIntraProcessBase(
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos_profile)
|
||||
: gc_(context), topic_name_(topic_name), qos_profile_(qos_profile)
|
||||
{}
|
||||
|
||||
virtual ~SubscriptionIntraProcessBase() = default;
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~SubscriptionIntraProcessBase();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_number_of_ready_guard_conditions() {return 1;}
|
||||
get_number_of_ready_guard_conditions() override {return 1;}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set) override;
|
||||
|
||||
bool
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set);
|
||||
is_ready(rcl_wait_set_t * wait_set) override = 0;
|
||||
|
||||
virtual bool
|
||||
is_ready(rcl_wait_set_t * wait_set) = 0;
|
||||
std::shared_ptr<void>
|
||||
take_data() override = 0;
|
||||
|
||||
virtual void
|
||||
execute() = 0;
|
||||
std::shared_ptr<void>
|
||||
take_data_by_entity_id(size_t id) override
|
||||
{
|
||||
(void)id;
|
||||
return take_data();
|
||||
}
|
||||
|
||||
virtual bool
|
||||
void
|
||||
execute(std::shared_ptr<void> & data) override = 0;
|
||||
|
||||
virtual
|
||||
bool
|
||||
use_take_shared_method() const = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
@@ -67,19 +87,113 @@ public:
|
||||
get_topic_name() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rmw_qos_profile_t
|
||||
QoS
|
||||
get_actual_qos() const;
|
||||
|
||||
protected:
|
||||
std::recursive_mutex reentrant_mutex_;
|
||||
rcl_guard_condition_t gc_;
|
||||
/// Set a callback to be called when each new message arrives.
|
||||
/**
|
||||
* The callback receives a size_t which is the number of messages received
|
||||
* since the last time this callback was called.
|
||||
* Normally this is 1, but can be > 1 if messages were received before any
|
||||
* callback was set.
|
||||
*
|
||||
* The callback also receives an int identifier argument.
|
||||
* This is needed because a Waitable may be composed of several distinct entities,
|
||||
* such as subscriptions, services, etc.
|
||||
* The application should provide a generic callback function that will be then
|
||||
* forwarded by the waitable to all of its entities.
|
||||
* Before forwarding, a different value for the identifier argument will be
|
||||
* bound to the function.
|
||||
* This implies that the provided callback can use the identifier to behave
|
||||
* differently depending on which entity triggered the waitable to become ready.
|
||||
*
|
||||
* Calling it again will clear any previously set callback.
|
||||
*
|
||||
* An exception will be thrown if the callback is not callable.
|
||||
*
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* If you want more information available in the callback, like the subscription
|
||||
* or other information, you may use a lambda with captures or std::bind.
|
||||
*
|
||||
* \param[in] callback functor to be called when a new message is received.
|
||||
*/
|
||||
void
|
||||
set_on_ready_callback(std::function<void(size_t, int)> callback) override
|
||||
{
|
||||
if (!callback) {
|
||||
throw std::invalid_argument(
|
||||
"The callback passed to set_on_ready_callback "
|
||||
"is not callable.");
|
||||
}
|
||||
|
||||
// Note: we bind the int identifier argument to this waitable's entity types
|
||||
auto new_callback =
|
||||
[callback, this](size_t number_of_events) {
|
||||
try {
|
||||
callback(number_of_events, static_cast<int>(EntityType::Subscription));
|
||||
} catch (const std::exception & exception) {
|
||||
RCLCPP_ERROR_STREAM(
|
||||
// TODO(wjwwood): get this class access to the node logger it is associated with
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"rclcpp::SubscriptionIntraProcessBase@" << this <<
|
||||
" caught " << rmw::impl::cpp::demangle(exception) <<
|
||||
" exception in user-provided callback for the 'on ready' callback: " <<
|
||||
exception.what());
|
||||
} catch (...) {
|
||||
RCLCPP_ERROR_STREAM(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"rclcpp::SubscriptionIntraProcessBase@" << this <<
|
||||
" caught unhandled exception in user-provided callback " <<
|
||||
"for the 'on ready' callback");
|
||||
}
|
||||
};
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
on_new_message_callback_ = new_callback;
|
||||
|
||||
if (unread_count_ > 0) {
|
||||
if (qos_profile_.history() == HistoryPolicy::KeepAll) {
|
||||
on_new_message_callback_(unread_count_);
|
||||
} else {
|
||||
// Use qos profile depth as upper bound for unread_count_
|
||||
on_new_message_callback_(std::min(unread_count_, qos_profile_.depth()));
|
||||
}
|
||||
unread_count_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Unset the callback registered for new messages, if any.
|
||||
void
|
||||
clear_on_ready_callback() override
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
on_new_message_callback_ = nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::recursive_mutex callback_mutex_;
|
||||
std::function<void(size_t)> on_new_message_callback_ {nullptr};
|
||||
size_t unread_count_{0};
|
||||
rclcpp::GuardCondition gc_;
|
||||
|
||||
private:
|
||||
virtual void
|
||||
trigger_guard_condition() = 0;
|
||||
|
||||
void
|
||||
invoke_on_new_message()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(this->callback_mutex_);
|
||||
if (this->on_new_message_callback_) {
|
||||
this->on_new_message_callback_(1);
|
||||
} else {
|
||||
this->unread_count_++;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string topic_name_;
|
||||
rmw_qos_profile_t qos_profile_;
|
||||
QoS qos_profile_;
|
||||
};
|
||||
|
||||
} // namespace experimental
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
// Copyright 2021 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__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/wait.h"
|
||||
|
||||
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/create_intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
|
||||
#include "rclcpp/experimental/ros_message_intra_process_buffer.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace experimental
|
||||
{
|
||||
|
||||
template<
|
||||
typename SubscribedType,
|
||||
typename Alloc = std::allocator<SubscribedType>,
|
||||
typename Deleter = std::default_delete<SubscribedType>,
|
||||
/// MessageT::ros_message_type if MessageT is a TypeAdapter,
|
||||
/// otherwise just MessageT.
|
||||
typename ROSMessageType = SubscribedType
|
||||
>
|
||||
class SubscriptionIntraProcessBuffer : public SubscriptionROSMsgIntraProcessBuffer<ROSMessageType,
|
||||
typename allocator::AllocRebind<ROSMessageType, Alloc>::allocator_type,
|
||||
allocator::Deleter<typename allocator::AllocRebind<ROSMessageType, Alloc>::allocator_type,
|
||||
ROSMessageType>>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcessBuffer)
|
||||
|
||||
using SubscribedTypeAllocatorTraits = allocator::AllocRebind<SubscribedType, Alloc>;
|
||||
using SubscribedTypeAllocator = typename SubscribedTypeAllocatorTraits::allocator_type;
|
||||
using SubscribedTypeDeleter = allocator::Deleter<SubscribedTypeAllocator, SubscribedType>;
|
||||
|
||||
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, Alloc>;
|
||||
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
|
||||
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
|
||||
|
||||
using ConstMessageSharedPtr = std::shared_ptr<const ROSMessageType>;
|
||||
using MessageUniquePtr = std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>;
|
||||
|
||||
using ConstDataSharedPtr = std::shared_ptr<const SubscribedType>;
|
||||
using SubscribedTypeUniquePtr = std::unique_ptr<SubscribedType, SubscribedTypeDeleter>;
|
||||
|
||||
using BufferUniquePtr = typename rclcpp::experimental::buffers::IntraProcessBuffer<
|
||||
SubscribedType,
|
||||
Alloc,
|
||||
SubscribedTypeDeleter
|
||||
>::UniquePtr;
|
||||
|
||||
SubscriptionIntraProcessBuffer(
|
||||
std::shared_ptr<Alloc> allocator,
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos_profile,
|
||||
rclcpp::IntraProcessBufferType buffer_type)
|
||||
: SubscriptionROSMsgIntraProcessBuffer<ROSMessageType, ROSMessageTypeAllocator,
|
||||
ROSMessageTypeDeleter>(
|
||||
context, topic_name, qos_profile),
|
||||
subscribed_type_allocator_(*allocator)
|
||||
{
|
||||
allocator::set_allocator_for_deleter(&subscribed_type_deleter_, &subscribed_type_allocator_);
|
||||
|
||||
// Create the intra-process buffer.
|
||||
buffer_ = rclcpp::experimental::create_intra_process_buffer<SubscribedType, Alloc,
|
||||
SubscribedTypeDeleter>(
|
||||
buffer_type,
|
||||
qos_profile,
|
||||
std::make_shared<Alloc>(subscribed_type_allocator_));
|
||||
}
|
||||
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set) override
|
||||
{
|
||||
(void) wait_set;
|
||||
return buffer_->has_data();
|
||||
}
|
||||
|
||||
SubscribedTypeUniquePtr
|
||||
convert_ros_message_to_subscribed_type_unique_ptr(const ROSMessageType & msg)
|
||||
{
|
||||
if constexpr (!std::is_same<SubscribedType, ROSMessageType>::value) {
|
||||
auto ptr = SubscribedTypeAllocatorTraits::allocate(subscribed_type_allocator_, 1);
|
||||
SubscribedTypeAllocatorTraits::construct(subscribed_type_allocator_, ptr);
|
||||
rclcpp::TypeAdapter<SubscribedType, ROSMessageType>::convert_to_custom(msg, *ptr);
|
||||
return SubscribedTypeUniquePtr(ptr, subscribed_type_deleter_);
|
||||
} else {
|
||||
throw std::runtime_error(
|
||||
"convert_ros_message_to_subscribed_type_unique_ptr "
|
||||
"unexpectedly called without TypeAdapter");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
provide_intra_process_message(ConstMessageSharedPtr message) override
|
||||
{
|
||||
if constexpr (std::is_same<SubscribedType, ROSMessageType>::value) {
|
||||
buffer_->add_shared(std::move(message));
|
||||
trigger_guard_condition();
|
||||
} else {
|
||||
buffer_->add_shared(convert_ros_message_to_subscribed_type_unique_ptr(*message));
|
||||
trigger_guard_condition();
|
||||
}
|
||||
this->invoke_on_new_message();
|
||||
}
|
||||
|
||||
void
|
||||
provide_intra_process_message(MessageUniquePtr message) override
|
||||
{
|
||||
if constexpr (std::is_same<SubscribedType, ROSMessageType>::value) {
|
||||
buffer_->add_unique(std::move(message));
|
||||
trigger_guard_condition();
|
||||
} else {
|
||||
buffer_->add_unique(convert_ros_message_to_subscribed_type_unique_ptr(*message));
|
||||
trigger_guard_condition();
|
||||
}
|
||||
this->invoke_on_new_message();
|
||||
}
|
||||
|
||||
void
|
||||
provide_intra_process_data(ConstDataSharedPtr message)
|
||||
{
|
||||
buffer_->add_shared(std::move(message));
|
||||
trigger_guard_condition();
|
||||
this->invoke_on_new_message();
|
||||
}
|
||||
|
||||
void
|
||||
provide_intra_process_data(SubscribedTypeUniquePtr message)
|
||||
{
|
||||
buffer_->add_unique(std::move(message));
|
||||
trigger_guard_condition();
|
||||
this->invoke_on_new_message();
|
||||
}
|
||||
|
||||
bool
|
||||
use_take_shared_method() const override
|
||||
{
|
||||
return buffer_->use_take_shared_method();
|
||||
}
|
||||
|
||||
protected:
|
||||
void
|
||||
trigger_guard_condition() override
|
||||
{
|
||||
this->gc_.trigger();
|
||||
}
|
||||
|
||||
BufferUniquePtr buffer_;
|
||||
SubscribedTypeAllocator subscribed_type_allocator_;
|
||||
SubscribedTypeDeleter subscribed_type_deleter_;
|
||||
};
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
|
||||
@@ -80,10 +80,12 @@ struct function_traits<ReturnTypeT (*)(Args ...)>: function_traits<ReturnTypeT(A
|
||||
|
||||
// std::bind for object methods
|
||||
template<typename ClassT, typename ReturnTypeT, typename ... Args, typename ... FArgs>
|
||||
#if defined _LIBCPP_VERSION // libc++ (Clang)
|
||||
#if defined DOXYGEN_ONLY
|
||||
struct function_traits<std::bind<ReturnTypeT (ClassT::*)(Args ...), FArgs ...>>
|
||||
#elif defined _LIBCPP_VERSION // libc++ (Clang)
|
||||
struct function_traits<std::__bind<ReturnTypeT (ClassT::*)(Args ...), FArgs ...>>
|
||||
#elif defined _GLIBCXX_RELEASE // glibc++ (GNU C++ >= 7.1)
|
||||
struct function_traits<std::_Bind<ReturnTypeT(ClassT::*(FArgs ...))(Args ...)>>
|
||||
struct function_traits<std::_Bind<ReturnTypeT(ClassT::* (FArgs ...))(Args ...)>>
|
||||
#elif defined __GLIBCXX__ // glibc++ (GNU C++)
|
||||
struct function_traits<std::_Bind<std::_Mem_fn<ReturnTypeT (ClassT::*)(Args ...)>(FArgs ...)>>
|
||||
#elif defined _MSC_VER // MS Visual Studio
|
||||
@@ -97,10 +99,12 @@ struct function_traits<
|
||||
|
||||
// std::bind for object const methods
|
||||
template<typename ClassT, typename ReturnTypeT, typename ... Args, typename ... FArgs>
|
||||
#if defined _LIBCPP_VERSION // libc++ (Clang)
|
||||
#if defined DOXYGEN_ONLY
|
||||
struct function_traits<std::bind<ReturnTypeT (ClassT::*)(Args ...) const, FArgs ...>>
|
||||
#elif 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>>
|
||||
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
|
||||
@@ -114,7 +118,9 @@ struct function_traits<
|
||||
|
||||
// std::bind for free functions
|
||||
template<typename ReturnTypeT, typename ... Args, typename ... FArgs>
|
||||
#if defined _LIBCPP_VERSION // libc++ (Clang)
|
||||
#if defined DOXYGEN_ONLY
|
||||
struct function_traits<std::bind<ReturnTypeT( &)(Args ...), FArgs ...>>
|
||||
#elif defined _LIBCPP_VERSION // libc++ (Clang)
|
||||
struct function_traits<std::__bind<ReturnTypeT( &)(Args ...), FArgs ...>>
|
||||
#elif defined __GLIBCXX__ // glibc++ (GNU C++)
|
||||
struct function_traits<std::_Bind<ReturnTypeT(*(FArgs ...))(Args ...)>>
|
||||
@@ -162,6 +168,32 @@ struct same_arguments : std::is_same<
|
||||
>
|
||||
{};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename ReturnTypeT, typename ... Args>
|
||||
struct as_std_function_helper;
|
||||
|
||||
template<typename ReturnTypeT, typename ... Args>
|
||||
struct as_std_function_helper<ReturnTypeT, std::tuple<Args ...>>
|
||||
{
|
||||
using type = std::function<ReturnTypeT(Args ...)>;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<
|
||||
typename FunctorT,
|
||||
typename FunctionTraits = function_traits<FunctorT>
|
||||
>
|
||||
struct as_std_function
|
||||
{
|
||||
using type = typename detail::as_std_function_helper<
|
||||
typename FunctionTraits::return_type,
|
||||
typename FunctionTraits::arguments
|
||||
>::type;
|
||||
};
|
||||
|
||||
} // namespace function_traits
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -42,20 +42,6 @@ RCLCPP_PUBLIC
|
||||
std::string
|
||||
to_string(const FutureReturnCode & future_return_code);
|
||||
|
||||
namespace executor
|
||||
{
|
||||
|
||||
using FutureReturnCode [[deprecated("use rclcpp::FutureReturnCode instead")]] = FutureReturnCode;
|
||||
|
||||
[[deprecated("use rclcpp::to_string(const rclcpp::FutureReturnCode &) instead")]]
|
||||
inline
|
||||
std::string
|
||||
to_string(const rclcpp::FutureReturnCode & future_return_code)
|
||||
{
|
||||
return rclcpp::to_string(future_return_code);
|
||||
}
|
||||
|
||||
} // namespace executor
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__FUTURE_RETURN_CODE_HPP_
|
||||
|
||||
143
rclcpp/include/rclcpp/generic_publisher.hpp
Normal file
143
rclcpp/include/rclcpp/generic_publisher.hpp
Normal file
@@ -0,0 +1,143 @@
|
||||
// Copyright 2018, Bosch Software Innovations GmbH.
|
||||
// Copyright 2021, Apex.AI 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__GENERIC_PUBLISHER_HPP_
|
||||
#define RCLCPP__GENERIC_PUBLISHER_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rcpputils/shared_library.hpp"
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/publisher_base.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/serialized_message.hpp"
|
||||
#include "rclcpp/typesupport_helpers.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// %Publisher for serialized messages whose type is not known at compile time.
|
||||
/**
|
||||
* Since the type is not known at compile time, this is not a template, and the dynamic library
|
||||
* containing type support information has to be identified and loaded based on the type name.
|
||||
*
|
||||
* It does not support intra-process handling.
|
||||
*/
|
||||
class GenericPublisher : public rclcpp::PublisherBase
|
||||
{
|
||||
public:
|
||||
// cppcheck-suppress unknownMacro
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(GenericPublisher)
|
||||
|
||||
/// Constructor.
|
||||
/**
|
||||
* In order to properly publish to a topic, this publisher needs to be added to
|
||||
* the node_topic_interface of the node passed into this constructor.
|
||||
*
|
||||
* \sa rclcpp::Node::create_generic_publisher() or rclcpp::create_generic_publisher() for
|
||||
* creating an instance of this class and adding it to the node_topic_interface.
|
||||
*
|
||||
* \param node_base Pointer to parent node's NodeBaseInterface
|
||||
* \param ts_lib Type support library, needs to correspond to topic_type
|
||||
* \param topic_name Topic name
|
||||
* \param topic_type Topic type
|
||||
* \param qos %QoS settings
|
||||
* \param options %Publisher options.
|
||||
* Not all publisher options are currently respected, the only relevant options for this
|
||||
* publisher are `event_callbacks`, `use_default_callbacks`, and `%callback_group`.
|
||||
*/
|
||||
template<typename AllocatorT = std::allocator<void>>
|
||||
GenericPublisher(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
std::shared_ptr<rcpputils::SharedLibrary> ts_lib,
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
|
||||
: rclcpp::PublisherBase(
|
||||
node_base,
|
||||
topic_name,
|
||||
*rclcpp::get_typesupport_handle(topic_type, "rosidl_typesupport_cpp", *ts_lib),
|
||||
options.template to_rcl_publisher_options<rclcpp::SerializedMessage>(qos)),
|
||||
ts_lib_(ts_lib)
|
||||
{
|
||||
// This is unfortunately duplicated with the code in publisher.hpp.
|
||||
// TODO(nnmm): Deduplicate by moving this into PublisherBase.
|
||||
if (options.event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.deadline_callback,
|
||||
RCL_PUBLISHER_OFFERED_DEADLINE_MISSED);
|
||||
}
|
||||
if (options.event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.liveliness_callback,
|
||||
RCL_PUBLISHER_LIVELINESS_LOST);
|
||||
}
|
||||
if (options.event_callbacks.incompatible_qos_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.incompatible_qos_callback,
|
||||
RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
|
||||
} else if (options.use_default_callbacks) {
|
||||
// Register default callback when not specified
|
||||
try {
|
||||
this->add_event_handler(
|
||||
[this](QOSOfferedIncompatibleQoSInfo & info) {
|
||||
this->default_incompatible_qos_callback(info);
|
||||
},
|
||||
RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
|
||||
} catch (UnsupportedEventTypeException & /*exc*/) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~GenericPublisher() = default;
|
||||
|
||||
/// Publish a rclcpp::SerializedMessage.
|
||||
RCLCPP_PUBLIC
|
||||
void publish(const rclcpp::SerializedMessage & message);
|
||||
|
||||
/**
|
||||
* Publish a rclcpp::SerializedMessage via loaned message after de-serialization.
|
||||
*
|
||||
* \param message a serialized message
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can show
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void publish_as_loaned_msg(const rclcpp::SerializedMessage & message);
|
||||
|
||||
private:
|
||||
// The type support library should stay loaded, so it is stored in the GenericPublisher
|
||||
std::shared_ptr<rcpputils::SharedLibrary> ts_lib_;
|
||||
|
||||
void * borrow_loaned_message();
|
||||
void deserialize_message(
|
||||
const rmw_serialized_message_t & serialized_message,
|
||||
void * deserialized_msg);
|
||||
void publish_loaned_message(void * loaned_message);
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__GENERIC_PUBLISHER_HPP_
|
||||
168
rclcpp/include/rclcpp/generic_subscription.hpp
Normal file
168
rclcpp/include/rclcpp/generic_subscription.hpp
Normal file
@@ -0,0 +1,168 @@
|
||||
// Copyright 2018, Bosch Software Innovations GmbH.
|
||||
// Copyright 2021, Apex.AI 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__GENERIC_SUBSCRIPTION_HPP_
|
||||
#define RCLCPP__GENERIC_SUBSCRIPTION_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rcpputils/shared_library.hpp"
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/serialized_message.hpp"
|
||||
#include "rclcpp/subscription_base.hpp"
|
||||
#include "rclcpp/typesupport_helpers.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// %Subscription for serialized messages whose type is not known at compile time.
|
||||
/**
|
||||
* Since the type is not known at compile time, this is not a template, and the dynamic library
|
||||
* containing type support information has to be identified and loaded based on the type name.
|
||||
*
|
||||
* It does not support intra-process handling.
|
||||
*/
|
||||
class GenericSubscription : public rclcpp::SubscriptionBase
|
||||
{
|
||||
public:
|
||||
// cppcheck-suppress unknownMacro
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(GenericSubscription)
|
||||
|
||||
/// Constructor.
|
||||
/**
|
||||
* In order to properly subscribe to a topic, this subscription needs to be added to
|
||||
* the node_topic_interface of the node passed into this constructor.
|
||||
*
|
||||
* \sa rclcpp::Node::create_generic_subscription() or rclcpp::create_generic_subscription() for
|
||||
* creating an instance of this class and adding it to the node_topic_interface.
|
||||
*
|
||||
* \param node_base Pointer to parent node's NodeBaseInterface
|
||||
* \param ts_lib Type support library, needs to correspond to topic_type
|
||||
* \param topic_name Topic name
|
||||
* \param topic_type Topic type
|
||||
* \param qos %QoS settings
|
||||
* \param callback Callback for new messages of serialized form
|
||||
* \param options %Subscription options.
|
||||
* Not all subscription options are currently respected, the only relevant options for this
|
||||
* subscription are `event_callbacks`, `use_default_callbacks`, `ignore_local_publications`, and
|
||||
* `%callback_group`.
|
||||
*/
|
||||
template<typename AllocatorT = std::allocator<void>>
|
||||
GenericSubscription(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
const std::shared_ptr<rcpputils::SharedLibrary> ts_lib,
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
// TODO(nnmm): Add variant for callback with message info. See issue #1604.
|
||||
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options)
|
||||
: SubscriptionBase(
|
||||
node_base,
|
||||
*rclcpp::get_typesupport_handle(topic_type, "rosidl_typesupport_cpp", *ts_lib),
|
||||
topic_name,
|
||||
options.template to_rcl_subscription_options<rclcpp::SerializedMessage>(qos),
|
||||
true),
|
||||
callback_(callback),
|
||||
ts_lib_(ts_lib)
|
||||
{
|
||||
// This is unfortunately duplicated with the code in subscription.hpp.
|
||||
// TODO(nnmm): Deduplicate by moving this into SubscriptionBase.
|
||||
if (options.event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.deadline_callback,
|
||||
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
|
||||
}
|
||||
if (options.event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.liveliness_callback,
|
||||
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
|
||||
}
|
||||
if (options.event_callbacks.incompatible_qos_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.incompatible_qos_callback,
|
||||
RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
|
||||
} else if (options.use_default_callbacks) {
|
||||
// Register default callback when not specified
|
||||
try {
|
||||
this->add_event_handler(
|
||||
[this](QOSRequestedIncompatibleQoSInfo & info) {
|
||||
this->default_incompatible_qos_callback(info);
|
||||
},
|
||||
RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
|
||||
} catch (UnsupportedEventTypeException & /*exc*/) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
if (options.event_callbacks.message_lost_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.message_lost_callback,
|
||||
RCL_SUBSCRIPTION_MESSAGE_LOST);
|
||||
}
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~GenericSubscription() = default;
|
||||
|
||||
// Same as create_serialized_message() as the subscription is to serialized_messages only
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<void> create_message() override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<rclcpp::SerializedMessage> create_serialized_message() override;
|
||||
|
||||
/// Cast the message to a rclcpp::SerializedMessage and call the callback.
|
||||
RCLCPP_PUBLIC
|
||||
void handle_message(
|
||||
std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info) override;
|
||||
|
||||
/// Handle dispatching rclcpp::SerializedMessage to user callback.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
|
||||
const rclcpp::MessageInfo & message_info) override;
|
||||
|
||||
/// This function is currently not implemented.
|
||||
RCLCPP_PUBLIC
|
||||
void handle_loaned_message(
|
||||
void * loaned_message, const rclcpp::MessageInfo & message_info) override;
|
||||
|
||||
// Same as return_serialized_message() as the subscription is to serialized_messages only
|
||||
RCLCPP_PUBLIC
|
||||
void return_message(std::shared_ptr<void> & message) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> & message) override;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(GenericSubscription)
|
||||
|
||||
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback_;
|
||||
// The type support library should stay loaded, so it is stored in the GenericSubscription
|
||||
std::shared_ptr<rcpputils::SharedLibrary> ts_lib_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__GENERIC_SUBSCRIPTION_HPP_
|
||||
98
rclcpp/include/rclcpp/get_message_type_support_handle.hpp
Normal file
98
rclcpp/include/rclcpp/get_message_type_support_handle.hpp
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright 2021 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__GET_MESSAGE_TYPE_SUPPORT_HANDLE_HPP_
|
||||
#define RCLCPP__GET_MESSAGE_TYPE_SUPPORT_HANDLE_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "rosidl_runtime_cpp/traits.hpp"
|
||||
#include "rosidl_runtime_cpp/message_type_support_decl.hpp"
|
||||
#include "rosidl_typesupport_cpp/message_type_support.hpp"
|
||||
|
||||
#include "rclcpp/type_adapter.hpp"
|
||||
|
||||
/// Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
#ifdef DOXYGEN_ONLY
|
||||
|
||||
/// Returns the message type support for the given `MessageT` type.
|
||||
/**
|
||||
* \tparam MessageT an actual ROS message type or an adapted type using `rclcpp::TypeAdapter`
|
||||
*/
|
||||
template<typename MessageT>
|
||||
constexpr const rosidl_message_type_support_t & get_message_type_support_handle();
|
||||
|
||||
#else
|
||||
|
||||
template<typename MessageT>
|
||||
constexpr
|
||||
typename std::enable_if_t<
|
||||
rosidl_generator_traits::is_message<MessageT>::value,
|
||||
const rosidl_message_type_support_t &
|
||||
>
|
||||
get_message_type_support_handle()
|
||||
{
|
||||
auto handle = rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>();
|
||||
if (!handle) {
|
||||
throw std::runtime_error("Type support handle unexpectedly nullptr");
|
||||
}
|
||||
return *handle;
|
||||
}
|
||||
|
||||
template<typename AdaptedType>
|
||||
constexpr
|
||||
typename std::enable_if_t<
|
||||
!rosidl_generator_traits::is_message<AdaptedType>::value &&
|
||||
rclcpp::TypeAdapter<AdaptedType>::is_specialized::value,
|
||||
const rosidl_message_type_support_t &
|
||||
>
|
||||
get_message_type_support_handle()
|
||||
{
|
||||
auto handle = rosidl_typesupport_cpp::get_message_type_support_handle<
|
||||
typename TypeAdapter<AdaptedType>::ros_message_type
|
||||
>();
|
||||
if (!handle) {
|
||||
throw std::runtime_error("Type support handle unexpectedly nullptr");
|
||||
}
|
||||
return *handle;
|
||||
}
|
||||
|
||||
// This specialization is a pass through runtime check, which allows a better
|
||||
// static_assert to catch this issue further down the line.
|
||||
// This should never get to be called in practice, and is purely defensive.
|
||||
template<
|
||||
typename AdaptedType
|
||||
>
|
||||
constexpr
|
||||
typename std::enable_if_t<
|
||||
!rosidl_generator_traits::is_message<AdaptedType>::value &&
|
||||
!TypeAdapter<AdaptedType>::is_specialized::value,
|
||||
const rosidl_message_type_support_t &
|
||||
>
|
||||
get_message_type_support_handle()
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"this specialization of rclcpp::get_message_type_support_handle() "
|
||||
"should never be called");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__GET_MESSAGE_TYPE_SUPPORT_HANDLE_HPP_
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/wait.h"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
@@ -63,7 +64,7 @@ class GraphListener : public std::enable_shared_from_this<GraphListener>
|
||||
{
|
||||
public:
|
||||
RCLCPP_PUBLIC
|
||||
explicit GraphListener(std::shared_ptr<rclcpp::Context> parent_context);
|
||||
explicit GraphListener(const rclcpp::Context::SharedPtr & parent_context);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~GraphListener();
|
||||
@@ -160,14 +161,23 @@ protected:
|
||||
void
|
||||
run_loop();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init_wait_set();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
cleanup_wait_set();
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(GraphListener)
|
||||
|
||||
/** \internal */
|
||||
void
|
||||
__shutdown(bool);
|
||||
__shutdown();
|
||||
|
||||
rclcpp::Context::WeakPtr parent_context_;
|
||||
std::weak_ptr<rclcpp::Context> weak_parent_context_;
|
||||
std::shared_ptr<rcl_context_t> rcl_parent_context_;
|
||||
|
||||
std::thread listener_thread_;
|
||||
bool is_started_;
|
||||
@@ -178,8 +188,7 @@ private:
|
||||
mutable std::mutex node_graph_interfaces_mutex_;
|
||||
std::vector<rclcpp::node_interfaces::NodeGraphInterface *> node_graph_interfaces_;
|
||||
|
||||
rcl_guard_condition_t interrupt_guard_condition_ = rcl_get_zero_initialized_guard_condition();
|
||||
rcl_guard_condition_t * shutdown_guard_condition_;
|
||||
rclcpp::GuardCondition interrupt_guard_condition_;
|
||||
rcl_wait_set_t wait_set_ = rcl_get_zero_initialized_wait_set();
|
||||
};
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ public:
|
||||
* Defaults to using the global default context singleton.
|
||||
* Shared ownership of the context is held with the guard condition until
|
||||
* destruction.
|
||||
* \param[in] guard_condition_options Optional guard condition options to be used.
|
||||
* Defaults to using the default guard condition options.
|
||||
* \throws std::invalid_argument if the context is nullptr.
|
||||
* \throws rclcpp::exceptions::RCLError based exceptions when underlying
|
||||
* rcl functions fail.
|
||||
@@ -47,7 +49,9 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
explicit GuardCondition(
|
||||
rclcpp::Context::SharedPtr context =
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
rclcpp::contexts::get_global_default_context(),
|
||||
rcl_guard_condition_options_t guard_condition_options =
|
||||
rcl_guard_condition_get_default_options());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
@@ -58,6 +62,11 @@ public:
|
||||
rclcpp::Context::SharedPtr
|
||||
get_context() const;
|
||||
|
||||
/// Return the underlying rcl guard condition structure.
|
||||
RCLCPP_PUBLIC
|
||||
rcl_guard_condition_t &
|
||||
get_rcl_guard_condition();
|
||||
|
||||
/// Return the underlying rcl guard condition structure.
|
||||
RCLCPP_PUBLIC
|
||||
const rcl_guard_condition_t &
|
||||
@@ -89,10 +98,27 @@ public:
|
||||
bool
|
||||
exchange_in_use_by_wait_set_state(bool in_use_state);
|
||||
|
||||
/// Adds the guard condition to a waitset
|
||||
/**
|
||||
* This function is thread-safe.
|
||||
* \param[in] wait_set pointer to a wait set where to add the guard condition
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_on_trigger_callback(std::function<void(size_t)> callback);
|
||||
|
||||
protected:
|
||||
rclcpp::Context::SharedPtr context_;
|
||||
rcl_guard_condition_t rcl_guard_condition_;
|
||||
std::atomic<bool> in_use_by_wait_set_{false};
|
||||
std::recursive_mutex reentrant_mutex_;
|
||||
std::function<void(size_t)> on_trigger_callback_{nullptr};
|
||||
size_t unread_count_{0};
|
||||
rcl_wait_set_t * wait_set_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define RCLCPP__INIT_OPTIONS_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "rcl/init_options.h"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
@@ -28,7 +29,7 @@ class InitOptions
|
||||
{
|
||||
public:
|
||||
/// If true, the context will be shutdown on SIGINT by the signal handler (if it was installed).
|
||||
bool shutdown_on_sigint = true;
|
||||
bool shutdown_on_signal = true;
|
||||
|
||||
/// Constructor
|
||||
/**
|
||||
@@ -80,11 +81,30 @@ public:
|
||||
const rcl_init_options_t *
|
||||
get_rcl_init_options() const;
|
||||
|
||||
/// Retrieve default domain id and set.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
use_default_domain_id();
|
||||
|
||||
/// Set the domain id.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_domain_id(size_t domain_id);
|
||||
|
||||
/// Return domain id.
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_domain_id() const;
|
||||
|
||||
protected:
|
||||
void
|
||||
finalize_init_options();
|
||||
|
||||
private:
|
||||
void
|
||||
finalize_init_options_impl();
|
||||
|
||||
mutable std::mutex init_options_mutex_;
|
||||
std::unique_ptr<rcl_init_options_t> init_options_;
|
||||
bool initialize_logging_{true};
|
||||
};
|
||||
|
||||
35
rclcpp/include/rclcpp/is_ros_compatible_type.hpp
Normal file
35
rclcpp/include/rclcpp/is_ros_compatible_type.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2021 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__IS_ROS_COMPATIBLE_TYPE_HPP_
|
||||
#define RCLCPP__IS_ROS_COMPATIBLE_TYPE_HPP_
|
||||
|
||||
#include "rosidl_runtime_cpp/traits.hpp"
|
||||
|
||||
#include "rclcpp/type_adapter.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
struct is_ros_compatible_type
|
||||
{
|
||||
static constexpr bool value =
|
||||
rosidl_generator_traits::is_message<T>::value ||
|
||||
rclcpp::TypeAdapter<T>::is_specialized::value;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__IS_ROS_COMPATIBLE_TYPE_HPP_
|
||||
@@ -103,6 +103,9 @@ public:
|
||||
* \param[in] allocator Allocator instance in case middleware can not allocate messages
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
*/
|
||||
[[
|
||||
deprecated("used the LoanedMessage constructor that does not use a shared_ptr to the allocator")
|
||||
]]
|
||||
LoanedMessage(
|
||||
const rclcpp::PublisherBase * pub,
|
||||
std::shared_ptr<std::allocator<MessageT>> allocator)
|
||||
@@ -114,7 +117,9 @@ public:
|
||||
: pub_(std::move(other.pub_)),
|
||||
message_(std::move(other.message_)),
|
||||
message_allocator_(std::move(other.message_allocator_))
|
||||
{}
|
||||
{
|
||||
other.message_ = nullptr;
|
||||
}
|
||||
|
||||
/// Destructor of the LoanedMessage class.
|
||||
/**
|
||||
@@ -183,14 +188,30 @@ public:
|
||||
/**
|
||||
* A call to `release()` will unmanage the memory for the ROS message.
|
||||
* That means that the destructor of this class will not free the memory on scope exit.
|
||||
* If the message is loaned from the middleware but not be published, the user needs to call
|
||||
* `rcl_return_loaned_message_from_publisher` manually.
|
||||
* If the memory is from the local allocator, the memory is freed when the unique pointer
|
||||
* goes out instead.
|
||||
*
|
||||
* \return Raw pointer to the message instance.
|
||||
* \return std::unique_ptr to the message instance.
|
||||
*/
|
||||
MessageT * release()
|
||||
std::unique_ptr<MessageT, std::function<void(MessageT *)>>
|
||||
release()
|
||||
{
|
||||
auto msg = message_;
|
||||
message_ = nullptr;
|
||||
return msg;
|
||||
|
||||
if (pub_.can_loan_messages()) {
|
||||
return std::unique_ptr<MessageT, std::function<void(MessageT *)>>(msg, [](MessageT *) {});
|
||||
}
|
||||
|
||||
return std::unique_ptr<MessageT, std::function<void(MessageT *)>>(
|
||||
msg,
|
||||
[allocator = message_allocator_](MessageT * msg_ptr) mutable {
|
||||
// call destructor before deallocating
|
||||
msg_ptr->~MessageT();
|
||||
allocator.deallocate(msg_ptr, 1);
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "rcl/node.h"
|
||||
#include "rcutils/logging.h"
|
||||
#include "rcpputils/filesystem_helper.hpp"
|
||||
|
||||
/**
|
||||
* \def RCLCPP_LOGGING_ENABLED
|
||||
@@ -74,8 +76,32 @@ RCLCPP_PUBLIC
|
||||
Logger
|
||||
get_node_logger(const rcl_node_t * node);
|
||||
|
||||
/// Get the current logging directory.
|
||||
/**
|
||||
* For more details of how the logging directory is determined,
|
||||
* see rcl_logging_get_logging_directory().
|
||||
*
|
||||
* \returns the logging directory being used.
|
||||
* \throws rclcpp::exceptions::RCLError if an unexpected error occurs.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rcpputils::fs::path
|
||||
get_logging_directory();
|
||||
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
/// An enum for the type of logger level.
|
||||
enum class Level
|
||||
{
|
||||
Unset = RCUTILS_LOG_SEVERITY_UNSET, ///< The unset log level
|
||||
Debug = RCUTILS_LOG_SEVERITY_DEBUG, ///< The debug log level
|
||||
Info = RCUTILS_LOG_SEVERITY_INFO, ///< The info log level
|
||||
Warn = RCUTILS_LOG_SEVERITY_WARN, ///< The warn log level
|
||||
Error = RCUTILS_LOG_SEVERITY_ERROR, ///< The error log level
|
||||
Fatal = RCUTILS_LOG_SEVERITY_FATAL, ///< The fatal log level
|
||||
};
|
||||
|
||||
private:
|
||||
friend Logger rclcpp::get_logger(const std::string & name);
|
||||
friend ::rclcpp::node_interfaces::NodeLogging;
|
||||
@@ -138,6 +164,16 @@ public:
|
||||
}
|
||||
return Logger(*name_ + "." + suffix);
|
||||
}
|
||||
|
||||
/// Set level for current logger.
|
||||
/**
|
||||
* \param[in] level the logger's level
|
||||
* \throws rclcpp::exceptions::RCLInvalidArgument if level is invalid.
|
||||
* \throws rclcpp::exceptions::RCLError if other error happens.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_level(Level level);
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define RCLCPP__MEMORY_STRATEGY_HPP_
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "rcl/allocator.h"
|
||||
@@ -42,11 +43,13 @@ class RCLCPP_PUBLIC MemoryStrategy
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(MemoryStrategy)
|
||||
using WeakNodeList = std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>;
|
||||
using WeakCallbackGroupsToNodesMap = std::map<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
|
||||
std::owner_less<rclcpp::CallbackGroup::WeakPtr>>;
|
||||
|
||||
virtual ~MemoryStrategy() = default;
|
||||
|
||||
virtual bool collect_entities(const WeakNodeList & weak_nodes) = 0;
|
||||
virtual bool collect_entities(const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) = 0;
|
||||
|
||||
virtual size_t number_of_ready_subscriptions() const = 0;
|
||||
virtual size_t number_of_ready_services() const = 0;
|
||||
@@ -61,87 +64,89 @@ public:
|
||||
virtual void clear_handles() = 0;
|
||||
virtual void remove_null_handles(rcl_wait_set_t * wait_set) = 0;
|
||||
|
||||
virtual void add_guard_condition(const rcl_guard_condition_t * guard_condition) = 0;
|
||||
virtual void
|
||||
add_guard_condition(const rclcpp::GuardCondition & guard_condition) = 0;
|
||||
|
||||
virtual void remove_guard_condition(const rcl_guard_condition_t * guard_condition) = 0;
|
||||
virtual void
|
||||
remove_guard_condition(const rclcpp::GuardCondition * guard_condition) = 0;
|
||||
|
||||
virtual void
|
||||
get_next_subscription(
|
||||
rclcpp::AnyExecutable & any_exec,
|
||||
const WeakNodeList & weak_nodes) = 0;
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) = 0;
|
||||
|
||||
virtual void
|
||||
get_next_service(
|
||||
rclcpp::AnyExecutable & any_exec,
|
||||
const WeakNodeList & weak_nodes) = 0;
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) = 0;
|
||||
|
||||
virtual void
|
||||
get_next_client(
|
||||
rclcpp::AnyExecutable & any_exec,
|
||||
const WeakNodeList & weak_nodes) = 0;
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) = 0;
|
||||
|
||||
virtual void
|
||||
get_next_timer(
|
||||
rclcpp::AnyExecutable & any_exec,
|
||||
const WeakNodeList & weak_nodes) = 0;
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) = 0;
|
||||
|
||||
virtual void
|
||||
get_next_waitable(
|
||||
rclcpp::AnyExecutable & any_exec,
|
||||
const WeakNodeList & weak_nodes) = 0;
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) = 0;
|
||||
|
||||
virtual rcl_allocator_t
|
||||
get_allocator() = 0;
|
||||
|
||||
static rclcpp::SubscriptionBase::SharedPtr
|
||||
get_subscription_by_handle(
|
||||
std::shared_ptr<const rcl_subscription_t> subscriber_handle,
|
||||
const WeakNodeList & weak_nodes);
|
||||
const std::shared_ptr<const rcl_subscription_t> & subscriber_handle,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
static rclcpp::ServiceBase::SharedPtr
|
||||
get_service_by_handle(
|
||||
std::shared_ptr<const rcl_service_t> service_handle,
|
||||
const WeakNodeList & weak_nodes);
|
||||
const std::shared_ptr<const rcl_service_t> & service_handle,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
static rclcpp::ClientBase::SharedPtr
|
||||
get_client_by_handle(
|
||||
std::shared_ptr<const rcl_client_t> client_handle,
|
||||
const WeakNodeList & weak_nodes);
|
||||
const std::shared_ptr<const rcl_client_t> & client_handle,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
static rclcpp::TimerBase::SharedPtr
|
||||
get_timer_by_handle(
|
||||
std::shared_ptr<const rcl_timer_t> timer_handle,
|
||||
const WeakNodeList & weak_nodes);
|
||||
const std::shared_ptr<const rcl_timer_t> & timer_handle,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
static rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
|
||||
get_node_by_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group,
|
||||
const WeakNodeList & weak_nodes);
|
||||
const rclcpp::CallbackGroup::SharedPtr & group,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
static rclcpp::CallbackGroup::SharedPtr
|
||||
get_group_by_subscription(
|
||||
rclcpp::SubscriptionBase::SharedPtr subscription,
|
||||
const WeakNodeList & weak_nodes);
|
||||
const rclcpp::SubscriptionBase::SharedPtr & subscription,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
static rclcpp::CallbackGroup::SharedPtr
|
||||
get_group_by_service(
|
||||
rclcpp::ServiceBase::SharedPtr service,
|
||||
const WeakNodeList & weak_nodes);
|
||||
const rclcpp::ServiceBase::SharedPtr & service,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
static rclcpp::CallbackGroup::SharedPtr
|
||||
get_group_by_client(
|
||||
rclcpp::ClientBase::SharedPtr client,
|
||||
const WeakNodeList & weak_nodes);
|
||||
const rclcpp::ClientBase::SharedPtr & client,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
static rclcpp::CallbackGroup::SharedPtr
|
||||
get_group_by_timer(
|
||||
rclcpp::TimerBase::SharedPtr timer,
|
||||
const WeakNodeList & weak_nodes);
|
||||
const rclcpp::TimerBase::SharedPtr & timer,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
|
||||
static rclcpp::CallbackGroup::SharedPtr
|
||||
get_group_by_waitable(
|
||||
rclcpp::Waitable::SharedPtr waitable,
|
||||
const WeakNodeList & weak_nodes);
|
||||
const rclcpp::Waitable::SharedPtr & waitable,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
|
||||
};
|
||||
|
||||
} // namespace memory_strategy
|
||||
|
||||
115
rclcpp/include/rclcpp/network_flow_endpoint.hpp
Normal file
115
rclcpp/include/rclcpp/network_flow_endpoint.hpp
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright 2020 Ericsson AB
|
||||
//
|
||||
// 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__NETWORK_FLOW_ENDPOINT_HPP_
|
||||
#define RCLCPP__NETWORK_FLOW_ENDPOINT_HPP_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include "rcl/network_flow_endpoints.h"
|
||||
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Forward declaration
|
||||
class NetworkFlowEndpoint;
|
||||
|
||||
/// Check if two NetworkFlowEndpoint instances are equal
|
||||
RCLCPP_PUBLIC
|
||||
bool operator==(const NetworkFlowEndpoint & left, const NetworkFlowEndpoint & right);
|
||||
|
||||
/// Check if two NetworkFlowEndpoint instances are not equal
|
||||
RCLCPP_PUBLIC
|
||||
bool operator!=(const NetworkFlowEndpoint & left, const NetworkFlowEndpoint & right);
|
||||
|
||||
/// Streaming helper for NetworkFlowEndpoint
|
||||
RCLCPP_PUBLIC
|
||||
std::ostream & operator<<(std::ostream & os, const NetworkFlowEndpoint & network_flow_endpoint);
|
||||
|
||||
/**
|
||||
* Class describes a network flow endpoint based on the counterpart definition
|
||||
* in the RMW layer.
|
||||
*/
|
||||
class NetworkFlowEndpoint
|
||||
{
|
||||
public:
|
||||
/// Construct from rcl_network_flow_endpoint_t
|
||||
RCLCPP_PUBLIC
|
||||
explicit NetworkFlowEndpoint(const rcl_network_flow_endpoint_t & network_flow_endpoint)
|
||||
: transport_protocol_(
|
||||
rcl_network_flow_endpoint_get_transport_protocol_string(network_flow_endpoint.
|
||||
transport_protocol)),
|
||||
internet_protocol_(
|
||||
rcl_network_flow_endpoint_get_internet_protocol_string(
|
||||
network_flow_endpoint.internet_protocol)),
|
||||
transport_port_(network_flow_endpoint.transport_port),
|
||||
flow_label_(network_flow_endpoint.flow_label),
|
||||
dscp_(network_flow_endpoint.dscp),
|
||||
internet_address_(network_flow_endpoint.internet_address)
|
||||
{
|
||||
}
|
||||
|
||||
/// Get transport protocol
|
||||
RCLCPP_PUBLIC
|
||||
const std::string & transport_protocol() const;
|
||||
|
||||
/// Get internet protocol
|
||||
RCLCPP_PUBLIC
|
||||
const std::string & internet_protocol() const;
|
||||
|
||||
/// Get transport port
|
||||
RCLCPP_PUBLIC
|
||||
uint16_t transport_port() const;
|
||||
|
||||
/// Get flow label
|
||||
RCLCPP_PUBLIC
|
||||
uint32_t flow_label() const;
|
||||
|
||||
/// Get DSCP
|
||||
RCLCPP_PUBLIC
|
||||
uint8_t dscp() const;
|
||||
|
||||
/// Get internet address
|
||||
RCLCPP_PUBLIC
|
||||
const std::string & internet_address() const;
|
||||
|
||||
/// Compare two NetworkFlowEndpoint instances
|
||||
friend bool rclcpp::operator==(
|
||||
const NetworkFlowEndpoint & left,
|
||||
const NetworkFlowEndpoint & right);
|
||||
friend bool rclcpp::operator!=(
|
||||
const NetworkFlowEndpoint & left,
|
||||
const NetworkFlowEndpoint & right);
|
||||
|
||||
/// Streaming helper
|
||||
friend std::ostream & rclcpp::operator<<(
|
||||
std::ostream & os,
|
||||
const NetworkFlowEndpoint & network_flow_endpoint);
|
||||
|
||||
private:
|
||||
std::string transport_protocol_;
|
||||
std::string internet_protocol_;
|
||||
uint16_t transport_port_;
|
||||
uint32_t flow_label_;
|
||||
uint8_t dscp_;
|
||||
std::string internet_address_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__NETWORK_FLOW_ENDPOINT_HPP_
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -26,6 +27,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rcutils/macros.h"
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/node.h"
|
||||
|
||||
@@ -39,6 +42,8 @@
|
||||
#include "rclcpp/clock.hpp"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/event.hpp"
|
||||
#include "rclcpp/generic_publisher.hpp"
|
||||
#include "rclcpp/generic_subscription.hpp"
|
||||
#include "rclcpp/logger.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/message_memory_strategy.hpp"
|
||||
@@ -139,12 +144,20 @@ public:
|
||||
/// Create and return a callback group.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::CallbackGroup::SharedPtr
|
||||
create_callback_group(rclcpp::CallbackGroupType group_type);
|
||||
create_callback_group(
|
||||
rclcpp::CallbackGroupType group_type,
|
||||
bool automatically_add_to_executor_with_node = true);
|
||||
|
||||
/// Return the list of callback groups in the node.
|
||||
/// Iterate over the callback groups in the node, calling the given function on each valid one.
|
||||
/**
|
||||
* This method is called in a thread-safe way, and also makes sure to only call the given
|
||||
* function on those items that are still valid.
|
||||
*
|
||||
* \param[in] func The callback function to call on each valid callback group.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<rclcpp::CallbackGroup::WeakPtr> &
|
||||
get_callback_groups() const;
|
||||
void
|
||||
for_each_callback_group(const node_interfaces::NodeBaseInterface::CallbackGroupFunction & func);
|
||||
|
||||
/// Create and return a Publisher.
|
||||
/**
|
||||
@@ -159,7 +172,7 @@ public:
|
||||
* 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().volatile());
|
||||
* 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);
|
||||
@@ -199,13 +212,8 @@ public:
|
||||
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 MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
AllocatorT
|
||||
>
|
||||
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType
|
||||
>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
@@ -235,7 +243,7 @@ public:
|
||||
/// Create and return a Client.
|
||||
/**
|
||||
* \param[in] service_name The topic to service on.
|
||||
* \param[in] rmw_qos_profile_t Quality of service profile for client.
|
||||
* \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.
|
||||
*/
|
||||
@@ -250,7 +258,7 @@ public:
|
||||
/**
|
||||
* \param[in] service_name The topic to service on.
|
||||
* \param[in] callback User-defined callback function.
|
||||
* \param[in] rmw_qos_profile_t Quality of service profile for client.
|
||||
* \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.
|
||||
*/
|
||||
@@ -262,6 +270,55 @@ public:
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_services_default,
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
/// Create and return a GenericPublisher.
|
||||
/**
|
||||
* The returned pointer will never be empty, but this function can throw various exceptions, for
|
||||
* instance when the message's package can not be found on the AMENT_PREFIX_PATH.
|
||||
*
|
||||
* \param[in] topic_name Topic name
|
||||
* \param[in] topic_type Topic type
|
||||
* \param[in] qos %QoS settings
|
||||
* \param options %Publisher options.
|
||||
* Not all publisher options are currently respected, the only relevant options for this
|
||||
* publisher are `event_callbacks`, `use_default_callbacks`, and `%callback_group`.
|
||||
* \return Shared pointer to the created generic publisher.
|
||||
*/
|
||||
template<typename AllocatorT = std::allocator<void>>
|
||||
std::shared_ptr<rclcpp::GenericPublisher> create_generic_publisher(
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::PublisherOptionsWithAllocator<AllocatorT>()
|
||||
)
|
||||
);
|
||||
|
||||
/// Create and return a GenericSubscription.
|
||||
/**
|
||||
* The returned pointer will never be empty, but this function can throw various exceptions, for
|
||||
* instance when the message's package can not be found on the AMENT_PREFIX_PATH.
|
||||
*
|
||||
* \param[in] topic_name Topic name
|
||||
* \param[in] topic_type Topic type
|
||||
* \param[in] qos %QoS settings
|
||||
* \param[in] callback Callback for new messages of serialized form
|
||||
* \param[in] options %Subscription options.
|
||||
* Not all subscription options are currently respected, the only relevant options for this
|
||||
* subscription are `event_callbacks`, `use_default_callbacks`, `ignore_local_publications`, and
|
||||
* `%callback_group`.
|
||||
* \return Shared pointer to the created generic subscription.
|
||||
*/
|
||||
template<typename AllocatorT = std::allocator<void>>
|
||||
std::shared_ptr<rclcpp::GenericSubscription> create_generic_subscription(
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
|
||||
)
|
||||
);
|
||||
|
||||
/// Declare and initialize a parameter, return the effective value.
|
||||
/**
|
||||
* This method is used to declare that a parameter exists on this node.
|
||||
@@ -301,16 +358,43 @@ public:
|
||||
* name is invalid.
|
||||
* \throws rclcpp::exceptions::InvalidParameterValueException if initial
|
||||
* value fails to be set.
|
||||
* \throws rclcpp::exceptions::InvalidParameterTypeException
|
||||
* if the type of the default value or override is wrong.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
const rclcpp::ParameterValue & default_value = rclcpp::ParameterValue(),
|
||||
const rclcpp::ParameterValue & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
bool ignore_override = false);
|
||||
|
||||
/// Declare and initialize a parameter, return the effective value.
|
||||
/**
|
||||
* Same as the previous one, but a default value is not provided and the user
|
||||
* must provide a parameter override of the correct type.
|
||||
*
|
||||
* \param[in] name The name of the parameter.
|
||||
* \param[in] type Desired type of the parameter, which will enforced at runtime.
|
||||
* \param[in] parameter_descriptor An optional, custom description for
|
||||
* the parameter.
|
||||
* \param[in] ignore_override When `true`, the parameter override is ignored.
|
||||
* Default to `false`.
|
||||
* \return A const reference to the value of the parameter.
|
||||
* \throws Same as the previous overload taking a default value.
|
||||
* \throws rclcpp::exceptions::InvalidParameterTypeException
|
||||
* if an override is not provided or the provided override is of the wrong type.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
rclcpp::ParameterType type,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor{},
|
||||
bool ignore_override = false);
|
||||
|
||||
/// Declare and initialize a parameter with a type.
|
||||
/**
|
||||
* See the non-templated declare_parameter() on this class for details.
|
||||
@@ -341,6 +425,18 @@ public:
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
bool ignore_override = false);
|
||||
|
||||
/// Declare and initialize a parameter with a type.
|
||||
/**
|
||||
* See the non-templated declare_parameter() on this class for details.
|
||||
*/
|
||||
template<typename ParameterT>
|
||||
auto
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
bool ignore_override = false);
|
||||
|
||||
/// Declare and initialize several parameters with the same namespace and type.
|
||||
/**
|
||||
* For each key in the map, a parameter with a name of "namespace.key"
|
||||
@@ -608,6 +704,24 @@ public:
|
||||
ParameterT & parameter,
|
||||
const ParameterT & alternative_value) const;
|
||||
|
||||
/// Return the parameter value, or the "alternative_value" if not set.
|
||||
/**
|
||||
* If the parameter was not set, then the "alternative_value" argument is returned.
|
||||
*
|
||||
* This method will not throw the rclcpp::exceptions::ParameterNotDeclaredException exception.
|
||||
*
|
||||
* In all cases, the parameter is never set or declared within the node.
|
||||
*
|
||||
* \param[in] name The name of the parameter to get.
|
||||
* \param[in] alternative_value Value to be stored in output if the parameter was not set.
|
||||
* \returns The value of the parameter.
|
||||
*/
|
||||
template<typename ParameterT>
|
||||
ParameterT
|
||||
get_parameter_or(
|
||||
const std::string & name,
|
||||
const ParameterT & alternative_value) const;
|
||||
|
||||
/// Return the parameters by the given parameter names.
|
||||
/**
|
||||
* Like get_parameters(), this method may throw the
|
||||
@@ -781,6 +895,9 @@ public:
|
||||
*
|
||||
* This allows the node developer to control which parameters may be changed.
|
||||
*
|
||||
* It is considered bad practice to reject changes for "unknown" parameters as this prevents
|
||||
* other parts of the node (that may be aware of these parameters) from handling them.
|
||||
*
|
||||
* Note that the callback is called when declare_parameter() and its variants
|
||||
* are called, and so you cannot assume the parameter has been set before
|
||||
* this callback, so when checking a new value against the existing one, you
|
||||
@@ -812,6 +929,7 @@ public:
|
||||
* \throws std::bad_alloc if the allocation of the OnSetParametersCallbackHandle fails.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
RCUTILS_WARN_UNUSED
|
||||
OnSetParametersCallbackHandle::SharedPtr
|
||||
add_on_set_parameters_callback(OnParametersSetCallbackType callback);
|
||||
|
||||
@@ -842,26 +960,6 @@ public:
|
||||
void
|
||||
remove_on_set_parameters_callback(const OnSetParametersCallbackHandle * const handler);
|
||||
|
||||
/// Register a callback to be called anytime a parameter is about to be changed.
|
||||
/**
|
||||
* \deprecated Use add_on_set_parameters_callback instead.
|
||||
* With this method, only one callback can be set at a time. The callback that was previously
|
||||
* set by this method is returned or `nullptr` if no callback was previously set.
|
||||
*
|
||||
* The callbacks added with `add_on_set_parameters_callback` are stored in a different place.
|
||||
* `remove_on_set_parameters_callback` can't be used with the callbacks registered with this
|
||||
* method. For removing it, use `set_on_parameters_set_callback(nullptr)`.
|
||||
*
|
||||
* \param[in] callback The callback to be called when the value for a
|
||||
* parameter is about to be set.
|
||||
* \return The previous callback that was registered, if there was one,
|
||||
* otherwise nullptr.
|
||||
*/
|
||||
[[deprecated("use add_on_set_parameters_callback(OnParametersSetCallbackType callback) instead")]]
|
||||
RCLCPP_PUBLIC
|
||||
OnParametersSetCallbackType
|
||||
set_on_parameters_set_callback(rclcpp::Node::OnParametersSetCallbackType callback);
|
||||
|
||||
/// Get the fully-qualified names of all available nodes.
|
||||
/**
|
||||
* The fully-qualified name includes the local namespace and name of the node.
|
||||
@@ -889,11 +987,15 @@ public:
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_service_names_and_types() const;
|
||||
|
||||
/// Return the number of publishers that are advertised on a given topic.
|
||||
/// Return a map of existing service names to list of service types for a specific node.
|
||||
/**
|
||||
* \param[in] topic_name the topic_name on which to count the publishers.
|
||||
* \return number of publishers that are advertised on a given topic.
|
||||
* \throws std::runtime_error if publishers could not be counted
|
||||
* This function only considers services - not clients.
|
||||
* The returned names are the actual names used and do not have remap rules applied.
|
||||
*
|
||||
* \param[in] node_name name of the node.
|
||||
* \param[in] namespace_ namespace of the node.
|
||||
* \return a map of existing service names to list of service types.
|
||||
* \throws std::runtime_error anything that rcl_error can throw.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
@@ -901,15 +1003,21 @@ public:
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_) const;
|
||||
|
||||
/// Return the number of publishers created for a given topic.
|
||||
/**
|
||||
* \param[in] topic_name the actual topic name used; it will not be automatically remapped.
|
||||
* \return number of publishers that have been created for the given topic.
|
||||
* \throws std::runtime_error if publishers could not be counted
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
count_publishers(const std::string & topic_name) const;
|
||||
|
||||
/// Return the number of subscribers who have created a subscription for a given topic.
|
||||
/// Return the number of subscribers created for a given topic.
|
||||
/**
|
||||
* \param[in] topic_name the topic_name on which to count the subscribers.
|
||||
* \return number of subscribers who have created a subscription for a given topic.
|
||||
* \throws std::runtime_error if publishers could not be counted
|
||||
* \param[in] topic_name the actual topic name used; it will not be automatically remapped.
|
||||
* \return number of subscribers that have been created for the given topic.
|
||||
* \throws std::runtime_error if subscribers could not be counted
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
@@ -930,7 +1038,7 @@ public:
|
||||
* A relative or private topic will be expanded using this node's namespace and name.
|
||||
* The queried `topic_name` is not remapped.
|
||||
*
|
||||
* \param[in] topic_name the topic_name on which to find the publishers.
|
||||
* \param[in] topic_name the actual topic name used; it will not be automatically remapped.
|
||||
* \param[in] no_mangle if `true`, `topic_name` needs to be a valid middleware topic name,
|
||||
* otherwise it should be a valid ROS topic name. Defaults to `false`.
|
||||
* \return a list of TopicEndpointInfo representing all the publishers on this topic.
|
||||
@@ -956,7 +1064,7 @@ public:
|
||||
* A relative or private topic will be expanded using this node's namespace and name.
|
||||
* The queried `topic_name` is not remapped.
|
||||
*
|
||||
* \param[in] topic_name the topic_name on which to find the subscriptions.
|
||||
* \param[in] topic_name the actual topic name used; it will not be automatically remapped.
|
||||
* \param[in] no_mangle if `true`, `topic_name` needs to be a valid middleware topic name,
|
||||
* otherwise it should be a valid ROS topic name. Defaults to `false`.
|
||||
* \return a list of TopicEndpointInfo representing all the subscriptions on this topic.
|
||||
@@ -1062,7 +1170,7 @@ public:
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr
|
||||
get_node_parameters_interface();
|
||||
|
||||
/// Return the Node's internal NodeParametersInterface implementation.
|
||||
/// Return the Node's internal NodeTimeSourceInterface implementation.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr
|
||||
get_node_time_source_interface();
|
||||
@@ -1190,10 +1298,6 @@ protected:
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Node)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
group_in_node(CallbackGroup::SharedPtr group);
|
||||
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_;
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_;
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_;
|
||||
|
||||
@@ -36,10 +36,12 @@
|
||||
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/create_client.hpp"
|
||||
#include "rclcpp/create_generic_publisher.hpp"
|
||||
#include "rclcpp/create_generic_subscription.hpp"
|
||||
#include "rclcpp/create_publisher.hpp"
|
||||
#include "rclcpp/create_service.hpp"
|
||||
#include "rclcpp/create_timer.hpp"
|
||||
#include "rclcpp/create_subscription.hpp"
|
||||
#include "rclcpp/create_timer.hpp"
|
||||
#include "rclcpp/detail/resolve_enable_topic_statistics.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
@@ -84,7 +86,6 @@ template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT,
|
||||
typename CallbackMessageT,
|
||||
typename SubscriptionT,
|
||||
typename MessageMemoryStrategyT>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
@@ -152,6 +153,43 @@ Node::create_service(
|
||||
group);
|
||||
}
|
||||
|
||||
template<typename AllocatorT>
|
||||
std::shared_ptr<rclcpp::GenericPublisher>
|
||||
Node::create_generic_publisher(
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
|
||||
{
|
||||
return rclcpp::create_generic_publisher(
|
||||
node_topics_,
|
||||
extend_name_with_sub_namespace(topic_name, this->get_sub_namespace()),
|
||||
topic_type,
|
||||
qos,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
template<typename AllocatorT>
|
||||
std::shared_ptr<rclcpp::GenericSubscription>
|
||||
Node::create_generic_subscription(
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options)
|
||||
{
|
||||
return rclcpp::create_generic_subscription(
|
||||
node_topics_,
|
||||
extend_name_with_sub_namespace(topic_name, this->get_sub_namespace()),
|
||||
topic_type,
|
||||
qos,
|
||||
std::move(callback),
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
template<typename ParameterT>
|
||||
auto
|
||||
Node::declare_parameter(
|
||||
@@ -172,6 +210,28 @@ Node::declare_parameter(
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
auto
|
||||
Node::declare_parameter(
|
||||
const std::string & name,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
|
||||
bool ignore_override)
|
||||
{
|
||||
// get advantage of parameter value template magic to get
|
||||
// the correct rclcpp::ParameterType from ParameterT
|
||||
rclcpp::ParameterValue value{ParameterT{}};
|
||||
try {
|
||||
return this->declare_parameter(
|
||||
name,
|
||||
value.get_type(),
|
||||
parameter_descriptor,
|
||||
ignore_override
|
||||
).get<ParameterT>();
|
||||
} catch (const ParameterTypeException &) {
|
||||
throw exceptions::UninitializedStaticallyTypedParameterException(name);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
std::vector<ParameterT>
|
||||
Node::declare_parameters(
|
||||
@@ -253,6 +313,17 @@ Node::get_parameter_or(
|
||||
return got_parameter;
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
ParameterT
|
||||
Node::get_parameter_or(
|
||||
const std::string & name,
|
||||
const ParameterT & alternative_value) const
|
||||
{
|
||||
ParameterT parameter;
|
||||
get_parameter_or(name, parameter, alternative_value);
|
||||
return parameter;
|
||||
}
|
||||
|
||||
// this is a partially-specialized version of get_parameter above,
|
||||
// where our concrete type for ParameterT is std::map, but the to-be-determined
|
||||
// type is the value in the map.
|
||||
|
||||
@@ -15,11 +15,14 @@
|
||||
#ifndef RCLCPP__NODE_INTERFACES__NODE_BASE_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__NODE_BASE_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/node.h"
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
@@ -31,7 +34,7 @@ namespace node_interfaces
|
||||
{
|
||||
|
||||
/// Implementation of the NodeBase part of the Node API.
|
||||
class NodeBase : public NodeBaseInterface
|
||||
class NodeBase : public NodeBaseInterface, public std::enable_shared_from_this<NodeBase>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBase)
|
||||
@@ -83,7 +86,9 @@ public:
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::CallbackGroup::SharedPtr
|
||||
create_callback_group(rclcpp::CallbackGroupType group_type) override;
|
||||
create_callback_group(
|
||||
rclcpp::CallbackGroupType group_type,
|
||||
bool automatically_add_to_executor_with_node = true) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::CallbackGroup::SharedPtr
|
||||
@@ -93,22 +98,25 @@ public:
|
||||
bool
|
||||
callback_group_in_node(rclcpp::CallbackGroup::SharedPtr group) override;
|
||||
|
||||
/// Iterate over the stored callback groups, calling the given function on each valid one.
|
||||
/**
|
||||
* This method is called in a thread-safe way, and also makes sure to only call the given
|
||||
* function on those items that are still valid.
|
||||
*
|
||||
* \param[in] func The callback function to call on each valid callback group.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<rclcpp::CallbackGroup::WeakPtr> &
|
||||
get_callback_groups() const override;
|
||||
void
|
||||
for_each_callback_group(const CallbackGroupFunction & func) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::atomic_bool &
|
||||
get_associated_with_executor_atomic() override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rcl_guard_condition_t *
|
||||
rclcpp::GuardCondition &
|
||||
get_notify_guard_condition() override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::unique_lock<std::recursive_mutex>
|
||||
acquire_notify_guard_condition_lock() const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
get_use_intra_process_default() const override;
|
||||
@@ -116,6 +124,10 @@ public:
|
||||
bool
|
||||
get_enable_topic_statistics_default() const override;
|
||||
|
||||
std::string
|
||||
resolve_topic_or_service_name(
|
||||
const std::string & name, bool is_service, bool only_expand = false) const override;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(NodeBase)
|
||||
|
||||
@@ -126,13 +138,14 @@ private:
|
||||
std::shared_ptr<rcl_node_t> node_handle_;
|
||||
|
||||
rclcpp::CallbackGroup::SharedPtr default_callback_group_;
|
||||
std::mutex callback_groups_mutex_;
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr> callback_groups_;
|
||||
|
||||
std::atomic_bool associated_with_executor_;
|
||||
|
||||
/// Guard condition for notifying the Executor of changes to this node.
|
||||
mutable std::recursive_mutex notify_guard_condition_mutex_;
|
||||
rcl_guard_condition_t notify_guard_condition_ = rcl_get_zero_initialized_guard_condition();
|
||||
rclcpp::GuardCondition notify_guard_condition_;
|
||||
bool notify_guard_condition_is_valid_;
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -106,7 +107,9 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
rclcpp::CallbackGroup::SharedPtr
|
||||
create_callback_group(rclcpp::CallbackGroupType group_type) = 0;
|
||||
create_callback_group(
|
||||
rclcpp::CallbackGroupType group_type,
|
||||
bool automatically_add_to_executor_with_node = true) = 0;
|
||||
|
||||
/// Return the default callback group.
|
||||
RCLCPP_PUBLIC
|
||||
@@ -120,11 +123,19 @@ public:
|
||||
bool
|
||||
callback_group_in_node(rclcpp::CallbackGroup::SharedPtr group) = 0;
|
||||
|
||||
/// Return list of callback groups associated with this node.
|
||||
using CallbackGroupFunction = std::function<void (rclcpp::CallbackGroup::SharedPtr)>;
|
||||
|
||||
/// Iterate over the stored callback groups, calling the given function on each valid one.
|
||||
/**
|
||||
* This method is called in a thread-safe way, and also makes sure to only call the given
|
||||
* function on those items that are still valid.
|
||||
*
|
||||
* \param[in] func The callback function to call on each valid callback group.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
const std::vector<rclcpp::CallbackGroup::WeakPtr> &
|
||||
get_callback_groups() const = 0;
|
||||
void
|
||||
for_each_callback_group(const CallbackGroupFunction & func) = 0;
|
||||
|
||||
/// Return the atomic bool which is used to ensure only one executor is used.
|
||||
RCLCPP_PUBLIC
|
||||
@@ -132,24 +143,17 @@ public:
|
||||
std::atomic_bool &
|
||||
get_associated_with_executor_atomic() = 0;
|
||||
|
||||
/// Return guard condition that should be notified when the internal node state changes.
|
||||
/// Return a guard condition that should be notified when the internal node state changes.
|
||||
/**
|
||||
* For example, this should be notified when a publisher is added or removed.
|
||||
*
|
||||
* \return the rcl_guard_condition_t if it is valid, else nullptr
|
||||
* \return the GuardCondition if it is valid, else thow runtime error
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
rcl_guard_condition_t *
|
||||
rclcpp::GuardCondition &
|
||||
get_notify_guard_condition() = 0;
|
||||
|
||||
/// Acquire and return a scoped lock that protects the notify guard condition.
|
||||
/** This should be used when triggering the notify guard condition. */
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::unique_lock<std::recursive_mutex>
|
||||
acquire_notify_guard_condition_lock() const = 0;
|
||||
|
||||
/// Return the default preference for using intra process communication.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
@@ -161,6 +165,13 @@ public:
|
||||
virtual
|
||||
bool
|
||||
get_enable_topic_statistics_default() const = 0;
|
||||
|
||||
/// Expand and remap a given topic or service name.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::string
|
||||
resolve_topic_or_service_name(
|
||||
const std::string & name, bool is_service, bool only_expand = false) const = 0;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -70,10 +71,34 @@ public:
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_client_names_and_types_by_node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_publisher_names_and_types_by_node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
bool no_demangle = false) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_subscriber_names_and_types_by_node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
bool no_demangle = false) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<std::string>
|
||||
get_node_names() const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<std::tuple<std::string, std::string, std::string>>
|
||||
get_node_names_with_enclaves() const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<std::pair<std::string, std::string>>
|
||||
get_node_names_and_namespaces() const override;
|
||||
@@ -110,7 +135,7 @@ public:
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
count_graph_users() override;
|
||||
count_graph_users() const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::TopicEndpointInfo>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -146,6 +147,9 @@ public:
|
||||
/**
|
||||
* A topic is considered to exist when at least one publisher or subscriber
|
||||
* exists for it, whether they be local or remote to this process.
|
||||
* The returned names are the actual names of the topics, either announced by another nodes or by this one.
|
||||
* Attempting to create publishers or subscribers using names returned by this function may not
|
||||
* result in the desired topic name being used depending on the remap rules in use.
|
||||
*
|
||||
* \param[in] no_demangle if true, topic names and types are not demangled
|
||||
*/
|
||||
@@ -159,6 +163,9 @@ public:
|
||||
* A service is considered to exist when at least one service server or
|
||||
* service client exists for it, whether they be local or remote to this
|
||||
* process.
|
||||
* The returned names are the actual names of the services, either announced by another nodes or by this one.
|
||||
* Attempting to create clients or services using names returned by this function may not result in
|
||||
* the desired service name being used depending on the remap rules in use.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
@@ -168,6 +175,9 @@ public:
|
||||
/// Return a map of existing service names to list of service types for a specific node.
|
||||
/**
|
||||
* This function only considers services - not clients.
|
||||
* The returned names are the actual names after remap rules applied.
|
||||
* Attempting to create service clients using names returned by this function may not
|
||||
* result in the desired service name being used depending on the remap rules in use.
|
||||
*
|
||||
* \param[in] node_name name of the node
|
||||
* \param[in] namespace_ namespace of the node
|
||||
@@ -179,25 +189,104 @@ public:
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_) const = 0;
|
||||
|
||||
/// Return a map of existing service names and types with a specific node.
|
||||
/**
|
||||
* This function only considers clients - not service servers.
|
||||
* The returned names are the actual names after remap rules applied.
|
||||
* Attempting to create service servers using names returned by this function may not
|
||||
* result in the desired service name being used depending on the remap rules in use.
|
||||
*
|
||||
* \param[in] node_name name of the node
|
||||
* \param[in] namespace_ namespace of the node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_client_names_and_types_by_node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_) const = 0;
|
||||
|
||||
/// Return a map of existing topic names to list of topic types for a specific node.
|
||||
/**
|
||||
* This function only considers publishers - not subscribers.
|
||||
* The returned names are the actual names after remap rules applied.
|
||||
* Attempting to create publishers or subscribers using names returned by this function may not
|
||||
* result in the desired topic name being used depending on the remap rules in use.
|
||||
*
|
||||
* \param[in] node_name name of the node
|
||||
* \param[in] namespace_ namespace of the node
|
||||
* \param[in] no_demangle if true, topic names and types are not demangled
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_publisher_names_and_types_by_node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
bool no_demangle = false) const = 0;
|
||||
|
||||
/// Return a map of existing topic names to list of topic types for a specific node.
|
||||
/**
|
||||
* This function only considers subscribers - not publishers.
|
||||
* The returned names are the actual names after remap rules applied.
|
||||
* Attempting to create publishers or subscribers using names returned by this function may not
|
||||
* result in the desired topic name being used depending on the remap rules in use.
|
||||
*
|
||||
* \param[in] node_name name of the node
|
||||
* \param[in] namespace_ namespace of the node
|
||||
* \param[in] no_demangle if true, topic names and types are not demangled
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_subscriber_names_and_types_by_node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
bool no_demangle = false) const = 0;
|
||||
|
||||
/// Return a vector of existing node names (string).
|
||||
/*
|
||||
* The returned names are the actual names after remap rules applied.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::vector<std::string>
|
||||
get_node_names() const = 0;
|
||||
|
||||
/// Return a vector of existing node names, namespaces and enclaves (tuple of string).
|
||||
/*
|
||||
* The returned names are the actual names after remap rules applied.
|
||||
* The enclaves contain the runtime security artifacts, those can be
|
||||
* used to establish secured network.
|
||||
* See https://design.ros2.org/articles/ros2_security_enclaves.html
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::vector<std::tuple<std::string, std::string, std::string>>
|
||||
get_node_names_with_enclaves() const = 0;
|
||||
|
||||
/// Return a vector of existing node names and namespaces (pair of string).
|
||||
/*
|
||||
* The returned names are the actual names after remap rules applied.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::vector<std::pair<std::string, std::string>>
|
||||
get_node_names_and_namespaces() const = 0;
|
||||
|
||||
/// Return the number of publishers that are advertised on a given topic.
|
||||
/*
|
||||
* \param[in] topic_name the actual topic name used; it will not be automatically remapped.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
size_t
|
||||
count_publishers(const std::string & topic_name) const = 0;
|
||||
|
||||
/// Return the number of subscribers who have created a subscription for a given topic.
|
||||
/*
|
||||
* \param[in] topic_name the actual topic name used; it will not be automatically remapped.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
size_t
|
||||
@@ -263,10 +352,13 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
size_t
|
||||
count_graph_users() = 0;
|
||||
count_graph_users() const = 0;
|
||||
|
||||
/// Return the topic endpoint information about publishers on a given topic.
|
||||
/**
|
||||
* \param[in] topic_name the actual topic name used; it will not be automatically remapped.
|
||||
* \param[in] no_mangle if `true`, `topic_name` needs to be a valid middleware topic name,
|
||||
* otherwise it should be a valid ROS topic name.
|
||||
* \sa rclcpp::Node::get_publishers_info_by_topic
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
@@ -276,6 +368,9 @@ public:
|
||||
|
||||
/// Return the topic endpoint information about subscriptions on a given topic.
|
||||
/**
|
||||
* \param[in] topic_name the actual topic name used; it will not be automatically remapped.
|
||||
* \param[in] no_mangle if `true`, `topic_name` needs to be a valid middleware topic name,
|
||||
* otherwise it should be a valid ROS topic name.
|
||||
* \sa rclcpp::Node::get_subscriptions_info_by_topic
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rcutils/macros.h"
|
||||
|
||||
#include "rcl_interfaces/msg/list_parameters_result.hpp"
|
||||
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
|
||||
#include "rcl_interfaces/msg/parameter_event.hpp"
|
||||
@@ -106,8 +108,18 @@ public:
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
const rclcpp::ParameterValue & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
|
||||
bool ignore_override) override;
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor{},
|
||||
bool ignore_override = false) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
rclcpp::ParameterType type,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
bool ignore_override = false) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
@@ -160,6 +172,7 @@ public:
|
||||
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
RCUTILS_WARN_UNUSED
|
||||
OnSetParametersCallbackHandle::SharedPtr
|
||||
add_on_set_parameters_callback(OnParametersSetCallbackType callback) override;
|
||||
|
||||
@@ -167,11 +180,6 @@ public:
|
||||
void
|
||||
remove_on_set_parameters_callback(const OnSetParametersCallbackHandle * const handler) override;
|
||||
|
||||
[[deprecated("use add_on_set_parameters_callback(OnParametersSetCallbackType callback) instead")]]
|
||||
RCLCPP_PUBLIC
|
||||
OnParametersSetCallbackType
|
||||
set_on_parameters_set_callback(OnParametersSetCallbackType callback) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::map<std::string, rclcpp::ParameterValue> &
|
||||
get_parameter_overrides() const override;
|
||||
|
||||
@@ -64,7 +64,21 @@ public:
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
const rclcpp::ParameterValue & default_value = rclcpp::ParameterValue(),
|
||||
const rclcpp::ParameterValue & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
bool ignore_override = false) = 0;
|
||||
|
||||
/// Declare a parameter.
|
||||
/**
|
||||
* \sa rclcpp::Node::declare_parameter
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
rclcpp::ParameterType type,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
bool ignore_override = false) = 0;
|
||||
@@ -96,7 +110,7 @@ public:
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
set_parameters(const std::vector<rclcpp::Parameter> & parameters) = 0;
|
||||
|
||||
/// Set and initialize a parameter, all at once.
|
||||
/// Set one or more parameters, all at once.
|
||||
/**
|
||||
* \sa rclcpp::Node::set_parameters_atomically
|
||||
*/
|
||||
@@ -191,17 +205,6 @@ public:
|
||||
void
|
||||
remove_on_set_parameters_callback(const OnSetParametersCallbackHandle * const handler) = 0;
|
||||
|
||||
/// Register a callback for when parameters are being set, return an existing one.
|
||||
/**
|
||||
* \deprecated Use add_on_set_parameters_callback instead.
|
||||
* \sa rclcpp::Node::set_on_parameters_set_callback
|
||||
*/
|
||||
[[deprecated("use add_on_set_parameters_callback(OnParametersSetCallbackType callback) instead")]]
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
OnParametersSetCallbackType
|
||||
set_on_parameters_set_callback(OnParametersSetCallbackType callback) = 0;
|
||||
|
||||
/// Return the initial parameter values used by the NodeParameters to override default values.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#ifndef RCLCPP__NODE_INTERFACES__NODE_SERVICES_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__NODE_SERVICES_HPP_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/client.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
@@ -53,6 +55,10 @@ public:
|
||||
rclcpp::ServiceBase::SharedPtr service_base_ptr,
|
||||
rclcpp::CallbackGroup::SharedPtr group) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
resolve_service_name(const std::string & name, bool only_expand = false) const override;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(NodeServices)
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#ifndef RCLCPP__NODE_INTERFACES__NODE_SERVICES_INTERFACE_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__NODE_SERVICES_INTERFACE_HPP_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/client.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
@@ -49,6 +51,12 @@ public:
|
||||
add_service(
|
||||
rclcpp::ServiceBase::SharedPtr service_base_ptr,
|
||||
rclcpp::CallbackGroup::SharedPtr group) = 0;
|
||||
|
||||
/// Get the remapped and expanded service name given a input name.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::string
|
||||
resolve_service_name(const std::string & name, bool only_expand = false) const = 0;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "rclcpp/node_interfaces/node_services_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_time_source_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/time_source.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -46,7 +47,9 @@ public:
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services,
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock,
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters,
|
||||
const rclcpp::QoS & qos = rclcpp::ClockQoS(),
|
||||
bool use_clock_thread = true
|
||||
);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
|
||||
@@ -20,12 +20,16 @@
|
||||
#include "rcl/publisher.h"
|
||||
#include "rcl/subscription.h"
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/publisher.hpp"
|
||||
#include "rclcpp/publisher_base.hpp"
|
||||
#include "rclcpp/publisher_factory.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/subscription_base.hpp"
|
||||
#include "rclcpp/subscription_factory.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -81,6 +85,10 @@ public:
|
||||
rclcpp::node_interfaces::NodeTimersInterface *
|
||||
get_node_timers_interface() const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
resolve_topic_name(const std::string & name, bool only_expand = false) const override;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(NodeTopics)
|
||||
|
||||
|
||||
@@ -86,6 +86,12 @@ public:
|
||||
virtual
|
||||
rclcpp::node_interfaces::NodeTimersInterface *
|
||||
get_node_timers_interface() const = 0;
|
||||
|
||||
/// Get a remapped and expanded topic name given an input name.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::string
|
||||
resolve_topic_name(const std::string & name, bool only_expand = false) const = 0;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
|
||||
@@ -46,6 +46,9 @@ public:
|
||||
* - enable_topic_statistics = false
|
||||
* - start_parameter_services = true
|
||||
* - start_parameter_event_publisher = true
|
||||
* - clock_qos = rclcpp::ClockQoS()
|
||||
* - use_clock_thread = true
|
||||
* - rosout_qos = rclcpp::RosoutQoS()
|
||||
* - parameter_event_qos = rclcpp::ParameterEventQoS
|
||||
* - with history setting and depth from rmw_qos_profile_parameter_events
|
||||
* - parameter_event_publisher_options = rclcpp::PublisherOptionsBase
|
||||
@@ -243,6 +246,33 @@ public:
|
||||
NodeOptions &
|
||||
start_parameter_event_publisher(bool start_parameter_event_publisher);
|
||||
|
||||
/// Return a reference to the clock QoS.
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::QoS &
|
||||
clock_qos() const;
|
||||
|
||||
/// Set the clock QoS.
|
||||
/**
|
||||
* The QoS settings to be used for the publisher on /clock topic, if enabled.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
clock_qos(const rclcpp::QoS & clock_qos);
|
||||
|
||||
|
||||
/// Return the use_clock_thread flag.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
use_clock_thread() const;
|
||||
|
||||
/// Set the use_clock_thread flag, return this for parameter idiom.
|
||||
/**
|
||||
* If true, a dedicated thread will be used to subscribe to "/clock" topic.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
use_clock_thread(bool use_clock_thread);
|
||||
|
||||
/// Return a reference to the parameter_event_qos QoS.
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::QoS &
|
||||
@@ -256,6 +286,19 @@ public:
|
||||
NodeOptions &
|
||||
parameter_event_qos(const rclcpp::QoS & parameter_event_qos);
|
||||
|
||||
/// Return a reference to the rosout QoS.
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::QoS &
|
||||
rosout_qos() const;
|
||||
|
||||
/// Set the rosout QoS.
|
||||
/**
|
||||
* The QoS settings to be used for the publisher on /rosout topic, if enabled.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
rosout_qos(const rclcpp::QoS & rosout_qos);
|
||||
|
||||
/// Return a reference to the parameter_event_publisher_options.
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::PublisherOptionsBase &
|
||||
@@ -306,6 +349,9 @@ public:
|
||||
* 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.
|
||||
* Parameter declaration from overrides is done in the node's base constructor,
|
||||
* so the user must take care to check if the parameter is already (e.g.
|
||||
* automatically) declared before declaring it themselves.
|
||||
* Already declared parameters will not be re-declared, and parameters
|
||||
* declared in this way will use the default constructed ParameterDescriptor.
|
||||
*/
|
||||
@@ -327,11 +373,6 @@ public:
|
||||
NodeOptions &
|
||||
allocator(rcl_allocator_t allocator);
|
||||
|
||||
protected:
|
||||
/// Retrieve the ROS_DOMAIN_ID environment variable and populate options.
|
||||
size_t
|
||||
get_domain_id_from_env() const;
|
||||
|
||||
private:
|
||||
// This is mutable to allow for a const accessor which lazily creates the node options instance.
|
||||
/// Underlying rcl_node_options structure.
|
||||
@@ -359,10 +400,16 @@ private:
|
||||
|
||||
bool start_parameter_event_publisher_ {true};
|
||||
|
||||
rclcpp::QoS clock_qos_ = rclcpp::ClockQoS();
|
||||
|
||||
bool use_clock_thread_ {true};
|
||||
|
||||
rclcpp::QoS parameter_event_qos_ = rclcpp::ParameterEventsQoS(
|
||||
rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events)
|
||||
);
|
||||
|
||||
rclcpp::QoS rosout_qos_ = rclcpp::RosoutQoS();
|
||||
|
||||
rclcpp::PublisherOptionsBase parameter_event_publisher_options_ = rclcpp::PublisherOptionsBase();
|
||||
|
||||
bool allow_undeclared_parameters_ {false};
|
||||
|
||||
@@ -264,6 +264,7 @@ get_value_helper<rclcpp::Parameter>(const rclcpp::Parameter * parameter)
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// \cond
|
||||
template<typename T>
|
||||
decltype(auto)
|
||||
Parameter::get_value() const
|
||||
@@ -275,6 +276,7 @@ Parameter::get_value() const
|
||||
throw exceptions::InvalidParameterTypeException(this->name_, ex.what());
|
||||
}
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#ifndef RCLCPP__PARAMETER_CLIENT_HPP_
|
||||
#define RCLCPP__PARAMETER_CLIENT_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -29,11 +31,14 @@
|
||||
#include "rcl_interfaces/srv/list_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/set_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/set_parameters_atomically.hpp"
|
||||
#include "rcl_yaml_param_parser/parser.h"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/executors.hpp"
|
||||
#include "rclcpp/create_subscription.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/parameter_map.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rmw/rmw.h"
|
||||
@@ -48,13 +53,13 @@ public:
|
||||
|
||||
/// Create an async parameters client.
|
||||
/**
|
||||
* \param node_base_interface[in] The node base interface of the corresponding node.
|
||||
* \param node_topics_interface[in] Node topic base interface.
|
||||
* \param node_graph_interface[in] The node graph interface of the corresponding node.
|
||||
* \param node_services_interface[in] Node service interface.
|
||||
* \param remote_node_name[in] (optional) name of the remote node
|
||||
* \param qos_profile[in] (optional) The rmw qos profile to use to subscribe
|
||||
* \param group[in] (optional) The async parameter client will be added to this callback group.
|
||||
* \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
|
||||
* \param[in] group (optional) The async parameter client will be added to this callback group.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
AsyncParametersClient(
|
||||
@@ -68,31 +73,49 @@ public:
|
||||
|
||||
/// Constructor
|
||||
/**
|
||||
* \param node[in] The async paramters client will be added to this node.
|
||||
* \param remote_node_name[in] (optional) name of the remote node
|
||||
* \param qos_profile[in] (optional) The rmw qos profile to use to subscribe
|
||||
* \param group[in] (optional) The async parameter client will be added to this callback group.
|
||||
* \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
|
||||
* \param[in] group (optional) The async parameter client will be added to this callback group.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
AsyncParametersClient(
|
||||
const rclcpp::Node::SharedPtr node,
|
||||
template<typename NodeT>
|
||||
explicit AsyncParametersClient(
|
||||
const std::shared_ptr<NodeT> node,
|
||||
const std::string & remote_node_name = "",
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters,
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr);
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr)
|
||||
: AsyncParametersClient(
|
||||
node->get_node_base_interface(),
|
||||
node->get_node_topics_interface(),
|
||||
node->get_node_graph_interface(),
|
||||
node->get_node_services_interface(),
|
||||
remote_node_name,
|
||||
qos_profile,
|
||||
group)
|
||||
{}
|
||||
|
||||
/// Constructor
|
||||
/**
|
||||
* \param node[in] The async paramters client will be added to this node.
|
||||
* \param remote_node_name[in] (optional) name of the remote node
|
||||
* \param qos_profile[in] (optional) The rmw qos profile to use to subscribe
|
||||
* \param group[in] (optional) The async parameter client will be added to this callback group.
|
||||
* \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
|
||||
* \param[in] group (optional) The async parameter client will be added to this callback group.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
AsyncParametersClient(
|
||||
rclcpp::Node * node,
|
||||
template<typename NodeT>
|
||||
explicit AsyncParametersClient(
|
||||
NodeT * node,
|
||||
const std::string & remote_node_name = "",
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters,
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr);
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr)
|
||||
: AsyncParametersClient(
|
||||
node->get_node_base_interface(),
|
||||
node->get_node_topics_interface(),
|
||||
node->get_node_graph_interface(),
|
||||
node->get_node_services_interface(),
|
||||
remote_node_name,
|
||||
qos_profile,
|
||||
group)
|
||||
{}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<std::vector<rclcpp::Parameter>>
|
||||
@@ -102,6 +125,14 @@ public:
|
||||
void(std::shared_future<std::vector<rclcpp::Parameter>>)
|
||||
> callback = nullptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>>
|
||||
describe_parameters(
|
||||
const std::vector<std::string> & names,
|
||||
std::function<
|
||||
void(std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>>)
|
||||
> callback = nullptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<std::vector<rclcpp::ParameterType>>
|
||||
get_parameter_types(
|
||||
@@ -126,6 +157,42 @@ public:
|
||||
void(std::shared_future<rcl_interfaces::msg::SetParametersResult>)
|
||||
> callback = nullptr);
|
||||
|
||||
/// Delete several parameters at once.
|
||||
/**
|
||||
* This function behaves like command-line tool `ros2 param delete` would.
|
||||
*
|
||||
* \param parameters_names vector of parameters names
|
||||
* \return the future of the set_parameter service used to delete the parameters
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
|
||||
delete_parameters(
|
||||
const std::vector<std::string> & parameters_names);
|
||||
|
||||
/// Load parameters from yaml file.
|
||||
/**
|
||||
* This function behaves like command-line tool `ros2 param load` would.
|
||||
*
|
||||
* \param yaml_filename the full name of the yaml file
|
||||
* \return the future of the set_parameter service used to load the parameters
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
|
||||
load_parameters(
|
||||
const std::string & yaml_filename);
|
||||
|
||||
/// Load parameters from parameter map.
|
||||
/**
|
||||
* This function filters the parameters to be set based on the node name.
|
||||
*
|
||||
* \param parameter_map named parameters to be loaded
|
||||
* \return the future of the set_parameter service used to load the parameters
|
||||
* \throw InvalidParametersException if there is no parameter to set
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
|
||||
load_parameters(const rclcpp::ParameterMap & parameter_map);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<rcl_interfaces::msg::ListParametersResult>
|
||||
list_parameters(
|
||||
@@ -177,7 +244,7 @@ public:
|
||||
{
|
||||
return rclcpp::create_subscription<rcl_interfaces::msg::ParameterEvent>(
|
||||
node,
|
||||
"parameter_events",
|
||||
"/parameter_events",
|
||||
qos,
|
||||
std::forward<CallbackT>(callback),
|
||||
options);
|
||||
@@ -237,31 +304,61 @@ class SyncParametersClient
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SyncParametersClient)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
template<typename NodeT>
|
||||
explicit SyncParametersClient(
|
||||
rclcpp::Node::SharedPtr node,
|
||||
std::shared_ptr<NodeT> node,
|
||||
const std::string & remote_node_name = "",
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters)
|
||||
: SyncParametersClient(
|
||||
std::make_shared<rclcpp::executors::SingleThreadedExecutor>(),
|
||||
node,
|
||||
remote_node_name,
|
||||
qos_profile)
|
||||
{}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
template<typename NodeT>
|
||||
SyncParametersClient(
|
||||
rclcpp::Executor::SharedPtr executor,
|
||||
rclcpp::Node::SharedPtr node,
|
||||
std::shared_ptr<NodeT> node,
|
||||
const std::string & remote_node_name = "",
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters)
|
||||
: SyncParametersClient(
|
||||
executor,
|
||||
node->get_node_base_interface(),
|
||||
node->get_node_topics_interface(),
|
||||
node->get_node_graph_interface(),
|
||||
node->get_node_services_interface(),
|
||||
remote_node_name,
|
||||
qos_profile)
|
||||
{}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
template<typename NodeT>
|
||||
explicit SyncParametersClient(
|
||||
rclcpp::Node * node,
|
||||
NodeT * node,
|
||||
const std::string & remote_node_name = "",
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters)
|
||||
: SyncParametersClient(
|
||||
std::make_shared<rclcpp::executors::SingleThreadedExecutor>(),
|
||||
node,
|
||||
remote_node_name,
|
||||
qos_profile)
|
||||
{}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
template<typename NodeT>
|
||||
SyncParametersClient(
|
||||
rclcpp::Executor::SharedPtr executor,
|
||||
rclcpp::Node * node,
|
||||
NodeT * node,
|
||||
const std::string & remote_node_name = "",
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters)
|
||||
: SyncParametersClient(
|
||||
executor,
|
||||
node->get_node_base_interface(),
|
||||
node->get_node_topics_interface(),
|
||||
node->get_node_graph_interface(),
|
||||
node->get_node_services_interface(),
|
||||
remote_node_name,
|
||||
qos_profile)
|
||||
{}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
SyncParametersClient(
|
||||
@@ -271,11 +368,30 @@ public:
|
||||
const rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
|
||||
const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,
|
||||
const std::string & remote_node_name = "",
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters)
|
||||
: executor_(executor), node_base_interface_(node_base_interface)
|
||||
{
|
||||
async_parameters_client_ =
|
||||
std::make_shared<AsyncParametersClient>(
|
||||
node_base_interface,
|
||||
node_topics_interface,
|
||||
node_graph_interface,
|
||||
node_services_interface,
|
||||
remote_node_name,
|
||||
qos_profile);
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
template<typename RepT = int64_t, typename RatioT = std::milli>
|
||||
std::vector<rclcpp::Parameter>
|
||||
get_parameters(const std::vector<std::string> & parameter_names);
|
||||
get_parameters(
|
||||
const std::vector<std::string> & parameter_names,
|
||||
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
|
||||
{
|
||||
return get_parameters(
|
||||
parameter_names,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
);
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
@@ -319,23 +435,107 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
template<typename RepT = int64_t, typename RatioT = std::milli>
|
||||
std::vector<rcl_interfaces::msg::ParameterDescriptor>
|
||||
describe_parameters(
|
||||
const std::vector<std::string> & parameter_names,
|
||||
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
|
||||
{
|
||||
return describe_parameters(
|
||||
parameter_names,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
);
|
||||
}
|
||||
|
||||
template<typename RepT = int64_t, typename RatioT = std::milli>
|
||||
std::vector<rclcpp::ParameterType>
|
||||
get_parameter_types(const std::vector<std::string> & parameter_names);
|
||||
get_parameter_types(
|
||||
const std::vector<std::string> & parameter_names,
|
||||
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
|
||||
{
|
||||
return get_parameter_types(
|
||||
parameter_names,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
);
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
template<typename RepT = int64_t, typename RatioT = std::milli>
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
set_parameters(const std::vector<rclcpp::Parameter> & parameters);
|
||||
set_parameters(
|
||||
const std::vector<rclcpp::Parameter> & parameters,
|
||||
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
|
||||
{
|
||||
return set_parameters(
|
||||
parameters,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
);
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
template<typename RepT = int64_t, typename RatioT = std::milli>
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
set_parameters_atomically(const std::vector<rclcpp::Parameter> & parameters);
|
||||
set_parameters_atomically(
|
||||
const std::vector<rclcpp::Parameter> & parameters,
|
||||
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
|
||||
{
|
||||
return set_parameters_atomically(
|
||||
parameters,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
);
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
/// Delete several parameters at once.
|
||||
/**
|
||||
* This function behaves like command-line tool `ros2 param delete` would.
|
||||
*
|
||||
* \param parameters_names vector of parameters names
|
||||
* \param timeout for the spin used to make it synchronous
|
||||
* \return the future of the set_parameter service used to delete the parameters
|
||||
*/
|
||||
template<typename RepT = int64_t, typename RatioT = std::milli>
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
delete_parameters(
|
||||
const std::vector<std::string> & parameters_names,
|
||||
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
|
||||
{
|
||||
return delete_parameters(
|
||||
parameters_names,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
);
|
||||
}
|
||||
|
||||
/// Load parameters from yaml file.
|
||||
/**
|
||||
* This function behaves like command-line tool `ros2 param load` would.
|
||||
*
|
||||
* \param yaml_filename the full name of the yaml file
|
||||
* \param timeout for the spin used to make it synchronous
|
||||
* \return the future of the set_parameter service used to load the parameters
|
||||
*/
|
||||
template<typename RepT = int64_t, typename RatioT = std::milli>
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
load_parameters(
|
||||
const std::string & yaml_filename,
|
||||
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
|
||||
{
|
||||
return load_parameters(
|
||||
yaml_filename,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
);
|
||||
}
|
||||
|
||||
template<typename RepT = int64_t, typename RatioT = std::milli>
|
||||
rcl_interfaces::msg::ListParametersResult
|
||||
list_parameters(
|
||||
const std::vector<std::string> & parameter_prefixes,
|
||||
uint64_t depth);
|
||||
uint64_t depth,
|
||||
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
|
||||
{
|
||||
return list_parameters(
|
||||
parameter_prefixes,
|
||||
depth,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
);
|
||||
}
|
||||
|
||||
template<typename CallbackT>
|
||||
typename rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
|
||||
@@ -378,6 +578,56 @@ public:
|
||||
return async_parameters_client_->wait_for_service(timeout);
|
||||
}
|
||||
|
||||
protected:
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::Parameter>
|
||||
get_parameters(
|
||||
const std::vector<std::string> & parameter_names,
|
||||
std::chrono::nanoseconds timeout);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rcl_interfaces::msg::ParameterDescriptor>
|
||||
describe_parameters(
|
||||
const std::vector<std::string> & parameter_names,
|
||||
std::chrono::nanoseconds timeout);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::ParameterType>
|
||||
get_parameter_types(
|
||||
const std::vector<std::string> & parameter_names,
|
||||
std::chrono::nanoseconds timeout);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
set_parameters(
|
||||
const std::vector<rclcpp::Parameter> & parameters,
|
||||
std::chrono::nanoseconds timeout);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
delete_parameters(
|
||||
const std::vector<std::string> & parameters_names,
|
||||
std::chrono::nanoseconds timeout);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
load_parameters(
|
||||
const std::string & yaml_filename,
|
||||
std::chrono::nanoseconds timeout);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
set_parameters_atomically(
|
||||
const std::vector<rclcpp::Parameter> & parameters,
|
||||
std::chrono::nanoseconds timeout);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rcl_interfaces::msg::ListParametersResult
|
||||
list_parameters(
|
||||
const std::vector<std::string> & parameter_prefixes,
|
||||
uint64_t depth,
|
||||
std::chrono::nanoseconds timeout);
|
||||
|
||||
private:
|
||||
rclcpp::Executor::SharedPtr executor_;
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface_;
|
||||
|
||||
347
rclcpp/include/rclcpp/parameter_event_handler.hpp
Normal file
347
rclcpp/include/rclcpp/parameter_event_handler.hpp
Normal file
@@ -0,0 +1,347 @@
|
||||
// Copyright 2019 Intel Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__PARAMETER_EVENT_HANDLER_HPP_
|
||||
#define RCLCPP__PARAMETER_EVENT_HANDLER_HPP_
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/create_subscription.hpp"
|
||||
#include "rclcpp/node_interfaces/get_node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/subscription.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rcl_interfaces/msg/parameter_event.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
struct ParameterCallbackHandle
|
||||
{
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(ParameterCallbackHandle)
|
||||
|
||||
using ParameterCallbackType = std::function<void (const rclcpp::Parameter &)>;
|
||||
|
||||
std::string parameter_name;
|
||||
std::string node_name;
|
||||
ParameterCallbackType callback;
|
||||
};
|
||||
|
||||
struct ParameterEventCallbackHandle
|
||||
{
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(ParameterEventCallbackHandle)
|
||||
|
||||
using ParameterEventCallbackType =
|
||||
std::function<void (const rcl_interfaces::msg::ParameterEvent &)>;
|
||||
|
||||
ParameterEventCallbackType callback;
|
||||
};
|
||||
|
||||
/// A class used to "handle" (monitor and respond to) changes to parameters.
|
||||
/**
|
||||
* The ParameterEventHandler class allows for the monitoring of changes to node parameters,
|
||||
* either a node's own parameters or parameters owned by other nodes in the system.
|
||||
* Multiple parameter callbacks can be set and will be invoked when the specified parameter
|
||||
* changes.
|
||||
*
|
||||
* The first step is to instantiate a ParameterEventHandler, providing a ROS node to use
|
||||
* to create any required subscriptions:
|
||||
*
|
||||
* auto param_handler = std::make_shared<rclcpp::ParameterEventHandler>(node);
|
||||
*
|
||||
* Next, you can supply a callback to the add_parameter_callback method, as follows:
|
||||
*
|
||||
* auto cb1 = [&node](const rclcpp::Parameter & p) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb1: Received an update to parameter \"%s\" of type %s: \"%ld\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_int());
|
||||
* };
|
||||
* auto handle1 = param_handler->add_parameter_callback("an_int_param", cb1);
|
||||
*
|
||||
* In this case, we didn't supply a node name (the third, optional, parameter) so the
|
||||
* default will be to monitor for changes to the "an_int_param" parameter associated with
|
||||
* the ROS node supplied in the ParameterEventHandler constructor.
|
||||
* The callback, a lambda function in this case, simply prints out the value of the parameter.
|
||||
*
|
||||
* You may also monitor for changes to parameters in other nodes by supplying the node
|
||||
* name to add_parameter_callback:
|
||||
*
|
||||
* auto cb2 = [&node](const rclcpp::Parameter & p) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb2: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_string().c_str());
|
||||
* };
|
||||
* auto handle2 = param_handler->add_parameter_callback(
|
||||
* "some_remote_param_name", cb2, "some_remote_node_name");
|
||||
*
|
||||
* In this case, the callback will be invoked whenever "some_remote_param_name" changes
|
||||
* on remote node "some_remote_node_name".
|
||||
*
|
||||
* To remove a parameter callback, call remove_parameter_callback, passing the handle returned
|
||||
* from add_parameter_callback:
|
||||
*
|
||||
* param_handler->remove_parameter_callback(handle2);
|
||||
*
|
||||
* You can also monitor for *all* parameter changes, using add_parameter_event_callback.
|
||||
* In this case, the callback will be invoked whenever any parameter changes in the system.
|
||||
* You are likely interested in a subset of these parameter changes, so in the callback it
|
||||
* is convenient to use a regular expression on the node names or namespaces of interest.
|
||||
* For example:
|
||||
*
|
||||
* auto cb3 =
|
||||
* [fqn, remote_param_name, &node](const rcl_interfaces::msg::ParameterEvent & event) {
|
||||
* // Look for any updates to parameters in "/a_namespace" as well as any parameter changes
|
||||
* // to our own node ("this_node")
|
||||
* std::regex re("(/a_namespace/.*)|(/this_node)");
|
||||
* if (regex_match(event.node, re)) {
|
||||
* // Now that we know the event matches the regular expression we scanned for, we can
|
||||
* // use 'get_parameter_from_event' to get a specific parameter name that we're looking for
|
||||
* rclcpp::Parameter p;
|
||||
* if (rclcpp::ParameterEventsSubscriber::get_parameter_from_event(
|
||||
* event, p, remote_param_name, fqn))
|
||||
* {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_string().c_str());
|
||||
* }
|
||||
*
|
||||
* // You can also use 'get_parameter*s*_from_event' to enumerate all changes that came
|
||||
* // in on this event
|
||||
* auto params = rclcpp::ParameterEventsSubscriber::get_parameters_from_event(event);
|
||||
* for (auto & p : params) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.value_to_string().c_str());
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
* auto handle3 = param_handler->add_parameter_event_callback(cb3);
|
||||
*
|
||||
* For both parameter callbacks and parameter event callbacks, when multiple callbacks are added,
|
||||
* the callbacks are invoked last-in, first-called order (LIFO).
|
||||
*
|
||||
* To remove a parameter event callback, use:
|
||||
*
|
||||
* param_handler->remove_event_parameter_callback(handle);
|
||||
*/
|
||||
class ParameterEventHandler
|
||||
{
|
||||
public:
|
||||
/// Construct a parameter events monitor.
|
||||
/**
|
||||
* \param[in] node The node to use to create any required subscribers.
|
||||
* \param[in] qos The QoS settings to use for any subscriptions.
|
||||
*/
|
||||
template<typename NodeT>
|
||||
explicit ParameterEventHandler(
|
||||
NodeT node,
|
||||
const rclcpp::QoS & qos =
|
||||
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events)))
|
||||
: node_base_(rclcpp::node_interfaces::get_node_base_interface(node))
|
||||
{
|
||||
auto node_topics = rclcpp::node_interfaces::get_node_topics_interface(node);
|
||||
|
||||
callbacks_ = std::make_shared<Callbacks>();
|
||||
|
||||
event_subscription_ = rclcpp::create_subscription<rcl_interfaces::msg::ParameterEvent>(
|
||||
node_topics, "/parameter_events", qos,
|
||||
[callbacks = callbacks_](const rcl_interfaces::msg::ParameterEvent & event) {
|
||||
callbacks->event_callback(event);
|
||||
});
|
||||
}
|
||||
|
||||
using ParameterEventCallbackType =
|
||||
ParameterEventCallbackHandle::ParameterEventCallbackType;
|
||||
|
||||
/// Set a callback for all parameter events.
|
||||
/**
|
||||
* This function may be called multiple times to set multiple parameter event callbacks.
|
||||
*
|
||||
* \param[in] callback Function callback to be invoked on parameter updates.
|
||||
* \returns A handle used to refer to the callback.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
ParameterEventCallbackHandle::SharedPtr
|
||||
add_parameter_event_callback(
|
||||
ParameterEventCallbackType callback);
|
||||
|
||||
/// Remove parameter event callback registered with add_parameter_event_callback.
|
||||
/**
|
||||
* \param[in] callback_handle Handle of the callback to remove.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_parameter_event_callback(
|
||||
ParameterEventCallbackHandle::SharedPtr callback_handle);
|
||||
|
||||
using ParameterCallbackType = ParameterCallbackHandle::ParameterCallbackType;
|
||||
|
||||
/// Add a callback for a specified parameter.
|
||||
/**
|
||||
* If a node_name is not provided, defaults to the current node.
|
||||
*
|
||||
* \param[in] parameter_name Name of parameter to monitor.
|
||||
* \param[in] callback Function callback to be invoked upon parameter update.
|
||||
* \param[in] node_name Name of node which hosts the parameter.
|
||||
* \returns A handle used to refer to the callback.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
ParameterCallbackHandle::SharedPtr
|
||||
add_parameter_callback(
|
||||
const std::string & parameter_name,
|
||||
ParameterCallbackType callback,
|
||||
const std::string & node_name = "");
|
||||
|
||||
/// Remove a parameter callback registered with add_parameter_callback.
|
||||
/**
|
||||
* The parameter name and node name are inspected from the callback handle. The callback handle
|
||||
* is erased from the list of callback handles on the {parameter_name, node_name} in the map.
|
||||
* An error is thrown if the handle does not exist and/or was already removed.
|
||||
*
|
||||
* \param[in] callback_handle Handle of the callback to remove.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_parameter_callback(
|
||||
ParameterCallbackHandle::SharedPtr callback_handle);
|
||||
|
||||
/// Get an rclcpp::Parameter from a parameter event.
|
||||
/**
|
||||
* If a node_name is not provided, defaults to the current node.
|
||||
*
|
||||
* \param[in] event Event msg to be inspected.
|
||||
* \param[out] parameter Reference to rclcpp::Parameter to be assigned.
|
||||
* \param[in] parameter_name Name of parameter.
|
||||
* \param[in] node_name Name of node which hosts the parameter.
|
||||
* \returns Output parameter is set with requested parameter info and returns true if
|
||||
* requested parameter name and node is in event. Otherwise, returns false.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
static bool
|
||||
get_parameter_from_event(
|
||||
const rcl_interfaces::msg::ParameterEvent & event,
|
||||
rclcpp::Parameter & parameter,
|
||||
const std::string & parameter_name,
|
||||
const std::string & node_name = "");
|
||||
|
||||
/// Get an rclcpp::Parameter from parameter event
|
||||
/**
|
||||
* If a node_name is not provided, defaults to the current node.
|
||||
*
|
||||
* The user is responsible to check if the returned parameter has been properly assigned.
|
||||
* By default, if the requested parameter is not found in the event, the returned parameter
|
||||
* has parameter value of type rclcpp::PARAMETER_NOT_SET.
|
||||
*
|
||||
* \param[in] event Event msg to be inspected.
|
||||
* \param[in] parameter_name Name of parameter.
|
||||
* \param[in] node_name Name of node which hosts the parameter.
|
||||
* \returns The resultant rclcpp::Parameter from the event.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
static rclcpp::Parameter
|
||||
get_parameter_from_event(
|
||||
const rcl_interfaces::msg::ParameterEvent & event,
|
||||
const std::string & parameter_name,
|
||||
const std::string & node_name = "");
|
||||
|
||||
/// Get all rclcpp::Parameter values from a parameter event
|
||||
/**
|
||||
* \param[in] event Event msg to be inspected.
|
||||
* \returns A vector rclcpp::Parameter values from the event.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
static std::vector<rclcpp::Parameter>
|
||||
get_parameters_from_event(
|
||||
const rcl_interfaces::msg::ParameterEvent & event);
|
||||
|
||||
using CallbacksContainerType = std::list<ParameterCallbackHandle::WeakPtr>;
|
||||
|
||||
protected:
|
||||
// *INDENT-OFF* Uncrustify doesn't handle indented public/private labels
|
||||
// Hash function for string pair required in std::unordered_map
|
||||
// See: https://stackoverflow.com/questions/35985960/c-why-is-boosthash-combine-the-best-way-to-combine-hash-values
|
||||
class StringPairHash
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
inline void hash_combine(std::size_t & seed, const T & v) const
|
||||
{
|
||||
std::hash<T> hasher;
|
||||
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
|
||||
inline size_t operator()(const std::pair<std::string, std::string> & s) const
|
||||
{
|
||||
size_t seed = 0;
|
||||
hash_combine(seed, s.first);
|
||||
hash_combine(seed, s.second);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
// *INDENT-ON*
|
||||
|
||||
struct Callbacks
|
||||
{
|
||||
std::recursive_mutex mutex_;
|
||||
|
||||
// Map container for registered parameters
|
||||
std::unordered_map<
|
||||
std::pair<std::string, std::string>,
|
||||
CallbacksContainerType,
|
||||
StringPairHash
|
||||
> parameter_callbacks_;
|
||||
|
||||
std::list<ParameterEventCallbackHandle::WeakPtr> event_callbacks_;
|
||||
|
||||
/// Callback for parameter events subscriptions.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
event_callback(const rcl_interfaces::msg::ParameterEvent & event);
|
||||
};
|
||||
|
||||
std::shared_ptr<Callbacks> callbacks_;
|
||||
|
||||
// Utility function for resolving node path.
|
||||
std::string resolve_path(const std::string & path);
|
||||
|
||||
// Node interface used for base functionality
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> node_base_;
|
||||
|
||||
rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr event_subscription_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__PARAMETER_EVENT_HANDLER_HPP_
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(ParameterEventsFilter)
|
||||
enum class EventType {NEW, DELETED, CHANGED}; ///< An enum for the type of event.
|
||||
/// Used for the listed results
|
||||
using EventPair = std::pair<EventType, rcl_interfaces::msg::Parameter *>;
|
||||
using EventPair = std::pair<EventType, const rcl_interfaces::msg::Parameter *>;
|
||||
|
||||
/// Construct a filtered view of a parameter event.
|
||||
/**
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
ParameterEventsFilter(
|
||||
rcl_interfaces::msg::ParameterEvent::SharedPtr event,
|
||||
std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event,
|
||||
const std::vector<std::string> & names,
|
||||
const std::vector<EventType> & types);
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
private:
|
||||
// access only allowed via const accessor.
|
||||
std::vector<EventPair> result_; ///< Storage of the resultant vector
|
||||
rcl_interfaces::msg::ParameterEvent::SharedPtr event_; ///< Keep event in scope
|
||||
std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event_; ///< Keep event in scope
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef RCLCPP__PARAMETER_MAP_HPP_
|
||||
#define RCLCPP__PARAMETER_MAP_HPP_
|
||||
|
||||
#include <rcl_yaml_param_parser/parser.h>
|
||||
#include <rcl_yaml_param_parser/types.h>
|
||||
|
||||
#include <string>
|
||||
@@ -40,6 +41,16 @@ RCLCPP_PUBLIC
|
||||
ParameterMap
|
||||
parameter_map_from(const rcl_params_t * const c_params);
|
||||
|
||||
/// Convert parameters from rcl_yaml_param_parser into C++ class instances.
|
||||
/// \param[in] c_params C structures containing parameters for multiple nodes.
|
||||
/// \param[in] node_fqn a Fully Qualified Name of node, default value is nullptr.
|
||||
/// If it's not nullptr, return the relative node parameters belonging to this node_fqn.
|
||||
/// \returns a map where the keys are fully qualified node names and values a list of parameters.
|
||||
/// \throws InvalidParametersException if the `rcl_params_t` is inconsistent or invalid.
|
||||
RCLCPP_PUBLIC
|
||||
ParameterMap
|
||||
parameter_map_from(const rcl_params_t * const c_params, const char * node_fqn);
|
||||
|
||||
/// Convert parameter value from rcl_yaml_param_parser into a C++ class instance.
|
||||
/// \param[in] c_value C structure containing a value of a parameter.
|
||||
/// \returns an instance of a parameter value
|
||||
@@ -48,6 +59,14 @@ RCLCPP_PUBLIC
|
||||
ParameterValue
|
||||
parameter_value_from(const rcl_variant_t * const c_value);
|
||||
|
||||
/// Get the ParameterMap from a yaml file.
|
||||
/// \param[in] yaml_filename full name of the yaml file.
|
||||
/// \returns an instance of a parameter map
|
||||
/// \throws from rcl error of rcl_parse_yaml_file()
|
||||
RCLCPP_PUBLIC
|
||||
ParameterMap
|
||||
parameter_map_from_yaml_file(const std::string & yaml_filename);
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__PARAMETER_MAP_HPP_
|
||||
|
||||
@@ -301,6 +301,16 @@ public:
|
||||
return get<ParameterType::PARAMETER_BOOL_ARRAY>();
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
std::is_convertible<
|
||||
type, const std::vector<int> &>::value, const std::vector<int64_t> &>::type
|
||||
get() const
|
||||
{
|
||||
return get<ParameterType::PARAMETER_INTEGER_ARRAY>();
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
@@ -311,6 +321,16 @@ public:
|
||||
return get<ParameterType::PARAMETER_INTEGER_ARRAY>();
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
std::is_convertible<
|
||||
type, const std::vector<float> &>::value, const std::vector<double> &>::type
|
||||
get() const
|
||||
{
|
||||
return get<ParameterType::PARAMETER_DOUBLE_ARRAY>();
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
|
||||
@@ -15,31 +15,37 @@
|
||||
#ifndef RCLCPP__PUBLISHER_HPP_
|
||||
#define RCLCPP__PUBLISHER_HPP_
|
||||
|
||||
#include <rmw/error_handling.h>
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/publisher.h"
|
||||
#include "rmw/error_handling.h"
|
||||
#include "rmw/rmw.h"
|
||||
#include "rosidl_runtime_cpp/traits.hpp"
|
||||
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/allocator/allocator_deleter.hpp"
|
||||
#include "rclcpp/detail/resolve_use_intra_process.hpp"
|
||||
#include "rclcpp/experimental/intra_process_manager.hpp"
|
||||
#include "rclcpp/get_message_type_support_handle.hpp"
|
||||
#include "rclcpp/is_ros_compatible_type.hpp"
|
||||
#include "rclcpp/loaned_message.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/publisher_base.hpp"
|
||||
#include "rclcpp/publisher_options.hpp"
|
||||
#include "rclcpp/type_adapter.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "tracetools/tracetools.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
@@ -47,15 +53,61 @@ template<typename MessageT, typename AllocatorT>
|
||||
class LoanedMessage;
|
||||
|
||||
/// A publisher publishes messages of any type to a topic.
|
||||
/**
|
||||
* MessageT must be a:
|
||||
*
|
||||
* - ROS message type with its own type support (e.g. std_msgs::msgs::String), or a
|
||||
* - rclcpp::TypeAdapter<CustomType, ROSMessageType>
|
||||
* (e.g. rclcpp::TypeAdapter<std::string, std_msgs::msg::String), or a
|
||||
* - custom type that has been setup as the implicit type for a ROS type using
|
||||
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(custom_type, ros_message_type)
|
||||
*
|
||||
* In the case that MessageT is ROS message type (e.g. std_msgs::msg::String),
|
||||
* both PublishedType and ROSMessageType will be that type.
|
||||
* In the case that MessageT is a TypeAdapter<CustomType, ROSMessageType> type
|
||||
* (e.g. TypeAdapter<std::string, std_msgs::msg::String>), PublishedType will
|
||||
* be the custom type, and ROSMessageType will be the ros message type.
|
||||
*
|
||||
* This is achieved because of the "identity specialization" for TypeAdapter,
|
||||
* which returns itself if it is already a TypeAdapter, and the default
|
||||
* specialization which allows ROSMessageType to be void.
|
||||
* \sa rclcpp::TypeAdapter for more details.
|
||||
*/
|
||||
template<typename MessageT, typename AllocatorT = std::allocator<void>>
|
||||
class Publisher : public PublisherBase
|
||||
{
|
||||
public:
|
||||
using MessageAllocatorTraits = allocator::AllocRebind<MessageT, AllocatorT>;
|
||||
using MessageAllocator = typename MessageAllocatorTraits::allocator_type;
|
||||
using MessageDeleter = allocator::Deleter<MessageAllocator, MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
|
||||
using MessageSharedPtr = std::shared_ptr<const MessageT>;
|
||||
static_assert(
|
||||
rclcpp::is_ros_compatible_type<MessageT>::value,
|
||||
"given message type is not compatible with ROS and cannot be used with a Publisher");
|
||||
|
||||
/// MessageT::custom_type if MessageT is a TypeAdapter, otherwise just MessageT.
|
||||
using PublishedType = typename rclcpp::TypeAdapter<MessageT>::custom_type;
|
||||
using ROSMessageType = typename rclcpp::TypeAdapter<MessageT>::ros_message_type;
|
||||
|
||||
using PublishedTypeAllocatorTraits = allocator::AllocRebind<PublishedType, AllocatorT>;
|
||||
using PublishedTypeAllocator = typename PublishedTypeAllocatorTraits::allocator_type;
|
||||
using PublishedTypeDeleter = allocator::Deleter<PublishedTypeAllocator, PublishedType>;
|
||||
|
||||
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, AllocatorT>;
|
||||
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
|
||||
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
|
||||
|
||||
using MessageAllocatorTraits
|
||||
[[deprecated("use PublishedTypeAllocatorTraits")]] =
|
||||
PublishedTypeAllocatorTraits;
|
||||
using MessageAllocator
|
||||
[[deprecated("use PublishedTypeAllocator")]] =
|
||||
PublishedTypeAllocator;
|
||||
using MessageDeleter
|
||||
[[deprecated("use PublishedTypeDeleter")]] =
|
||||
PublishedTypeDeleter;
|
||||
using MessageUniquePtr
|
||||
[[deprecated("use std::unique_ptr<PublishedType, PublishedTypeDeleter>")]] =
|
||||
std::unique_ptr<PublishedType, PublishedTypeDeleter>;
|
||||
using MessageSharedPtr
|
||||
[[deprecated("use std::shared_ptr<const PublishedType>")]] =
|
||||
std::shared_ptr<const PublishedType>;
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<MessageT, AllocatorT>)
|
||||
|
||||
@@ -68,7 +120,7 @@ public:
|
||||
* \param[in] node_base NodeBaseInterface pointer that is used in part of the setup.
|
||||
* \param[in] topic Name of the topic to publish to.
|
||||
* \param[in] qos QoS profile for Subcription.
|
||||
* \param[in] options options for the subscription.
|
||||
* \param[in] options Options for the subscription.
|
||||
*/
|
||||
Publisher(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
@@ -78,12 +130,14 @@ public:
|
||||
: PublisherBase(
|
||||
node_base,
|
||||
topic,
|
||||
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
|
||||
rclcpp::get_message_type_support_handle<MessageT>(),
|
||||
options.template to_rcl_publisher_options<MessageT>(qos)),
|
||||
options_(options),
|
||||
message_allocator_(new MessageAllocator(*options.get_allocator().get()))
|
||||
published_type_allocator_(*options.get_allocator()),
|
||||
ros_message_type_allocator_(*options.get_allocator())
|
||||
{
|
||||
allocator::set_allocator_for_deleter(&message_deleter_, message_allocator_.get());
|
||||
allocator::set_allocator_for_deleter(&published_type_deleter_, &published_type_allocator_);
|
||||
allocator::set_allocator_for_deleter(&ros_message_type_deleter_, &ros_message_type_allocator_);
|
||||
|
||||
if (options_.event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
@@ -133,15 +187,15 @@ public:
|
||||
// Get the intra process manager instance for this context.
|
||||
auto ipm = context->get_sub_context<rclcpp::experimental::IntraProcessManager>();
|
||||
// Register the publisher with the intra process manager.
|
||||
if (qos.get_rmw_qos_profile().history == RMW_QOS_POLICY_HISTORY_KEEP_ALL) {
|
||||
if (qos.history() != rclcpp::HistoryPolicy::KeepLast) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with keep all history qos policy");
|
||||
"intraprocess communication allowed only with keep last history qos policy");
|
||||
}
|
||||
if (qos.get_rmw_qos_profile().depth == 0) {
|
||||
if (qos.depth() == 0) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with a zero qos history depth value");
|
||||
}
|
||||
if (qos.get_rmw_qos_profile().durability != RMW_QOS_POLICY_DURABILITY_VOLATILE) {
|
||||
if (qos.durability() != rclcpp::DurabilityPolicy::Volatile) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication allowed only with volatile durability");
|
||||
}
|
||||
@@ -168,21 +222,33 @@ public:
|
||||
* allocator.
|
||||
* \sa rclcpp::LoanedMessage for details of the LoanedMessage class.
|
||||
*
|
||||
* \return LoanedMessage containing memory for a ROS message of type MessageT
|
||||
* \return LoanedMessage containing memory for a ROS message of type ROSMessageType
|
||||
*/
|
||||
rclcpp::LoanedMessage<MessageT, AllocatorT>
|
||||
rclcpp::LoanedMessage<ROSMessageType, AllocatorT>
|
||||
borrow_loaned_message()
|
||||
{
|
||||
return rclcpp::LoanedMessage<MessageT, AllocatorT>(this, this->get_allocator());
|
||||
return rclcpp::LoanedMessage<ROSMessageType, AllocatorT>(
|
||||
*this,
|
||||
this->get_ros_message_type_allocator());
|
||||
}
|
||||
|
||||
/// Send a message to the topic for this publisher.
|
||||
/// Publish a message on the topic.
|
||||
/**
|
||||
* This function is templated on the input message type, MessageT.
|
||||
* \param[in] msg A shared pointer to the message to send.
|
||||
* This signature is enabled if the element_type of the std::unique_ptr is
|
||||
* a ROS message type, as opposed to the custom_type of a TypeAdapter, and
|
||||
* that type matches the type given when creating the publisher.
|
||||
*
|
||||
* This signature allows the user to give ownership of the message to rclcpp,
|
||||
* allowing for more efficient intra-process communication optimizations.
|
||||
*
|
||||
* \param[in] msg A unique pointer to the message to send.
|
||||
*/
|
||||
virtual void
|
||||
publish(std::unique_ptr<MessageT, MessageDeleter> msg)
|
||||
template<typename T>
|
||||
typename std::enable_if_t<
|
||||
rosidl_generator_traits::is_message<T>::value &&
|
||||
std::is_same<T, ROSMessageType>::value
|
||||
>
|
||||
publish(std::unique_ptr<T, ROSMessageTypeDeleter> msg)
|
||||
{
|
||||
if (!intra_process_is_enabled_) {
|
||||
this->do_inter_process_publish(*msg);
|
||||
@@ -198,15 +264,32 @@ public:
|
||||
get_subscription_count() > get_intra_process_subscription_count();
|
||||
|
||||
if (inter_process_publish_needed) {
|
||||
auto shared_msg = this->do_intra_process_publish_and_return_shared(std::move(msg));
|
||||
auto shared_msg =
|
||||
this->do_intra_process_ros_message_publish_and_return_shared(std::move(msg));
|
||||
this->do_inter_process_publish(*shared_msg);
|
||||
} else {
|
||||
this->do_intra_process_publish(std::move(msg));
|
||||
this->do_intra_process_ros_message_publish(std::move(msg));
|
||||
}
|
||||
}
|
||||
|
||||
virtual void
|
||||
publish(const MessageT & msg)
|
||||
/// Publish a message on the topic.
|
||||
/**
|
||||
* This signature is enabled if the object being published is
|
||||
* a ROS message type, as opposed to the custom_type of a TypeAdapter, and
|
||||
* that type matches the type given when creating the publisher.
|
||||
*
|
||||
* This signature allows the user to give a reference to a message, which is
|
||||
* copied onto the heap without modification so that a copy can be owned by
|
||||
* rclcpp and ownership of the copy can be moved later if needed.
|
||||
*
|
||||
* \param[in] msg A const reference to the message to send.
|
||||
*/
|
||||
template<typename T>
|
||||
typename std::enable_if_t<
|
||||
rosidl_generator_traits::is_message<T>::value &&
|
||||
std::is_same<T, ROSMessageType>::value
|
||||
>
|
||||
publish(const T & msg)
|
||||
{
|
||||
// Avoid allocating when not using intra process.
|
||||
if (!intra_process_is_enabled_) {
|
||||
@@ -216,9 +299,83 @@ public:
|
||||
// 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 = MessageAllocatorTraits::allocate(*message_allocator_.get(), 1);
|
||||
MessageAllocatorTraits::construct(*message_allocator_.get(), ptr, msg);
|
||||
MessageUniquePtr unique_msg(ptr, message_deleter_);
|
||||
auto unique_msg = this->duplicate_ros_message_as_unique_ptr(msg);
|
||||
this->publish(std::move(unique_msg));
|
||||
}
|
||||
|
||||
/// Publish a message on the topic.
|
||||
/**
|
||||
* This signature is enabled if this class was created with a TypeAdapter and
|
||||
* the element_type of the std::unique_ptr matches the custom_type for the
|
||||
* TypeAdapter used with this class.
|
||||
*
|
||||
* This signature allows the user to give ownership of the message to rclcpp,
|
||||
* allowing for more efficient intra-process communication optimizations.
|
||||
*
|
||||
* \param[in] msg A unique pointer to the message to send.
|
||||
*/
|
||||
template<typename T>
|
||||
typename std::enable_if_t<
|
||||
rclcpp::TypeAdapter<MessageT>::is_specialized::value &&
|
||||
std::is_same<T, PublishedType>::value
|
||||
>
|
||||
publish(std::unique_ptr<T, PublishedTypeDeleter> msg)
|
||||
{
|
||||
// Avoid allocating when not using intra process.
|
||||
if (!intra_process_is_enabled_) {
|
||||
// In this case we're not using intra process.
|
||||
ROSMessageType ros_msg;
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*msg, ros_msg);
|
||||
return this->do_inter_process_publish(ros_msg);
|
||||
}
|
||||
|
||||
bool inter_process_publish_needed =
|
||||
get_subscription_count() > get_intra_process_subscription_count();
|
||||
|
||||
if (inter_process_publish_needed) {
|
||||
ROSMessageType ros_msg;
|
||||
// TODO(clalancette): This is unnecessarily doing an additional conversion
|
||||
// that may have already been done in do_intra_process_publish_and_return_shared().
|
||||
// We should just reuse that effort.
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*msg, ros_msg);
|
||||
this->do_intra_process_publish(std::move(msg));
|
||||
this->do_inter_process_publish(ros_msg);
|
||||
} else {
|
||||
this->do_intra_process_publish(std::move(msg));
|
||||
}
|
||||
}
|
||||
|
||||
/// Publish a message on the topic.
|
||||
/**
|
||||
* This signature is enabled if this class was created with a TypeAdapter and
|
||||
* the given type matches the custom_type of the TypeAdapter.
|
||||
*
|
||||
* This signature allows the user to give a reference to a message, which is
|
||||
* copied onto the heap without modification so that a copy can be owned by
|
||||
* rclcpp and ownership of the copy can be moved later if needed.
|
||||
*
|
||||
* \param[in] msg A const reference to the message to send.
|
||||
*/
|
||||
template<typename T>
|
||||
typename std::enable_if_t<
|
||||
rclcpp::TypeAdapter<MessageT>::is_specialized::value &&
|
||||
std::is_same<T, PublishedType>::value
|
||||
>
|
||||
publish(const T & msg)
|
||||
{
|
||||
// Avoid double allocating when not using intra process.
|
||||
if (!intra_process_is_enabled_) {
|
||||
// Convert to the ROS message equivalent and publish it.
|
||||
ROSMessageType ros_msg;
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(msg, ros_msg);
|
||||
// In this case we're not using intra process.
|
||||
return this->do_inter_process_publish(ros_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 unique_msg = this->duplicate_type_adapt_message_as_unique_ptr(msg);
|
||||
this->publish(std::move(unique_msg));
|
||||
}
|
||||
|
||||
@@ -243,7 +400,7 @@ public:
|
||||
* \param loaned_msg The LoanedMessage instance to be published.
|
||||
*/
|
||||
void
|
||||
publish(rclcpp::LoanedMessage<MessageT, AllocatorT> && loaned_msg)
|
||||
publish(rclcpp::LoanedMessage<ROSMessageType, AllocatorT> && loaned_msg)
|
||||
{
|
||||
if (!loaned_msg.is_valid()) {
|
||||
throw std::runtime_error("loaned message is not valid");
|
||||
@@ -261,7 +418,7 @@ public:
|
||||
if (this->can_loan_messages()) {
|
||||
// we release the ownership from the rclpp::LoanedMessage instance
|
||||
// and let the middleware clean up the memory.
|
||||
this->do_loaned_message_publish(loaned_msg.release());
|
||||
this->do_loaned_message_publish(std::move(loaned_msg.release()));
|
||||
} else {
|
||||
// we don't release the ownership, let the middleware copy the ros message
|
||||
// and thus the destructor of rclcpp::LoanedMessage cleans up the memory.
|
||||
@@ -269,16 +426,30 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<MessageAllocator>
|
||||
[[deprecated("use get_published_type_allocator() or get_ros_message_type_allocator() instead")]]
|
||||
std::shared_ptr<PublishedTypeAllocator>
|
||||
get_allocator() const
|
||||
{
|
||||
return message_allocator_;
|
||||
return std::make_shared<PublishedTypeAllocator>(published_type_allocator_);
|
||||
}
|
||||
|
||||
PublishedTypeAllocator
|
||||
get_published_type_allocator() const
|
||||
{
|
||||
return published_type_allocator_;
|
||||
}
|
||||
|
||||
ROSMessageTypeAllocator
|
||||
get_ros_message_type_allocator() const
|
||||
{
|
||||
return ros_message_type_allocator_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void
|
||||
do_inter_process_publish(const MessageT & msg)
|
||||
do_inter_process_publish(const ROSMessageType & msg)
|
||||
{
|
||||
TRACEPOINT(rclcpp_publish, nullptr, static_cast<const void *>(&msg));
|
||||
auto status = rcl_publish(publisher_handle_.get(), &msg, nullptr);
|
||||
|
||||
if (RCL_RET_PUBLISHER_INVALID == status) {
|
||||
@@ -310,9 +481,10 @@ protected:
|
||||
}
|
||||
|
||||
void
|
||||
do_loaned_message_publish(MessageT * msg)
|
||||
do_loaned_message_publish(
|
||||
std::unique_ptr<ROSMessageType, std::function<void(ROSMessageType *)>> msg)
|
||||
{
|
||||
auto status = rcl_publish_loaned_message(publisher_handle_.get(), msg, nullptr);
|
||||
auto status = rcl_publish_loaned_message(publisher_handle_.get(), msg.get(), nullptr);
|
||||
|
||||
if (RCL_RET_PUBLISHER_INVALID == status) {
|
||||
rcl_reset_error(); // next call will reset error message if not context
|
||||
@@ -330,7 +502,7 @@ protected:
|
||||
}
|
||||
|
||||
void
|
||||
do_intra_process_publish(std::unique_ptr<MessageT, MessageDeleter> msg)
|
||||
do_intra_process_publish(std::unique_ptr<PublishedType, PublishedTypeDeleter> msg)
|
||||
{
|
||||
auto ipm = weak_ipm_.lock();
|
||||
if (!ipm) {
|
||||
@@ -341,14 +513,14 @@ protected:
|
||||
throw std::runtime_error("cannot publish msg which is a null pointer");
|
||||
}
|
||||
|
||||
ipm->template do_intra_process_publish<MessageT, AllocatorT>(
|
||||
ipm->template do_intra_process_publish<PublishedType, ROSMessageType, AllocatorT>(
|
||||
intra_process_publisher_id_,
|
||||
std::move(msg),
|
||||
message_allocator_);
|
||||
published_type_allocator_);
|
||||
}
|
||||
|
||||
std::shared_ptr<const MessageT>
|
||||
do_intra_process_publish_and_return_shared(std::unique_ptr<MessageT, MessageDeleter> msg)
|
||||
void
|
||||
do_intra_process_ros_message_publish(std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> msg)
|
||||
{
|
||||
auto ipm = weak_ipm_.lock();
|
||||
if (!ipm) {
|
||||
@@ -359,10 +531,58 @@ protected:
|
||||
throw std::runtime_error("cannot publish msg which is a null pointer");
|
||||
}
|
||||
|
||||
return ipm->template do_intra_process_publish_and_return_shared<MessageT, AllocatorT>(
|
||||
ipm->template do_intra_process_publish<ROSMessageType, ROSMessageType, AllocatorT>(
|
||||
intra_process_publisher_id_,
|
||||
std::move(msg),
|
||||
message_allocator_);
|
||||
ros_message_type_allocator_);
|
||||
}
|
||||
|
||||
std::shared_ptr<const ROSMessageType>
|
||||
do_intra_process_ros_message_publish_and_return_shared(
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> 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 publish msg which is a null pointer");
|
||||
}
|
||||
|
||||
return ipm->template do_intra_process_publish_and_return_shared<ROSMessageType, ROSMessageType,
|
||||
AllocatorT>(
|
||||
intra_process_publisher_id_,
|
||||
std::move(msg),
|
||||
ros_message_type_allocator_);
|
||||
}
|
||||
|
||||
|
||||
/// Return a new unique_ptr using the ROSMessageType of the publisher.
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>
|
||||
create_ros_message_unique_ptr()
|
||||
{
|
||||
auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_type_allocator_, 1);
|
||||
ROSMessageTypeAllocatorTraits::construct(ros_message_type_allocator_, ptr);
|
||||
return std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, ros_message_type_deleter_);
|
||||
}
|
||||
|
||||
/// Duplicate a given ros message as a unique_ptr.
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>
|
||||
duplicate_ros_message_as_unique_ptr(const ROSMessageType & msg)
|
||||
{
|
||||
auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_type_allocator_, 1);
|
||||
ROSMessageTypeAllocatorTraits::construct(ros_message_type_allocator_, ptr, msg);
|
||||
return std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, ros_message_type_deleter_);
|
||||
}
|
||||
|
||||
/// Duplicate a given type adapted message as a unique_ptr.
|
||||
std::unique_ptr<PublishedType, PublishedTypeDeleter>
|
||||
duplicate_type_adapt_message_as_unique_ptr(const PublishedType & msg)
|
||||
{
|
||||
auto ptr = PublishedTypeAllocatorTraits::allocate(published_type_allocator_, 1);
|
||||
PublishedTypeAllocatorTraits::construct(published_type_allocator_, ptr, msg);
|
||||
return std::unique_ptr<PublishedType, PublishedTypeDeleter>(ptr, published_type_deleter_);
|
||||
}
|
||||
|
||||
/// Copy of original options passed during construction.
|
||||
@@ -372,9 +592,10 @@ protected:
|
||||
*/
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> options_;
|
||||
|
||||
std::shared_ptr<MessageAllocator> message_allocator_;
|
||||
|
||||
MessageDeleter message_deleter_;
|
||||
PublishedTypeAllocator published_type_allocator_;
|
||||
PublishedTypeDeleter published_type_deleter_;
|
||||
ROSMessageTypeAllocator ros_message_type_allocator_;
|
||||
ROSMessageTypeDeleter ros_message_type_deleter_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -18,20 +18,25 @@
|
||||
#include <rmw/error_handling.h>
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/publisher.h"
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/network_flow_endpoint.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/qos_event.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rcpputils/time.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -109,9 +114,10 @@ public:
|
||||
get_publisher_handle() const;
|
||||
|
||||
/// Get all the QoS event handlers associated with this publisher.
|
||||
/** \return The vector of QoS event handlers. */
|
||||
/** \return The map of QoS event handlers. */
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
|
||||
const
|
||||
std::unordered_map<rcl_publisher_event_type_t, std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
|
||||
get_event_handlers() const;
|
||||
|
||||
/// Get subscription count
|
||||
@@ -193,6 +199,120 @@ public:
|
||||
uint64_t intra_process_publisher_id,
|
||||
IntraProcessManagerSharedPtr ipm);
|
||||
|
||||
/// Get network flow endpoints
|
||||
/**
|
||||
* Describes network flow endpoints that this publisher is sending messages out on
|
||||
* \return vector of NetworkFlowEndpoint
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::NetworkFlowEndpoint>
|
||||
get_network_flow_endpoints() const;
|
||||
|
||||
/// Wait until all published messages are acknowledged or until the specified timeout elapses.
|
||||
/**
|
||||
* This method waits until all published messages are acknowledged by all matching
|
||||
* subscriptions or the given timeout elapses.
|
||||
*
|
||||
* If the timeout is negative then this method will block indefinitely until all published
|
||||
* messages are acknowledged.
|
||||
* If the timeout is zero then this method will not block, it will check if all published
|
||||
* messages are acknowledged and return immediately.
|
||||
* If the timeout is greater than zero, this method will wait until all published messages are
|
||||
* acknowledged or the timeout elapses.
|
||||
*
|
||||
* This method only waits for acknowledgments if the publisher's QoS profile is RELIABLE.
|
||||
* Otherwise this method will immediately return `true`.
|
||||
*
|
||||
* \param[in] timeout the duration to wait for all published messages to be acknowledged.
|
||||
* \return `true` if all published messages were acknowledged before the given timeout
|
||||
* elapsed, otherwise `false`.
|
||||
* \throws rclcpp::exceptions::RCLError if middleware doesn't support or internal error occurs
|
||||
* \throws std::invalid_argument if timeout is greater than std::chrono::nanoseconds::max() or
|
||||
* less than std::chrono::nanoseconds::min()
|
||||
*/
|
||||
template<typename DurationRepT = int64_t, typename DurationT = std::milli>
|
||||
bool
|
||||
wait_for_all_acked(
|
||||
std::chrono::duration<DurationRepT, DurationT> timeout =
|
||||
std::chrono::duration<DurationRepT, DurationT>(-1)) const
|
||||
{
|
||||
rcl_duration_value_t rcl_timeout = rcpputils::convert_to_nanoseconds(timeout).count();
|
||||
|
||||
rcl_ret_t ret = rcl_publisher_wait_for_all_acked(publisher_handle_.get(), rcl_timeout);
|
||||
if (ret == RCL_RET_OK) {
|
||||
return true;
|
||||
} else if (ret == RCL_RET_TIMEOUT) {
|
||||
return false;
|
||||
} else {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a callback to be called when each new qos event instance occurs.
|
||||
/**
|
||||
* The callback receives a size_t which is the number of events that occurred
|
||||
* since the last time this callback was called.
|
||||
* Normally this is 1, but can be > 1 if events occurred before any
|
||||
* callback was set.
|
||||
*
|
||||
* Since this callback is called from the middleware, you should aim to make
|
||||
* it fast and not blocking.
|
||||
* If you need to do a lot of work or wait for some other event, you should
|
||||
* spin it off to another thread, otherwise you risk blocking the middleware.
|
||||
*
|
||||
* Calling it again will clear any previously set callback.
|
||||
*
|
||||
* An exception will be thrown if the callback is not callable.
|
||||
*
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* If you want more information available in the callback, like the qos event
|
||||
* or other information, you may use a lambda with captures or std::bind.
|
||||
*
|
||||
* \sa rclcpp::QOSEventHandlerBase::set_on_ready_callback
|
||||
*
|
||||
* \param[in] callback functor to be called when a new event occurs
|
||||
* \param[in] event_type identifier for the qos event we want to attach the callback to
|
||||
*/
|
||||
void
|
||||
set_on_new_qos_event_callback(
|
||||
std::function<void(size_t)> callback,
|
||||
rcl_publisher_event_type_t event_type)
|
||||
{
|
||||
if (event_handlers_.count(event_type) == 0) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Calling set_on_new_qos_event_callback for non registered publisher event_type");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!callback) {
|
||||
throw std::invalid_argument(
|
||||
"The callback passed to set_on_new_qos_event_callback "
|
||||
"is not callable.");
|
||||
}
|
||||
|
||||
// The on_ready_callback signature has an extra `int` argument used to disambiguate between
|
||||
// possible different entities within a generic waitable.
|
||||
// We hide that detail to users of this method.
|
||||
std::function<void(size_t, int)> new_callback = std::bind(callback, std::placeholders::_1);
|
||||
event_handlers_[event_type]->set_on_ready_callback(new_callback);
|
||||
}
|
||||
|
||||
/// Unset the callback registered for new qos events, if any.
|
||||
void
|
||||
clear_on_new_qos_event_callback(rcl_publisher_event_type_t event_type)
|
||||
{
|
||||
if (event_handlers_.count(event_type) == 0) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Calling clear_on_new_qos_event_callback for non registered event_type");
|
||||
return;
|
||||
}
|
||||
|
||||
event_handlers_[event_type]->clear_on_ready_callback();
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename EventCallbackT>
|
||||
void
|
||||
@@ -206,7 +326,7 @@ protected:
|
||||
rcl_publisher_event_init,
|
||||
publisher_handle_,
|
||||
event_type);
|
||||
event_handlers_.emplace_back(handler);
|
||||
event_handlers_.insert(std::make_pair(event_type, handler));
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
@@ -216,7 +336,8 @@ protected:
|
||||
|
||||
std::shared_ptr<rcl_publisher_t> publisher_handle_;
|
||||
|
||||
std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;
|
||||
std::unordered_map<rcl_publisher_event_type_t,
|
||||
std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;
|
||||
|
||||
using IntraProcessManagerWeakPtr =
|
||||
std::weak_ptr<rclcpp::experimental::IntraProcessManager>;
|
||||
@@ -225,6 +346,8 @@ protected:
|
||||
uint64_t intra_process_publisher_id_;
|
||||
|
||||
rmw_gid_t rmw_gid_;
|
||||
|
||||
const rosidl_message_type_support_t type_support_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/publisher.h"
|
||||
@@ -26,6 +27,7 @@
|
||||
#include "rclcpp/intra_process_setting.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/qos_event.hpp"
|
||||
#include "rclcpp/qos_overriding_options.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -44,22 +46,33 @@ struct PublisherOptionsBase
|
||||
/// Whether or not to use default callbacks when user doesn't supply any in event_callbacks
|
||||
bool use_default_callbacks = true;
|
||||
|
||||
/// Require middleware to generate unique network flow endpoints
|
||||
/// Disabled by default
|
||||
rmw_unique_network_flow_endpoints_requirement_t require_unique_network_flow_endpoints =
|
||||
RMW_UNIQUE_NETWORK_FLOW_ENDPOINTS_NOT_REQUIRED;
|
||||
|
||||
/// Callback group in which the waitable items from the publisher should be placed.
|
||||
std::shared_ptr<rclcpp::CallbackGroup> callback_group;
|
||||
|
||||
/// Optional RMW implementation specific payload to be used during creation of the publisher.
|
||||
std::shared_ptr<rclcpp::detail::RMWImplementationSpecificPublisherPayload>
|
||||
rmw_implementation_payload = nullptr;
|
||||
|
||||
QosOverridingOptions qos_overriding_options;
|
||||
};
|
||||
|
||||
/// Structure containing optional configuration for Publishers.
|
||||
template<typename Allocator>
|
||||
struct PublisherOptionsWithAllocator : public PublisherOptionsBase
|
||||
{
|
||||
static_assert(
|
||||
std::is_void_v<typename std::allocator_traits<Allocator>::value_type>,
|
||||
"Publisher allocator value type must be void");
|
||||
|
||||
/// Optional custom allocator.
|
||||
std::shared_ptr<Allocator> allocator = nullptr;
|
||||
|
||||
PublisherOptionsWithAllocator<Allocator>() {}
|
||||
PublisherOptionsWithAllocator() {}
|
||||
|
||||
/// Constructor using base class as input.
|
||||
explicit PublisherOptionsWithAllocator(const PublisherOptionsBase & publisher_options_base)
|
||||
@@ -72,11 +85,10 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase
|
||||
to_rcl_publisher_options(const rclcpp::QoS & qos) const
|
||||
{
|
||||
rcl_publisher_options_t result = rcl_publisher_get_default_options();
|
||||
using AllocatorTraits = std::allocator_traits<Allocator>;
|
||||
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
|
||||
auto message_alloc = std::make_shared<MessageAllocatorT>(*this->get_allocator().get());
|
||||
result.allocator = rclcpp::allocator::get_rcl_allocator<MessageT>(*message_alloc);
|
||||
result.allocator = this->get_rcl_allocator();
|
||||
result.qos = qos.get_rmw_qos_profile();
|
||||
result.rmw_publisher_options.require_unique_network_flow_endpoints =
|
||||
this->require_unique_network_flow_endpoints;
|
||||
|
||||
// Apply payload to rcl_publisher_options if necessary.
|
||||
if (rmw_implementation_payload && rmw_implementation_payload->has_been_customized()) {
|
||||
@@ -92,15 +104,35 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase
|
||||
get_allocator() const
|
||||
{
|
||||
if (!this->allocator) {
|
||||
// TODO(wjwwood): I would like to use the commented line instead, but
|
||||
// cppcheck 1.89 fails with:
|
||||
// Syntax Error: AST broken, binary operator '>' doesn't have two operands.
|
||||
// return std::make_shared<Allocator>();
|
||||
std::shared_ptr<Allocator> tmp(new Allocator());
|
||||
return tmp;
|
||||
if (!allocator_storage_) {
|
||||
allocator_storage_ = std::make_shared<Allocator>();
|
||||
}
|
||||
return allocator_storage_;
|
||||
}
|
||||
return this->allocator;
|
||||
}
|
||||
|
||||
private:
|
||||
using PlainAllocator =
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc<char>;
|
||||
|
||||
rcl_allocator_t
|
||||
get_rcl_allocator() const
|
||||
{
|
||||
if (!plain_allocator_storage_) {
|
||||
plain_allocator_storage_ =
|
||||
std::make_shared<PlainAllocator>(*this->get_allocator());
|
||||
}
|
||||
return rclcpp::allocator::get_rcl_allocator<char>(*plain_allocator_storage_);
|
||||
}
|
||||
|
||||
// This is a temporal workaround, to make sure that get_allocator()
|
||||
// always returns a copy of the same allocator.
|
||||
mutable std::shared_ptr<Allocator> allocator_storage_;
|
||||
|
||||
// This is a temporal workaround, to keep the plain allocator that backs
|
||||
// up the rcl allocator returned in rcl_publisher_options_t alive.
|
||||
mutable std::shared_ptr<PlainAllocator> plain_allocator_storage_;
|
||||
};
|
||||
|
||||
using PublisherOptions = PublisherOptionsWithAllocator<std::allocator<void>>;
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/duration.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rcl/logging_rosout.h"
|
||||
#include "rmw/incompatible_qos_events_statuses.h"
|
||||
#include "rmw/qos_profiles.h"
|
||||
#include "rmw/types.h"
|
||||
@@ -26,8 +28,48 @@
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::string qos_policy_name_from_kind(rmw_qos_policy_kind_t policy_kind);
|
||||
|
||||
enum class HistoryPolicy
|
||||
{
|
||||
KeepLast = RMW_QOS_POLICY_HISTORY_KEEP_LAST,
|
||||
KeepAll = RMW_QOS_POLICY_HISTORY_KEEP_ALL,
|
||||
SystemDefault = RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT,
|
||||
Unknown = RMW_QOS_POLICY_HISTORY_UNKNOWN,
|
||||
};
|
||||
|
||||
enum class ReliabilityPolicy
|
||||
{
|
||||
BestEffort = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT,
|
||||
Reliable = RMW_QOS_POLICY_RELIABILITY_RELIABLE,
|
||||
SystemDefault = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT,
|
||||
Unknown = RMW_QOS_POLICY_RELIABILITY_UNKNOWN,
|
||||
};
|
||||
|
||||
enum class DurabilityPolicy
|
||||
{
|
||||
Volatile = RMW_QOS_POLICY_DURABILITY_VOLATILE,
|
||||
TransientLocal = RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL,
|
||||
SystemDefault = RMW_QOS_POLICY_DURABILITY_SYSTEM_DEFAULT,
|
||||
Unknown = RMW_QOS_POLICY_DURABILITY_UNKNOWN,
|
||||
};
|
||||
|
||||
enum class LivelinessPolicy
|
||||
{
|
||||
Automatic = RMW_QOS_POLICY_LIVELINESS_AUTOMATIC,
|
||||
ManualByTopic = RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC,
|
||||
SystemDefault = RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT,
|
||||
Unknown = RMW_QOS_POLICY_LIVELINESS_UNKNOWN,
|
||||
};
|
||||
|
||||
enum class QoSCompatibility
|
||||
{
|
||||
Ok = RMW_QOS_COMPATIBILITY_OK,
|
||||
Warning = RMW_QOS_COMPATIBILITY_WARNING,
|
||||
Error = RMW_QOS_COMPATIBILITY_ERROR,
|
||||
};
|
||||
|
||||
/// QoS initialization values, cannot be created directly, use KeepAll or KeepLast instead.
|
||||
struct RCLCPP_PUBLIC QoSInitialization
|
||||
{
|
||||
@@ -56,10 +98,32 @@ struct RCLCPP_PUBLIC KeepLast : public rclcpp::QoSInitialization
|
||||
};
|
||||
|
||||
/// Encapsulation of Quality of Service settings.
|
||||
/**
|
||||
* Quality of Service settings control the behavior of publishers, subscriptions,
|
||||
* and other entities, and includes things like how data is sent or resent,
|
||||
* how data is buffered on the publishing and subscribing side, and other things.
|
||||
* See:
|
||||
* <a href="https://docs.ros.org/en/rolling/Concepts/About-Quality-of-Service-Settings.html">
|
||||
* https://docs.ros.org/en/rolling/Concepts/About-Quality-of-Service-Settings.html
|
||||
* </a>
|
||||
*/
|
||||
class RCLCPP_PUBLIC QoS
|
||||
{
|
||||
public:
|
||||
/// Constructor which allows you to construct a QoS by giving the only required settings.
|
||||
/// Create a QoS by specifying only the history policy and history depth.
|
||||
/**
|
||||
* When using the default initial profile, the defaults will include:
|
||||
*
|
||||
* - \link rclcpp::ReliabilityPolicy::Reliable ReliabilityPolicy::Reliable\endlink
|
||||
* - \link rclcpp::DurabilityPolicy::Volatile DurabilityPolicy::Volatile\endlink
|
||||
*
|
||||
* See rmw_qos_profile_default for a full list of default settings.
|
||||
* If some other rmw_qos_profile_t is passed to initial_profile, then the defaults will derive from
|
||||
* that profile instead.
|
||||
*
|
||||
* \param[in] qos_initialization Specifies history policy and history depth.
|
||||
* \param[in] initial_profile The rmw_qos_profile_t instance on which to base the default settings.
|
||||
*/
|
||||
explicit
|
||||
QoS(
|
||||
const QoSInitialization & qos_initialization,
|
||||
@@ -67,7 +131,11 @@ public:
|
||||
|
||||
/// Conversion constructor to ease construction in the common case of just specifying depth.
|
||||
/**
|
||||
* Convenience constructor, equivalent to QoS(KeepLast(history_depth)).
|
||||
* This is a convenience constructor that calls QoS(KeepLast(history_depth)).
|
||||
*
|
||||
* \param[in] history_depth How many messages can be queued when publishing
|
||||
* with a Publisher, or how many messages can be queued before being replaced
|
||||
* by a Subscription.
|
||||
*/
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
QoS(size_t history_depth); // NOLINT(runtime/explicit): conversion constructor
|
||||
@@ -80,6 +148,10 @@ public:
|
||||
const rmw_qos_profile_t &
|
||||
get_rmw_qos_profile() const;
|
||||
|
||||
/// Set the history policy.
|
||||
QoS &
|
||||
history(HistoryPolicy history);
|
||||
|
||||
/// Set the history policy.
|
||||
QoS &
|
||||
history(rmw_qos_history_policy_t history);
|
||||
@@ -96,6 +168,10 @@ public:
|
||||
QoS &
|
||||
reliability(rmw_qos_reliability_policy_t reliability);
|
||||
|
||||
/// Set the reliability setting.
|
||||
QoS &
|
||||
reliability(ReliabilityPolicy reliability);
|
||||
|
||||
/// Set the reliability setting to reliable.
|
||||
QoS &
|
||||
reliable();
|
||||
@@ -108,6 +184,10 @@ public:
|
||||
QoS &
|
||||
durability(rmw_qos_durability_policy_t durability);
|
||||
|
||||
/// Set the durability setting.
|
||||
QoS &
|
||||
durability(DurabilityPolicy durability);
|
||||
|
||||
/// Set the durability setting to volatile.
|
||||
/**
|
||||
* Note that this cannot be named `volatile` because it is a C++ keyword.
|
||||
@@ -139,6 +219,10 @@ public:
|
||||
QoS &
|
||||
liveliness(rmw_qos_liveliness_policy_t liveliness);
|
||||
|
||||
/// Set the liveliness setting.
|
||||
QoS &
|
||||
liveliness(LivelinessPolicy liveliness);
|
||||
|
||||
/// Set the liveliness_lease_duration setting.
|
||||
QoS &
|
||||
liveliness_lease_duration(rmw_time_t liveliness_lease_duration);
|
||||
@@ -151,6 +235,42 @@ public:
|
||||
QoS &
|
||||
avoid_ros_namespace_conventions(bool avoid_ros_namespace_conventions);
|
||||
|
||||
/// Get the history qos policy.
|
||||
HistoryPolicy
|
||||
history() const;
|
||||
|
||||
/// Get the history depth.
|
||||
size_t
|
||||
depth() const;
|
||||
|
||||
/// Get the reliability policy.
|
||||
ReliabilityPolicy
|
||||
reliability() const;
|
||||
|
||||
/// Get the durability policy.
|
||||
DurabilityPolicy
|
||||
durability() const;
|
||||
|
||||
/// Get the deadline duration setting.
|
||||
rclcpp::Duration
|
||||
deadline() const;
|
||||
|
||||
/// Get the lifespan duration setting.
|
||||
rclcpp::Duration
|
||||
lifespan() const;
|
||||
|
||||
/// Get the liveliness policy.
|
||||
LivelinessPolicy
|
||||
liveliness() const;
|
||||
|
||||
/// Get the liveliness lease duration setting.
|
||||
rclcpp::Duration
|
||||
liveliness_lease_duration() const;
|
||||
|
||||
/// Get the `avoid ros namespace convention` setting.
|
||||
bool
|
||||
avoid_ros_namespace_conventions() const;
|
||||
|
||||
private:
|
||||
rmw_qos_profile_t rmw_qos_profile_;
|
||||
};
|
||||
@@ -161,6 +281,81 @@ bool operator==(const QoS & left, const QoS & right);
|
||||
RCLCPP_PUBLIC
|
||||
bool operator!=(const QoS & left, const QoS & right);
|
||||
|
||||
/// Result type for checking QoS compatibility
|
||||
/**
|
||||
* \see rclcpp::qos_check_compatible()
|
||||
*/
|
||||
struct QoSCheckCompatibleResult
|
||||
{
|
||||
/// Compatibility result.
|
||||
QoSCompatibility compatibility;
|
||||
|
||||
/// Reason for a (possible) incompatibility.
|
||||
/**
|
||||
* Set if compatiblity is QoSCompatibility::Warning or QoSCompatiblity::Error.
|
||||
* Not set if the QoS profiles are compatible.
|
||||
*/
|
||||
std::string reason;
|
||||
};
|
||||
|
||||
/// Check if two QoS profiles are compatible.
|
||||
/**
|
||||
* Two QoS profiles are compatible if a publisher and subcription
|
||||
* using the QoS policies can communicate with each other.
|
||||
*
|
||||
* If any policies have value "system default" or "unknown" then it is possible that
|
||||
* compatiblity cannot be determined.
|
||||
* In this case, the value QoSCompatility::Warning is set as part of
|
||||
* the returned structure.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* ```cpp
|
||||
* rclcpp::QoSCheckCompatibleResult result = rclcpp::qos_check_compatible(
|
||||
* publisher_qos, subscription_qos);
|
||||
* if (rclcpp::QoSCompatibility::Error != result.compatibility) {
|
||||
* // QoS not compatible ...
|
||||
* // result.reason contains info about the incompatibility
|
||||
* } else if (rclcpp::QoSCompatibility::Warning != result.compatibility) {
|
||||
* // QoS may not be compatible ...
|
||||
* // result.reason contains info about the possible incompatibility
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* \param[in] publisher_qos: The QoS profile for a publisher.
|
||||
* \param[in] subscription_qos: The QoS profile for a subscription.
|
||||
* \return Struct with compatiblity set to QoSCompatibility::Ok if the QoS profiles are
|
||||
* compatible, or
|
||||
* \return Struct with compatibility set to QoSCompatibility::Warning if there is a chance
|
||||
* the QoS profiles are not compatible, or
|
||||
* \return Struct with compatibility set to QoSCompatibility::Error if the QoS profiles are
|
||||
* not compatible.
|
||||
* \throws rclcpp::exceptions::QoSCheckCompatibilityException if an unexpected error occurs.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
QoSCheckCompatibleResult
|
||||
qos_check_compatible(const QoS & publisher_qos, const QoS & subscription_qos);
|
||||
|
||||
/**
|
||||
* Clock QoS class
|
||||
* - History: Keep last,
|
||||
* - Depth: 1,
|
||||
* - Reliability: Best effort,
|
||||
* - Durability: Volatile,
|
||||
* - Deadline: Default,
|
||||
* - Lifespan: Default,
|
||||
* - Liveliness: System default,
|
||||
* - Liveliness lease duration: default,
|
||||
* - avoid ros namespace conventions: false
|
||||
*/
|
||||
class RCLCPP_PUBLIC ClockQoS : public QoS
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
ClockQoS(
|
||||
const QoSInitialization & qos_initialization = KeepLast(1));
|
||||
};
|
||||
|
||||
/**
|
||||
* Sensor Data QoS class
|
||||
* - History: Keep last,
|
||||
@@ -249,6 +444,28 @@ public:
|
||||
));
|
||||
};
|
||||
|
||||
/**
|
||||
* Rosout QoS class
|
||||
* - History: Keep last,
|
||||
* - Depth: 1000,
|
||||
* - Reliability: Reliable,
|
||||
* - Durability: TRANSIENT_LOCAL,
|
||||
* - Deadline: Default,
|
||||
* - Lifespan: {10, 0},
|
||||
* - Liveliness: System default,
|
||||
* - Liveliness lease duration: default,
|
||||
* - Avoid ros namespace conventions: false
|
||||
*/
|
||||
class RCLCPP_PUBLIC RosoutQoS : public QoS
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
RosoutQoS(
|
||||
const QoSInitialization & rosout_qos_initialization = (
|
||||
QoSInitialization::from_rmw(rcl_qos_profile_rosout_default)
|
||||
));
|
||||
};
|
||||
|
||||
/**
|
||||
* System defaults QoS class
|
||||
* - History: System default,
|
||||
|
||||
@@ -16,15 +16,22 @@
|
||||
#define RCLCPP__QOS_EVENT_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/event_callback.h"
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
#include "rmw/incompatible_qos_events_statuses.h"
|
||||
|
||||
#include "rcutils/logging_macros.h"
|
||||
|
||||
#include "rclcpp/detail/cpp_callback_trampoline.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -34,6 +41,7 @@ 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 QOSMessageLostInfo = rmw_message_lost_status_t;
|
||||
using QOSOfferedIncompatibleQoSInfo = rmw_offered_qos_incompatible_event_status_t;
|
||||
using QOSRequestedIncompatibleQoSInfo = rmw_requested_qos_incompatible_event_status_t;
|
||||
|
||||
@@ -41,6 +49,7 @@ using QOSDeadlineRequestedCallbackType = std::function<void (QOSDeadlineRequeste
|
||||
using QOSDeadlineOfferedCallbackType = std::function<void (QOSDeadlineOfferedInfo &)>;
|
||||
using QOSLivelinessChangedCallbackType = std::function<void (QOSLivelinessChangedInfo &)>;
|
||||
using QOSLivelinessLostCallbackType = std::function<void (QOSLivelinessLostInfo &)>;
|
||||
using QOSMessageLostCallbackType = std::function<void (QOSMessageLostInfo &)>;
|
||||
using QOSOfferedIncompatibleQoSCallbackType = std::function<void (QOSOfferedIncompatibleQoSInfo &)>;
|
||||
using QOSRequestedIncompatibleQoSCallbackType =
|
||||
std::function<void (QOSRequestedIncompatibleQoSInfo &)>;
|
||||
@@ -59,6 +68,7 @@ struct SubscriptionEventCallbacks
|
||||
QOSDeadlineRequestedCallbackType deadline_callback;
|
||||
QOSLivelinessChangedCallbackType liveliness_callback;
|
||||
QOSRequestedIncompatibleQoSCallbackType incompatible_qos_callback;
|
||||
QOSMessageLostCallbackType message_lost_callback;
|
||||
};
|
||||
|
||||
class UnsupportedEventTypeException : public exceptions::RCLErrorBase, public std::runtime_error
|
||||
@@ -79,6 +89,11 @@ public:
|
||||
class QOSEventHandlerBase : public Waitable
|
||||
{
|
||||
public:
|
||||
enum class EntityType : std::size_t
|
||||
{
|
||||
Event,
|
||||
};
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~QOSEventHandlerBase();
|
||||
|
||||
@@ -89,7 +104,7 @@ public:
|
||||
|
||||
/// Add the Waitable to a wait set.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
void
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set) override;
|
||||
|
||||
/// Check if the Waitable is ready.
|
||||
@@ -97,9 +112,111 @@ public:
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set) override;
|
||||
|
||||
/// Set a callback to be called when each new event instance occurs.
|
||||
/**
|
||||
* The callback receives a size_t which is the number of events that occurred
|
||||
* since the last time this callback was called.
|
||||
* Normally this is 1, but can be > 1 if events occurred before any
|
||||
* callback was set.
|
||||
*
|
||||
* The callback also receives an int identifier argument.
|
||||
* This is needed because a Waitable may be composed of several distinct entities,
|
||||
* such as subscriptions, services, etc.
|
||||
* The application should provide a generic callback function that will be then
|
||||
* forwarded by the waitable to all of its entities.
|
||||
* Before forwarding, a different value for the identifier argument will be
|
||||
* bond to the function.
|
||||
* This implies that the provided callback can use the identifier to behave
|
||||
* differently depending on which entity triggered the waitable to become ready.
|
||||
*
|
||||
* Since this callback is called from the middleware, you should aim to make
|
||||
* it fast and not blocking.
|
||||
* If you need to do a lot of work or wait for some other event, you should
|
||||
* spin it off to another thread, otherwise you risk blocking the middleware.
|
||||
*
|
||||
* Calling it again will clear any previously set callback.
|
||||
*
|
||||
* An exception will be thrown if the callback is not callable.
|
||||
*
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* If you want more information available in the callback, like the qos event
|
||||
* or other information, you may use a lambda with captures or std::bind.
|
||||
*
|
||||
* \sa rmw_event_set_callback
|
||||
* \sa rcl_event_set_callback
|
||||
*
|
||||
* \param[in] callback functor to be called when a new event occurs
|
||||
*/
|
||||
void
|
||||
set_on_ready_callback(std::function<void(size_t, int)> callback) override
|
||||
{
|
||||
if (!callback) {
|
||||
throw std::invalid_argument(
|
||||
"The callback passed to set_on_ready_callback "
|
||||
"is not callable.");
|
||||
}
|
||||
|
||||
// Note: we bind the int identifier argument to this waitable's entity types
|
||||
auto new_callback =
|
||||
[callback, this](size_t number_of_events) {
|
||||
try {
|
||||
callback(number_of_events, static_cast<int>(EntityType::Event));
|
||||
} catch (const std::exception & exception) {
|
||||
RCLCPP_ERROR_STREAM(
|
||||
// TODO(wjwwood): get this class access to the node logger it is associated with
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"rclcpp::QOSEventHandlerBase@" << this <<
|
||||
" caught " << rmw::impl::cpp::demangle(exception) <<
|
||||
" exception in user-provided callback for the 'on ready' callback: " <<
|
||||
exception.what());
|
||||
} catch (...) {
|
||||
RCLCPP_ERROR_STREAM(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"rclcpp::QOSEventHandlerBase@" << this <<
|
||||
" caught unhandled exception in user-provided callback " <<
|
||||
"for the 'on ready' callback");
|
||||
}
|
||||
};
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
|
||||
// Set it temporarily to the new callback, while we replace the old one.
|
||||
// This two-step setting, prevents a gap where the old std::function has
|
||||
// been replaced but the middleware hasn't been told about the new one yet.
|
||||
set_on_new_event_callback(
|
||||
rclcpp::detail::cpp_callback_trampoline<const void *, size_t>,
|
||||
static_cast<const void *>(&new_callback));
|
||||
|
||||
// Store the std::function to keep it in scope, also overwrites the existing one.
|
||||
on_new_event_callback_ = new_callback;
|
||||
|
||||
// Set it again, now using the permanent storage.
|
||||
set_on_new_event_callback(
|
||||
rclcpp::detail::cpp_callback_trampoline<const void *, size_t>,
|
||||
static_cast<const void *>(&on_new_event_callback_));
|
||||
}
|
||||
|
||||
/// Unset the callback registered for new events, if any.
|
||||
void
|
||||
clear_on_ready_callback() override
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
if (on_new_event_callback_) {
|
||||
set_on_new_event_callback(nullptr, nullptr);
|
||||
on_new_event_callback_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_on_new_event_callback(rcl_event_callback_t callback, const void * user_data);
|
||||
|
||||
rcl_event_t event_handle_;
|
||||
size_t wait_set_event_index_;
|
||||
std::recursive_mutex callback_mutex_;
|
||||
std::function<void(size_t)> on_new_event_callback_{nullptr};
|
||||
};
|
||||
|
||||
template<typename EventCallbackT, typename ParentHandleT>
|
||||
@@ -112,9 +229,8 @@ public:
|
||||
InitFuncT init_func,
|
||||
ParentHandleT parent_handle,
|
||||
EventTypeEnum event_type)
|
||||
: event_callback_(callback)
|
||||
: parent_handle_(parent_handle), event_callback_(callback)
|
||||
{
|
||||
parent_handle_ = parent_handle;
|
||||
event_handle_ = rcl_get_zero_initialized_event();
|
||||
rcl_ret_t ret = init_func(&event_handle_, parent_handle.get(), event_type);
|
||||
if (ret != RCL_RET_OK) {
|
||||
@@ -128,21 +244,38 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute any entities of the Waitable that are ready.
|
||||
void
|
||||
execute() override
|
||||
/// Take data so that the callback cannot be scheduled again
|
||||
std::shared_ptr<void>
|
||||
take_data() 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;
|
||||
return nullptr;
|
||||
}
|
||||
return std::static_pointer_cast<void>(std::make_shared<EventCallbackInfoT>(callback_info));
|
||||
}
|
||||
|
||||
event_callback_(callback_info);
|
||||
std::shared_ptr<void>
|
||||
take_data_by_entity_id(size_t id) override
|
||||
{
|
||||
(void)id;
|
||||
return take_data();
|
||||
}
|
||||
|
||||
/// Execute any entities of the Waitable that are ready.
|
||||
void
|
||||
execute(std::shared_ptr<void> & data) override
|
||||
{
|
||||
if (!data) {
|
||||
throw std::runtime_error("'data' is empty");
|
||||
}
|
||||
auto callback_ptr = std::static_pointer_cast<EventCallbackInfoT>(data);
|
||||
event_callback_(*callback_ptr);
|
||||
callback_ptr.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
154
rclcpp/include/rclcpp/qos_overriding_options.hpp
Normal file
154
rclcpp/include/rclcpp/qos_overriding_options.hpp
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2020 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_OVERRIDING_OPTIONS_HPP_
|
||||
#define RCLCPP__QOS_OVERRIDING_OPTIONS_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "rcl_interfaces/msg/set_parameters_result.hpp"
|
||||
#include "rmw/qos_policy_kind.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
enum class RCLCPP_PUBLIC_TYPE QosPolicyKind
|
||||
{
|
||||
AvoidRosNamespaceConventions = RMW_QOS_POLICY_AVOID_ROS_NAMESPACE_CONVENTIONS,
|
||||
Deadline = RMW_QOS_POLICY_DEADLINE,
|
||||
Depth = RMW_QOS_POLICY_DEPTH,
|
||||
Durability = RMW_QOS_POLICY_DURABILITY,
|
||||
History = RMW_QOS_POLICY_HISTORY,
|
||||
Lifespan = RMW_QOS_POLICY_LIFESPAN,
|
||||
Liveliness = RMW_QOS_POLICY_LIVELINESS,
|
||||
LivelinessLeaseDuration = RMW_QOS_POLICY_LIVELINESS_LEASE_DURATION,
|
||||
Reliability = RMW_QOS_POLICY_RELIABILITY,
|
||||
Invalid = RMW_QOS_POLICY_INVALID,
|
||||
};
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const char *
|
||||
qos_policy_kind_to_cstr(const QosPolicyKind & qpk);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::ostream &
|
||||
operator<<(std::ostream & os, const QosPolicyKind & qpk);
|
||||
|
||||
using QosCallbackResult = rcl_interfaces::msg::SetParametersResult;
|
||||
using QosCallback = std::function<QosCallbackResult(const rclcpp::QoS &)>;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// forward declare
|
||||
template<typename T>
|
||||
class QosParameters;
|
||||
}
|
||||
|
||||
/// Options that are passed in subscription/publisher constructor to specify QoSConfigurability.
|
||||
/**
|
||||
* This options struct allows configuring:
|
||||
* - Which policy kinds will have declared parameters.
|
||||
* - An optional callback, that will be called to validate the final qos profile.
|
||||
* - An optional id. In the case that different qos are desired for two publishers/subscriptions in
|
||||
* the same topic, this id will allow disambiguating them.
|
||||
*
|
||||
* Example parameter file:
|
||||
*
|
||||
* ```yaml
|
||||
* my_node_name:
|
||||
* ros__parameters:
|
||||
* qos_overrides:
|
||||
* /my/topic/name:
|
||||
* publisher: # publisher without provided id
|
||||
* reliability: reliable
|
||||
* depth: 100
|
||||
* publisher_my_id: # publisher with `id="my_id"
|
||||
* reliability: reliable
|
||||
* depth: 10
|
||||
* ```
|
||||
*/
|
||||
class QosOverridingOptions
|
||||
{
|
||||
public:
|
||||
/// Default constructor, no overrides allowed.
|
||||
RCLCPP_PUBLIC
|
||||
QosOverridingOptions() = default;
|
||||
|
||||
/// Construct passing a list of QoS policies and a verification callback.
|
||||
/**
|
||||
* This constructor is implicit, e.g.:
|
||||
* ```cpp
|
||||
* node->create_publisher(
|
||||
* "topic_name",
|
||||
* default_qos_profile,
|
||||
* {
|
||||
* {QosPolicyKind::Reliability},
|
||||
* [] (auto && qos) {return check_qos_validity(qos)},
|
||||
* "my_id"
|
||||
* });
|
||||
* ```
|
||||
* \param policy_kinds list of policy kinds that will be reconfigurable.
|
||||
* \param validation_callback callbak that will be called to validate the validity of
|
||||
* the qos profile set by the user.
|
||||
* \param id id of the entity.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
QosOverridingOptions(
|
||||
std::initializer_list<QosPolicyKind> policy_kinds,
|
||||
QosCallback validation_callback = nullptr,
|
||||
std::string id = {});
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::string &
|
||||
get_id() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<QosPolicyKind> &
|
||||
get_policy_kinds() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const QosCallback &
|
||||
get_validation_callback() const;
|
||||
|
||||
/// Construct passing a list of QoS policies and a verification callback.
|
||||
/**
|
||||
* Same as `QosOverridingOptions` constructor, but only declares the default policies:
|
||||
*
|
||||
* History, Depth, Reliability.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
static
|
||||
QosOverridingOptions
|
||||
with_default_policies(QosCallback validation_callback = nullptr, std::string id = {});
|
||||
|
||||
private:
|
||||
/// \internal Id of the entity requesting to create parameters.
|
||||
std::string id_;
|
||||
/// \internal Policy kinds that are allowed to be reconfigured.
|
||||
std::vector<QosPolicyKind> policy_kinds_;
|
||||
/// \internal Validation callback that will be called to verify the profile.
|
||||
QosCallback validation_callback_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__QOS_OVERRIDING_OPTIONS_HPP_
|
||||
@@ -117,6 +117,15 @@
|
||||
* - Allocator related items:
|
||||
* - rclcpp/allocator/allocator_common.hpp
|
||||
* - rclcpp/allocator/allocator_deleter.hpp
|
||||
* - Generic publisher
|
||||
* - rclcpp::Node::create_generic_publisher()
|
||||
* - rclcpp::GenericPublisher
|
||||
* - rclcpp::GenericPublisher::publish()
|
||||
* - rclcpp/generic_publisher.hpp
|
||||
* - Generic subscription
|
||||
* - rclcpp::Node::create_generic_subscription()
|
||||
* - rclcpp::GenericSubscription
|
||||
* - rclcpp/generic_subscription.hpp
|
||||
* - Memory management tools:
|
||||
* - rclcpp/memory_strategies.hpp
|
||||
* - rclcpp/memory_strategy.hpp
|
||||
@@ -131,9 +140,9 @@
|
||||
* - rclcpp/duration.hpp
|
||||
* - rclcpp/function_traits.hpp
|
||||
* - rclcpp/macros.hpp
|
||||
* - rclcpp/scope_exit.hpp
|
||||
* - rclcpp/time.hpp
|
||||
* - rclcpp/utilities.hpp
|
||||
* - rclcpp/typesupport_helpers.hpp
|
||||
* - rclcpp/visibility_control.hpp
|
||||
*/
|
||||
|
||||
@@ -147,14 +156,15 @@
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/parameter_client.hpp"
|
||||
#include "rclcpp/parameter_event_handler.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/parameter_service.hpp"
|
||||
#include "rclcpp/rate.hpp"
|
||||
#include "rclcpp/time.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/wait_set.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
#include "rclcpp/wait_set.hpp"
|
||||
|
||||
#endif // RCLCPP__RCLCPP_HPP_
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
#ifndef RCLCPP__SCOPE_EXIT_HPP_
|
||||
#define RCLCPP__SCOPE_EXIT_HPP_
|
||||
|
||||
// TODO(christophebedard) remove this header completely in I-turtle
|
||||
|
||||
#warning rclcpp/scope_exit.hpp has been deprecated, please use rcpputils/scope_exit.hpp instead
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
|
||||
/// Serialize a ROS2 message to a serialized stream
|
||||
/**
|
||||
* \param[in] message The ROS2 message which is read and serialized by rmw.
|
||||
* \param[in] ros_message The ROS2 message which is read and serialized by rmw.
|
||||
* \param[out] serialized_message The serialized message.
|
||||
*/
|
||||
void serialize_message(
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
/// Deserialize a serialized stream to a ROS message
|
||||
/**
|
||||
* \param[in] serialized_message The serialized message to be converted to ROS2 by rmw.
|
||||
* \param[out] message The deserialized ROS2 message.
|
||||
* \param[out] ros_message The deserialized ROS2 message.
|
||||
*/
|
||||
void deserialize_message(
|
||||
const SerializedMessage * serialized_message, void * ros_message) const;
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
* \param[in] initial_capacity The amount of memory to be allocated.
|
||||
* \param[in] allocator The allocator to be used for the initialization.
|
||||
*/
|
||||
SerializedMessage(
|
||||
explicit SerializedMessage(
|
||||
size_t initial_capacity,
|
||||
const rcl_allocator_t & allocator = rcl_get_default_allocator());
|
||||
|
||||
|
||||
@@ -19,23 +19,30 @@
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/event_callback.h"
|
||||
#include "rcl/service.h"
|
||||
|
||||
#include "rclcpp/any_service_callback.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/expand_topic_or_service_name.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rmw/error_handling.h"
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
#include "tracetools/tracetools.h"
|
||||
|
||||
#include "rclcpp/any_service_callback.hpp"
|
||||
#include "rclcpp/detail/cpp_callback_trampoline.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/expand_topic_or_service_name.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
@@ -121,6 +128,123 @@ public:
|
||||
bool
|
||||
exchange_in_use_by_wait_set_state(bool in_use_state);
|
||||
|
||||
/// Get the actual response publisher 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 service, 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 response publisher qos settings.
|
||||
* \throws std::runtime_error if failed to get qos settings
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::QoS
|
||||
get_response_publisher_actual_qos() const;
|
||||
|
||||
/// Get the actual request subscription 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 service, 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 request subscription qos settings.
|
||||
* \throws std::runtime_error if failed to get qos settings
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::QoS
|
||||
get_request_subscription_actual_qos() const;
|
||||
|
||||
/// Set a callback to be called when each new request is received.
|
||||
/**
|
||||
* The callback receives a size_t which is the number of requests received
|
||||
* since the last time this callback was called.
|
||||
* Normally this is 1, but can be > 1 if requests were received before any
|
||||
* callback was set.
|
||||
*
|
||||
* Since this callback is called from the middleware, you should aim to make
|
||||
* it fast and not blocking.
|
||||
* If you need to do a lot of work or wait for some other event, you should
|
||||
* spin it off to another thread, otherwise you risk blocking the middleware.
|
||||
*
|
||||
* Calling it again will clear any previously set callback.
|
||||
*
|
||||
*
|
||||
* An exception will be thrown if the callback is not callable.
|
||||
*
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* If you want more information available in the callback, like the service
|
||||
* or other information, you may use a lambda with captures or std::bind.
|
||||
*
|
||||
* \sa rmw_service_set_on_new_request_callback
|
||||
* \sa rcl_service_set_on_new_request_callback
|
||||
*
|
||||
* \param[in] callback functor to be called when a new request is received
|
||||
*/
|
||||
void
|
||||
set_on_new_request_callback(std::function<void(size_t)> callback)
|
||||
{
|
||||
if (!callback) {
|
||||
throw std::invalid_argument(
|
||||
"The callback passed to set_on_new_request_callback "
|
||||
"is not callable.");
|
||||
}
|
||||
|
||||
auto new_callback =
|
||||
[callback, this](size_t number_of_requests) {
|
||||
try {
|
||||
callback(number_of_requests);
|
||||
} catch (const std::exception & exception) {
|
||||
RCLCPP_ERROR_STREAM(
|
||||
node_logger_,
|
||||
"rclcpp::ServiceBase@" << this <<
|
||||
" caught " << rmw::impl::cpp::demangle(exception) <<
|
||||
" exception in user-provided callback for the 'on new request' callback: " <<
|
||||
exception.what());
|
||||
} catch (...) {
|
||||
RCLCPP_ERROR_STREAM(
|
||||
node_logger_,
|
||||
"rclcpp::ServiceBase@" << this <<
|
||||
" caught unhandled exception in user-provided callback " <<
|
||||
"for the 'on new request' callback");
|
||||
}
|
||||
};
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
|
||||
// Set it temporarily to the new callback, while we replace the old one.
|
||||
// This two-step setting, prevents a gap where the old std::function has
|
||||
// been replaced but the middleware hasn't been told about the new one yet.
|
||||
set_on_new_request_callback(
|
||||
rclcpp::detail::cpp_callback_trampoline<const void *, size_t>,
|
||||
static_cast<const void *>(&new_callback));
|
||||
|
||||
// Store the std::function to keep it in scope, also overwrites the existing one.
|
||||
on_new_request_callback_ = new_callback;
|
||||
|
||||
// Set it again, now using the permanent storage.
|
||||
set_on_new_request_callback(
|
||||
rclcpp::detail::cpp_callback_trampoline<const void *, size_t>,
|
||||
static_cast<const void *>(&on_new_request_callback_));
|
||||
}
|
||||
|
||||
/// Unset the callback registered for new requests, if any.
|
||||
void
|
||||
clear_on_new_request_callback()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
if (on_new_request_callback_) {
|
||||
set_on_new_request_callback(nullptr, nullptr);
|
||||
on_new_request_callback_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
RCLCPP_DISABLE_COPY(ServiceBase)
|
||||
|
||||
@@ -132,16 +256,27 @@ protected:
|
||||
const rcl_node_t *
|
||||
get_rcl_node_handle() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_on_new_request_callback(rcl_event_callback_t callback, const void * user_data);
|
||||
|
||||
std::shared_ptr<rcl_node_t> node_handle_;
|
||||
|
||||
std::shared_ptr<rcl_service_t> service_handle_;
|
||||
bool owns_rcl_handle_ = true;
|
||||
|
||||
rclcpp::Logger node_logger_;
|
||||
|
||||
std::atomic<bool> in_use_by_wait_set_{false};
|
||||
|
||||
std::recursive_mutex callback_mutex_;
|
||||
std::function<void(size_t)> on_new_request_callback_{nullptr};
|
||||
};
|
||||
|
||||
template<typename ServiceT>
|
||||
class Service : public ServiceBase
|
||||
class Service
|
||||
: public ServiceBase,
|
||||
public std::enable_shared_from_this<Service<ServiceT>>
|
||||
{
|
||||
public:
|
||||
using CallbackType = std::function<
|
||||
@@ -177,25 +312,16 @@ public:
|
||||
using rosidl_typesupport_cpp::get_service_type_support_handle;
|
||||
auto service_type_support_handle = get_service_type_support_handle<ServiceT>();
|
||||
|
||||
std::weak_ptr<rcl_node_t> weak_node_handle(node_handle_);
|
||||
// rcl does the static memory allocation here
|
||||
service_handle_ = std::shared_ptr<rcl_service_t>(
|
||||
new rcl_service_t, [weak_node_handle](rcl_service_t * service)
|
||||
new rcl_service_t, [handle = node_handle_, service_name](rcl_service_t * service)
|
||||
{
|
||||
auto handle = weak_node_handle.lock();
|
||||
if (handle) {
|
||||
if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
|
||||
"Error in destruction of rcl service handle: %s",
|
||||
rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
} else {
|
||||
if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Error in destruction of rcl service handle: "
|
||||
"the Node Handle was destructed too early. You will leak memory");
|
||||
rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
|
||||
"Error in destruction of rcl service handle: %s",
|
||||
rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
delete service;
|
||||
});
|
||||
@@ -223,8 +349,8 @@ public:
|
||||
}
|
||||
TRACEPOINT(
|
||||
rclcpp_service_callback_added,
|
||||
(const void *)get_service_handle().get(),
|
||||
(const void *)&any_callback_);
|
||||
static_cast<const void *>(get_service_handle().get()),
|
||||
static_cast<const void *>(&any_callback_));
|
||||
#ifndef TRACETOOLS_DISABLED
|
||||
any_callback_.register_callback_for_tracing();
|
||||
#endif
|
||||
@@ -237,7 +363,7 @@ public:
|
||||
* rclcpp::create_service().
|
||||
*
|
||||
* \param[in] node_handle NodeBaseInterface pointer that is used in part of the setup.
|
||||
* \param[in] service_name Name of the topic to publish to.
|
||||
* \param[in] service_handle service handle.
|
||||
* \param[in] any_callback User defined callback to call when a client request is received.
|
||||
*/
|
||||
Service(
|
||||
@@ -258,8 +384,8 @@ public:
|
||||
service_handle_ = service_handle;
|
||||
TRACEPOINT(
|
||||
rclcpp_service_callback_added,
|
||||
(const void *)get_service_handle().get(),
|
||||
(const void *)&any_callback_);
|
||||
static_cast<const void *>(get_service_handle().get()),
|
||||
static_cast<const void *>(&any_callback_));
|
||||
#ifndef TRACETOOLS_DISABLED
|
||||
any_callback_.register_callback_for_tracing();
|
||||
#endif
|
||||
@@ -272,7 +398,7 @@ public:
|
||||
* rclcpp::create_service().
|
||||
*
|
||||
* \param[in] node_handle NodeBaseInterface pointer that is used in part of the setup.
|
||||
* \param[in] service_name Name of the topic to publish to.
|
||||
* \param[in] service_handle service handle.
|
||||
* \param[in] any_callback User defined callback to call when a client request is received.
|
||||
*/
|
||||
Service(
|
||||
@@ -295,8 +421,8 @@ public:
|
||||
service_handle_->impl = service_handle->impl;
|
||||
TRACEPOINT(
|
||||
rclcpp_service_callback_added,
|
||||
(const void *)get_service_handle().get(),
|
||||
(const void *)&any_callback_);
|
||||
static_cast<const void *>(get_service_handle().get()),
|
||||
static_cast<const void *>(&any_callback_));
|
||||
#ifndef TRACETOOLS_DISABLED
|
||||
any_callback_.register_callback_for_tracing();
|
||||
#endif
|
||||
@@ -344,18 +470,10 @@ public:
|
||||
std::shared_ptr<void> request) override
|
||||
{
|
||||
auto typed_request = std::static_pointer_cast<typename ServiceT::Request>(request);
|
||||
auto response = std::make_shared<typename ServiceT::Response>();
|
||||
any_callback_.dispatch(request_header, typed_request, response);
|
||||
send_response(*request_header, *response);
|
||||
}
|
||||
|
||||
[[deprecated("use the send_response() which takes references instead of shared pointers")]]
|
||||
void
|
||||
send_response(
|
||||
std::shared_ptr<rmw_request_id_t> req_id,
|
||||
std::shared_ptr<typename ServiceT::Response> response)
|
||||
{
|
||||
send_response(*req_id, *response);
|
||||
auto response = any_callback_.dispatch(this->shared_from_this(), request_header, typed_request);
|
||||
if (response) {
|
||||
send_response(*request_header, *response);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "rcl/allocator.h"
|
||||
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/detail/add_guard_condition_to_rcl_wait_set.hpp"
|
||||
#include "rclcpp/memory_strategy.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
@@ -61,17 +62,17 @@ public:
|
||||
allocator_ = std::make_shared<VoidAlloc>();
|
||||
}
|
||||
|
||||
void add_guard_condition(const rcl_guard_condition_t * guard_condition) override
|
||||
void add_guard_condition(const rclcpp::GuardCondition & guard_condition) override
|
||||
{
|
||||
for (const auto & existing_guard_condition : guard_conditions_) {
|
||||
if (existing_guard_condition == guard_condition) {
|
||||
if (existing_guard_condition == &guard_condition) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
guard_conditions_.push_back(guard_condition);
|
||||
guard_conditions_.push_back(&guard_condition);
|
||||
}
|
||||
|
||||
void remove_guard_condition(const rcl_guard_condition_t * guard_condition) override
|
||||
void remove_guard_condition(const rclcpp::GuardCondition * guard_condition) override
|
||||
{
|
||||
for (auto it = guard_conditions_.begin(); it != guard_conditions_.end(); ++it) {
|
||||
if (*it == guard_condition) {
|
||||
@@ -150,48 +151,39 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
bool collect_entities(const WeakNodeList & weak_nodes) override
|
||||
bool collect_entities(const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
|
||||
{
|
||||
bool has_invalid_weak_nodes = false;
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
has_invalid_weak_nodes = true;
|
||||
bool has_invalid_weak_groups_or_nodes = false;
|
||||
for (const auto & pair : weak_groups_to_nodes) {
|
||||
auto group = pair.first.lock();
|
||||
auto node = pair.second.lock();
|
||||
if (group == nullptr || node == nullptr) {
|
||||
has_invalid_weak_groups_or_nodes = true;
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group || !group->can_be_taken_from().load()) {
|
||||
continue;
|
||||
}
|
||||
group->find_subscription_ptrs_if(
|
||||
[this](const rclcpp::SubscriptionBase::SharedPtr & subscription) {
|
||||
subscription_handles_.push_back(subscription->get_subscription_handle());
|
||||
return false;
|
||||
});
|
||||
group->find_service_ptrs_if(
|
||||
[this](const rclcpp::ServiceBase::SharedPtr & service) {
|
||||
service_handles_.push_back(service->get_service_handle());
|
||||
return false;
|
||||
});
|
||||
group->find_client_ptrs_if(
|
||||
[this](const rclcpp::ClientBase::SharedPtr & client) {
|
||||
client_handles_.push_back(client->get_client_handle());
|
||||
return false;
|
||||
});
|
||||
group->find_timer_ptrs_if(
|
||||
[this](const rclcpp::TimerBase::SharedPtr & timer) {
|
||||
timer_handles_.push_back(timer->get_timer_handle());
|
||||
return false;
|
||||
});
|
||||
group->find_waitable_ptrs_if(
|
||||
[this](const rclcpp::Waitable::SharedPtr & waitable) {
|
||||
waitable_handles_.push_back(waitable);
|
||||
return false;
|
||||
});
|
||||
if (!group || !group->can_be_taken_from().load()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
group->collect_all_ptrs(
|
||||
[this](const rclcpp::SubscriptionBase::SharedPtr & subscription) {
|
||||
subscription_handles_.push_back(subscription->get_subscription_handle());
|
||||
},
|
||||
[this](const rclcpp::ServiceBase::SharedPtr & service) {
|
||||
service_handles_.push_back(service->get_service_handle());
|
||||
},
|
||||
[this](const rclcpp::ClientBase::SharedPtr & client) {
|
||||
client_handles_.push_back(client->get_client_handle());
|
||||
},
|
||||
[this](const rclcpp::TimerBase::SharedPtr & timer) {
|
||||
timer_handles_.push_back(timer->get_timer_handle());
|
||||
},
|
||||
[this](const rclcpp::Waitable::SharedPtr & waitable) {
|
||||
waitable_handles_.push_back(waitable);
|
||||
});
|
||||
}
|
||||
return has_invalid_weak_nodes;
|
||||
|
||||
return has_invalid_weak_groups_or_nodes;
|
||||
}
|
||||
|
||||
void add_waitable_handle(const rclcpp::Waitable::SharedPtr & waitable) override
|
||||
@@ -204,7 +196,7 @@ public:
|
||||
|
||||
bool add_handles_to_wait_set(rcl_wait_set_t * wait_set) override
|
||||
{
|
||||
for (auto subscription : subscription_handles_) {
|
||||
for (const std::shared_ptr<const rcl_subscription_t> & subscription : subscription_handles_) {
|
||||
if (rcl_wait_set_add_subscription(wait_set, subscription.get(), NULL) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
@@ -213,7 +205,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
for (auto client : client_handles_) {
|
||||
for (const std::shared_ptr<const rcl_client_t> & client : client_handles_) {
|
||||
if (rcl_wait_set_add_client(wait_set, client.get(), NULL) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
@@ -222,7 +214,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
for (auto service : service_handles_) {
|
||||
for (const std::shared_ptr<const rcl_service_t> & service : service_handles_) {
|
||||
if (rcl_wait_set_add_service(wait_set, service.get(), NULL) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
@@ -231,7 +223,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
for (auto timer : timer_handles_) {
|
||||
for (const std::shared_ptr<const rcl_timer_t> & timer : timer_handles_) {
|
||||
if (rcl_wait_set_add_timer(wait_set, timer.get(), NULL) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
@@ -241,22 +233,11 @@ public:
|
||||
}
|
||||
|
||||
for (auto guard_condition : guard_conditions_) {
|
||||
if (rcl_wait_set_add_guard_condition(wait_set, guard_condition, NULL) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Couldn't add guard_condition to wait set: %s",
|
||||
rcl_get_error_string().str);
|
||||
return false;
|
||||
}
|
||||
detail::add_guard_condition_to_rcl_wait_set(*wait_set, *guard_condition);
|
||||
}
|
||||
|
||||
for (auto waitable : waitable_handles_) {
|
||||
if (!waitable->add_to_wait_set(wait_set)) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Couldn't add waitable to wait set: %s", rcl_get_error_string().str);
|
||||
return false;
|
||||
}
|
||||
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
|
||||
waitable->add_to_wait_set(wait_set);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -264,14 +245,14 @@ public:
|
||||
void
|
||||
get_next_subscription(
|
||||
rclcpp::AnyExecutable & any_exec,
|
||||
const WeakNodeList & weak_nodes) override
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
|
||||
{
|
||||
auto it = subscription_handles_.begin();
|
||||
while (it != subscription_handles_.end()) {
|
||||
auto subscription = get_subscription_by_handle(*it, weak_nodes);
|
||||
auto subscription = get_subscription_by_handle(*it, weak_groups_to_nodes);
|
||||
if (subscription) {
|
||||
// Find the group for this handle and see if it can be serviced
|
||||
auto group = get_group_by_subscription(subscription, weak_nodes);
|
||||
auto group = get_group_by_subscription(subscription, weak_groups_to_nodes);
|
||||
if (!group) {
|
||||
// Group was not found, meaning the subscription is not valid...
|
||||
// Remove it from the ready list and continue looking
|
||||
@@ -287,7 +268,7 @@ public:
|
||||
// Otherwise it is safe to set and return the any_exec
|
||||
any_exec.subscription = subscription;
|
||||
any_exec.callback_group = group;
|
||||
any_exec.node_base = get_node_by_group(group, weak_nodes);
|
||||
any_exec.node_base = get_node_by_group(group, weak_groups_to_nodes);
|
||||
subscription_handles_.erase(it);
|
||||
return;
|
||||
}
|
||||
@@ -299,14 +280,14 @@ public:
|
||||
void
|
||||
get_next_service(
|
||||
rclcpp::AnyExecutable & any_exec,
|
||||
const WeakNodeList & weak_nodes) override
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
|
||||
{
|
||||
auto it = service_handles_.begin();
|
||||
while (it != service_handles_.end()) {
|
||||
auto service = get_service_by_handle(*it, weak_nodes);
|
||||
auto service = get_service_by_handle(*it, weak_groups_to_nodes);
|
||||
if (service) {
|
||||
// Find the group for this handle and see if it can be serviced
|
||||
auto group = get_group_by_service(service, weak_nodes);
|
||||
auto group = get_group_by_service(service, weak_groups_to_nodes);
|
||||
if (!group) {
|
||||
// Group was not found, meaning the service is not valid...
|
||||
// Remove it from the ready list and continue looking
|
||||
@@ -322,7 +303,7 @@ public:
|
||||
// Otherwise it is safe to set and return the any_exec
|
||||
any_exec.service = service;
|
||||
any_exec.callback_group = group;
|
||||
any_exec.node_base = get_node_by_group(group, weak_nodes);
|
||||
any_exec.node_base = get_node_by_group(group, weak_groups_to_nodes);
|
||||
service_handles_.erase(it);
|
||||
return;
|
||||
}
|
||||
@@ -332,14 +313,16 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
get_next_client(rclcpp::AnyExecutable & any_exec, const WeakNodeList & weak_nodes) override
|
||||
get_next_client(
|
||||
rclcpp::AnyExecutable & any_exec,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
|
||||
{
|
||||
auto it = client_handles_.begin();
|
||||
while (it != client_handles_.end()) {
|
||||
auto client = get_client_by_handle(*it, weak_nodes);
|
||||
auto client = get_client_by_handle(*it, weak_groups_to_nodes);
|
||||
if (client) {
|
||||
// Find the group for this handle and see if it can be serviced
|
||||
auto group = get_group_by_client(client, weak_nodes);
|
||||
auto group = get_group_by_client(client, weak_groups_to_nodes);
|
||||
if (!group) {
|
||||
// Group was not found, meaning the service is not valid...
|
||||
// Remove it from the ready list and continue looking
|
||||
@@ -355,7 +338,7 @@ public:
|
||||
// Otherwise it is safe to set and return the any_exec
|
||||
any_exec.client = client;
|
||||
any_exec.callback_group = group;
|
||||
any_exec.node_base = get_node_by_group(group, weak_nodes);
|
||||
any_exec.node_base = get_node_by_group(group, weak_groups_to_nodes);
|
||||
client_handles_.erase(it);
|
||||
return;
|
||||
}
|
||||
@@ -367,14 +350,14 @@ public:
|
||||
void
|
||||
get_next_timer(
|
||||
rclcpp::AnyExecutable & any_exec,
|
||||
const WeakNodeList & weak_nodes) override
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
|
||||
{
|
||||
auto it = timer_handles_.begin();
|
||||
while (it != timer_handles_.end()) {
|
||||
auto timer = get_timer_by_handle(*it, weak_nodes);
|
||||
auto timer = get_timer_by_handle(*it, weak_groups_to_nodes);
|
||||
if (timer) {
|
||||
// Find the group for this handle and see if it can be serviced
|
||||
auto group = get_group_by_timer(timer, weak_nodes);
|
||||
auto group = get_group_by_timer(timer, weak_groups_to_nodes);
|
||||
if (!group) {
|
||||
// Group was not found, meaning the timer is not valid...
|
||||
// Remove it from the ready list and continue looking
|
||||
@@ -387,27 +370,34 @@ public:
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
if (!timer->call()) {
|
||||
// timer was cancelled, skip it.
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
// Otherwise it is safe to set and return the any_exec
|
||||
any_exec.timer = timer;
|
||||
any_exec.callback_group = group;
|
||||
any_exec.node_base = get_node_by_group(group, weak_nodes);
|
||||
any_exec.node_base = get_node_by_group(group, weak_groups_to_nodes);
|
||||
timer_handles_.erase(it);
|
||||
return;
|
||||
}
|
||||
// Else, the service is no longer valid, remove it and continue
|
||||
// Else, the timer is no longer valid, remove it and continue
|
||||
it = timer_handles_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
get_next_waitable(rclcpp::AnyExecutable & any_exec, const WeakNodeList & weak_nodes) override
|
||||
get_next_waitable(
|
||||
rclcpp::AnyExecutable & any_exec,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
|
||||
{
|
||||
auto it = waitable_handles_.begin();
|
||||
while (it != waitable_handles_.end()) {
|
||||
auto waitable = *it;
|
||||
std::shared_ptr<Waitable> & waitable = *it;
|
||||
if (waitable) {
|
||||
// Find the group for this handle and see if it can be serviced
|
||||
auto group = get_group_by_waitable(waitable, weak_nodes);
|
||||
auto group = get_group_by_waitable(waitable, weak_groups_to_nodes);
|
||||
if (!group) {
|
||||
// Group was not found, meaning the waitable is not valid...
|
||||
// Remove it from the ready list and continue looking
|
||||
@@ -423,7 +413,7 @@ public:
|
||||
// Otherwise it is safe to set and return the any_exec
|
||||
any_exec.waitable = waitable;
|
||||
any_exec.callback_group = group;
|
||||
any_exec.node_base = get_node_by_group(group, weak_nodes);
|
||||
any_exec.node_base = get_node_by_group(group, weak_groups_to_nodes);
|
||||
waitable_handles_.erase(it);
|
||||
return;
|
||||
}
|
||||
@@ -440,7 +430,7 @@ public:
|
||||
size_t number_of_ready_subscriptions() const override
|
||||
{
|
||||
size_t number_of_subscriptions = subscription_handles_.size();
|
||||
for (auto waitable : waitable_handles_) {
|
||||
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
|
||||
number_of_subscriptions += waitable->get_number_of_ready_subscriptions();
|
||||
}
|
||||
return number_of_subscriptions;
|
||||
@@ -449,7 +439,7 @@ public:
|
||||
size_t number_of_ready_services() const override
|
||||
{
|
||||
size_t number_of_services = service_handles_.size();
|
||||
for (auto waitable : waitable_handles_) {
|
||||
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
|
||||
number_of_services += waitable->get_number_of_ready_services();
|
||||
}
|
||||
return number_of_services;
|
||||
@@ -458,7 +448,7 @@ public:
|
||||
size_t number_of_ready_events() const override
|
||||
{
|
||||
size_t number_of_events = 0;
|
||||
for (auto waitable : waitable_handles_) {
|
||||
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
|
||||
number_of_events += waitable->get_number_of_ready_events();
|
||||
}
|
||||
return number_of_events;
|
||||
@@ -467,7 +457,7 @@ public:
|
||||
size_t number_of_ready_clients() const override
|
||||
{
|
||||
size_t number_of_clients = client_handles_.size();
|
||||
for (auto waitable : waitable_handles_) {
|
||||
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
|
||||
number_of_clients += waitable->get_number_of_ready_clients();
|
||||
}
|
||||
return number_of_clients;
|
||||
@@ -476,7 +466,7 @@ public:
|
||||
size_t number_of_guard_conditions() const override
|
||||
{
|
||||
size_t number_of_guard_conditions = guard_conditions_.size();
|
||||
for (auto waitable : waitable_handles_) {
|
||||
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
|
||||
number_of_guard_conditions += waitable->get_number_of_ready_guard_conditions();
|
||||
}
|
||||
return number_of_guard_conditions;
|
||||
@@ -485,7 +475,7 @@ public:
|
||||
size_t number_of_ready_timers() const override
|
||||
{
|
||||
size_t number_of_timers = timer_handles_.size();
|
||||
for (auto waitable : waitable_handles_) {
|
||||
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
|
||||
number_of_timers += waitable->get_number_of_ready_timers();
|
||||
}
|
||||
return number_of_timers;
|
||||
@@ -501,7 +491,7 @@ private:
|
||||
using VectorRebind =
|
||||
std::vector<T, typename std::allocator_traits<Alloc>::template rebind_alloc<T>>;
|
||||
|
||||
VectorRebind<const rcl_guard_condition_t *> guard_conditions_;
|
||||
VectorRebind<const rclcpp::GuardCondition *> guard_conditions_;
|
||||
|
||||
VectorRebind<std::shared_ptr<const rcl_subscription_t>> subscription_handles_;
|
||||
VectorRebind<std::shared_ptr<const rcl_service_t>> service_handles_;
|
||||
|
||||
@@ -60,10 +60,16 @@ class NodeTopicsInterface;
|
||||
|
||||
/// Subscription implementation, templated on the type of message this subscription receives.
|
||||
template<
|
||||
typename CallbackMessageT,
|
||||
typename MessageT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
/// MessageT::custom_type if MessageT is a TypeAdapter,
|
||||
/// otherwise just MessageT.
|
||||
typename SubscribedT = typename rclcpp::TypeAdapter<MessageT>::custom_type,
|
||||
/// MessageT::ros_message_type if MessageT is a TypeAdapter,
|
||||
/// otherwise just MessageT.
|
||||
typename ROSMessageT = typename rclcpp::TypeAdapter<MessageT>::ros_message_type,
|
||||
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
ROSMessageT,
|
||||
AllocatorT
|
||||
>>
|
||||
class Subscription : public SubscriptionBase
|
||||
@@ -71,14 +77,36 @@ class Subscription : public SubscriptionBase
|
||||
friend class rclcpp::node_interfaces::NodeTopicsInterface;
|
||||
|
||||
public:
|
||||
using MessageAllocatorTraits = allocator::AllocRebind<CallbackMessageT, AllocatorT>;
|
||||
using MessageAllocator = typename MessageAllocatorTraits::allocator_type;
|
||||
using MessageDeleter = allocator::Deleter<MessageAllocator, CallbackMessageT>;
|
||||
using ConstMessageSharedPtr = std::shared_ptr<const CallbackMessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<CallbackMessageT, MessageDeleter>;
|
||||
using SubscriptionTopicStatisticsSharedPtr =
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>>;
|
||||
// Redeclare these here to use outside of the class.
|
||||
using SubscribedType = SubscribedT;
|
||||
using ROSMessageType = ROSMessageT;
|
||||
using MessageMemoryStrategyType = MessageMemoryStrategyT;
|
||||
|
||||
using SubscribedTypeAllocatorTraits = allocator::AllocRebind<SubscribedType, AllocatorT>;
|
||||
using SubscribedTypeAllocator = typename SubscribedTypeAllocatorTraits::allocator_type;
|
||||
using SubscribedTypeDeleter = allocator::Deleter<SubscribedTypeAllocator, SubscribedType>;
|
||||
|
||||
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, AllocatorT>;
|
||||
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
|
||||
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
|
||||
|
||||
using MessageAllocatorTraits [[deprecated("use ROSMessageTypeAllocatorTraits")]] =
|
||||
ROSMessageTypeAllocatorTraits;
|
||||
using MessageAllocator [[deprecated("use ROSMessageTypeAllocator")]] =
|
||||
ROSMessageTypeAllocator;
|
||||
using MessageDeleter [[deprecated("use ROSMessageTypeDeleter")]] =
|
||||
ROSMessageTypeDeleter;
|
||||
|
||||
using ConstMessageSharedPtr [[deprecated]] = std::shared_ptr<const ROSMessageType>;
|
||||
using MessageUniquePtr
|
||||
[[deprecated("use std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> instead")]] =
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>;
|
||||
|
||||
private:
|
||||
using SubscriptionTopicStatisticsSharedPtr =
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Subscription)
|
||||
|
||||
/// Default constructor.
|
||||
@@ -92,7 +120,7 @@ public:
|
||||
* \param[in] topic_name Name of the topic to subscribe to.
|
||||
* \param[in] qos QoS profile for Subcription.
|
||||
* \param[in] callback User defined callback to call when a message is received.
|
||||
* \param[in] options options for the subscription.
|
||||
* \param[in] options Options for the subscription.
|
||||
* \param[in] message_memory_strategy The memory strategy to be used for managing message memory.
|
||||
* \param[in] subscription_topic_statistics Optional pointer to a topic statistics subcription.
|
||||
* \throws std::invalid_argument if the QoS is uncompatible with intra-process (if one
|
||||
@@ -104,7 +132,7 @@ public:
|
||||
const rosidl_message_type_support_t & type_support_handle,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
AnySubscriptionCallback<CallbackMessageT, AllocatorT> callback,
|
||||
AnySubscriptionCallback<MessageT, AllocatorT> callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
|
||||
typename MessageMemoryStrategyT::SharedPtr message_memory_strategy,
|
||||
SubscriptionTopicStatisticsSharedPtr subscription_topic_statistics = nullptr)
|
||||
@@ -112,25 +140,25 @@ public:
|
||||
node_base,
|
||||
type_support_handle,
|
||||
topic_name,
|
||||
options.template to_rcl_subscription_options<CallbackMessageT>(qos),
|
||||
rclcpp::subscription_traits::is_serialized_subscription_argument<CallbackMessageT>::value),
|
||||
options.template to_rcl_subscription_options<ROSMessageType>(qos),
|
||||
callback.is_serialized_message_callback()),
|
||||
any_callback_(callback),
|
||||
options_(options),
|
||||
message_memory_strategy_(message_memory_strategy)
|
||||
{
|
||||
if (options.event_callbacks.deadline_callback) {
|
||||
if (options_.event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.deadline_callback,
|
||||
options_.event_callbacks.deadline_callback,
|
||||
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
|
||||
}
|
||||
if (options.event_callbacks.liveliness_callback) {
|
||||
if (options_.event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.liveliness_callback,
|
||||
options_.event_callbacks.liveliness_callback,
|
||||
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
|
||||
}
|
||||
if (options.event_callbacks.incompatible_qos_callback) {
|
||||
if (options_.event_callbacks.incompatible_qos_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.incompatible_qos_callback,
|
||||
options_.event_callbacks.incompatible_qos_callback,
|
||||
RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
|
||||
} else if (options_.use_default_callbacks) {
|
||||
// Register default callback when not specified
|
||||
@@ -144,48 +172,57 @@ public:
|
||||
// pass
|
||||
}
|
||||
}
|
||||
if (options_.event_callbacks.message_lost_callback) {
|
||||
this->add_event_handler(
|
||||
options_.event_callbacks.message_lost_callback,
|
||||
RCL_SUBSCRIPTION_MESSAGE_LOST);
|
||||
}
|
||||
|
||||
// Setup intra process publishing if requested.
|
||||
if (rclcpp::detail::resolve_use_intra_process(options, *node_base)) {
|
||||
if (rclcpp::detail::resolve_use_intra_process(options_, *node_base)) {
|
||||
using rclcpp::detail::resolve_intra_process_buffer_type;
|
||||
|
||||
// Check if the QoS is compatible with intra-process.
|
||||
rmw_qos_profile_t qos_profile = get_actual_qos().get_rmw_qos_profile();
|
||||
if (qos_profile.history == RMW_QOS_POLICY_HISTORY_KEEP_ALL) {
|
||||
auto qos_profile = get_actual_qos();
|
||||
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with keep all history qos policy");
|
||||
"intraprocess communication allowed only with keep last history qos policy");
|
||||
}
|
||||
if (qos_profile.depth == 0) {
|
||||
if (qos_profile.depth() == 0) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with 0 depth qos policy");
|
||||
}
|
||||
if (qos_profile.durability != RMW_QOS_POLICY_DURABILITY_VOLATILE) {
|
||||
if (qos_profile.durability() != rclcpp::DurabilityPolicy::Volatile) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication allowed only with volatile durability");
|
||||
}
|
||||
|
||||
using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<
|
||||
MessageT,
|
||||
SubscribedType,
|
||||
SubscribedTypeAllocator,
|
||||
SubscribedTypeDeleter,
|
||||
ROSMessageT,
|
||||
AllocatorT>;
|
||||
|
||||
// First create a SubscriptionIntraProcess which will be given to the intra-process manager.
|
||||
auto context = node_base->get_context();
|
||||
using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<
|
||||
CallbackMessageT,
|
||||
AllocatorT,
|
||||
typename MessageUniquePtr::deleter_type>;
|
||||
auto subscription_intra_process = std::make_shared<SubscriptionIntraProcessT>(
|
||||
subscription_intra_process_ = std::make_shared<SubscriptionIntraProcessT>(
|
||||
callback,
|
||||
options.get_allocator(),
|
||||
options_.get_allocator(),
|
||||
context,
|
||||
this->get_topic_name(), // important to get like this, as it has the fully-qualified name
|
||||
qos_profile,
|
||||
resolve_intra_process_buffer_type(options.intra_process_buffer_type, callback));
|
||||
resolve_intra_process_buffer_type(options_.intra_process_buffer_type, callback));
|
||||
TRACEPOINT(
|
||||
rclcpp_subscription_init,
|
||||
(const void *)get_subscription_handle().get(),
|
||||
(const void *)subscription_intra_process.get());
|
||||
static_cast<const void *>(get_subscription_handle().get()),
|
||||
static_cast<const void *>(subscription_intra_process_.get()));
|
||||
|
||||
// Add it to the intra process manager.
|
||||
using rclcpp::experimental::IntraProcessManager;
|
||||
auto ipm = context->get_sub_context<IntraProcessManager>();
|
||||
uint64_t intra_process_subscription_id = ipm->add_subscription(subscription_intra_process);
|
||||
uint64_t intra_process_subscription_id = ipm->add_subscription(subscription_intra_process_);
|
||||
this->setup_intra_process(intra_process_subscription_id, ipm);
|
||||
}
|
||||
|
||||
@@ -195,12 +232,12 @@ public:
|
||||
|
||||
TRACEPOINT(
|
||||
rclcpp_subscription_init,
|
||||
(const void *)get_subscription_handle().get(),
|
||||
(const void *)this);
|
||||
static_cast<const void *>(get_subscription_handle().get()),
|
||||
static_cast<const void *>(this));
|
||||
TRACEPOINT(
|
||||
rclcpp_subscription_callback_added,
|
||||
(const void *)this,
|
||||
(const void *)&any_callback_);
|
||||
static_cast<const void *>(this),
|
||||
static_cast<const void *>(&any_callback_));
|
||||
// The callback object gets copied, so if registration is done too early/before this point
|
||||
// (e.g. in `AnySubscriptionCallback::set()`), its address won't match any address used later
|
||||
// in subsequent tracepoints.
|
||||
@@ -240,11 +277,34 @@ public:
|
||||
* \throws any rcl errors from rcl_take, \sa rclcpp::exceptions::throw_from_rcl_error()
|
||||
*/
|
||||
bool
|
||||
take(CallbackMessageT & message_out, rclcpp::MessageInfo & message_info_out)
|
||||
take(ROSMessageType & message_out, rclcpp::MessageInfo & message_info_out)
|
||||
{
|
||||
return this->take_type_erased(static_cast<void *>(&message_out), message_info_out);
|
||||
}
|
||||
|
||||
/// Take the next message from the inter-process subscription.
|
||||
/**
|
||||
* This version takes a SubscribedType which is different from the
|
||||
* ROSMessageType when a rclcpp::TypeAdapter is in used.
|
||||
*
|
||||
* \sa take(ROSMessageType &, rclcpp::MessageInfo &)
|
||||
*/
|
||||
template<typename TakeT>
|
||||
std::enable_if_t<
|
||||
!rosidl_generator_traits::is_message<TakeT>::value &&
|
||||
std::is_same_v<TakeT, SubscribedType>,
|
||||
bool
|
||||
>
|
||||
take(TakeT & message_out, rclcpp::MessageInfo & message_info_out)
|
||||
{
|
||||
ROSMessageType local_message;
|
||||
bool taken = this->take_type_erased(static_cast<void *>(&local_message), message_info_out);
|
||||
if (taken) {
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_custom(local_message, message_out);
|
||||
}
|
||||
return taken;
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
create_message() override
|
||||
{
|
||||
@@ -271,27 +331,63 @@ public:
|
||||
// we should ignore this copy of the message.
|
||||
return;
|
||||
}
|
||||
auto typed_message = std::static_pointer_cast<CallbackMessageT>(message);
|
||||
auto typed_message = std::static_pointer_cast<ROSMessageType>(message);
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> now;
|
||||
if (subscription_topic_statistics_) {
|
||||
// get current time before executing callback to
|
||||
// exclude callback duration from topic statistics result.
|
||||
now = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
any_callback_.dispatch(typed_message, message_info);
|
||||
|
||||
if (subscription_topic_statistics_) {
|
||||
const auto nanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::now());
|
||||
const auto nanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(now);
|
||||
const auto time = rclcpp::Time(nanos.time_since_epoch().count());
|
||||
subscription_topic_statistics_->handle_message(*typed_message, time);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
|
||||
const rclcpp::MessageInfo & message_info) override
|
||||
{
|
||||
// TODO(wjwwood): enable topic statistics for serialized messages
|
||||
any_callback_.dispatch(serialized_message, message_info);
|
||||
}
|
||||
|
||||
void
|
||||
handle_loaned_message(
|
||||
void * loaned_message,
|
||||
const rclcpp::MessageInfo & message_info) override
|
||||
{
|
||||
auto typed_message = static_cast<CallbackMessageT *>(loaned_message);
|
||||
if (matches_any_intra_process_publishers(&message_info.get_rmw_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 = static_cast<ROSMessageType *>(loaned_message);
|
||||
// message is loaned, so we have to make sure that the deleter does not deallocate the message
|
||||
auto sptr = std::shared_ptr<CallbackMessageT>(
|
||||
typed_message, [](CallbackMessageT * msg) {(void) msg;});
|
||||
auto sptr = std::shared_ptr<ROSMessageType>(
|
||||
typed_message, [](ROSMessageType * msg) {(void) msg;});
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> now;
|
||||
if (subscription_topic_statistics_) {
|
||||
// get current time before executing callback to
|
||||
// exclude callback duration from topic statistics result.
|
||||
now = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
any_callback_.dispatch(sptr, message_info);
|
||||
|
||||
if (subscription_topic_statistics_) {
|
||||
const auto nanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(now);
|
||||
const auto time = rclcpp::Time(nanos.time_since_epoch().count());
|
||||
subscription_topic_statistics_->handle_message(*typed_message, time);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the borrowed message.
|
||||
@@ -301,7 +397,7 @@ public:
|
||||
void
|
||||
return_message(std::shared_ptr<void> & message) override
|
||||
{
|
||||
auto typed_message = std::static_pointer_cast<CallbackMessageT>(message);
|
||||
auto typed_message = std::static_pointer_cast<ROSMessageType>(message);
|
||||
message_memory_strategy_->return_message(typed_message);
|
||||
}
|
||||
|
||||
@@ -324,15 +420,16 @@ public:
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Subscription)
|
||||
|
||||
AnySubscriptionCallback<CallbackMessageT, AllocatorT> any_callback_;
|
||||
AnySubscriptionCallback<MessageT, AllocatorT> any_callback_;
|
||||
/// Copy of original options passed during construction.
|
||||
/**
|
||||
* It is important to save a copy of this so that the rmw payload which it
|
||||
* may contain is kept alive for the duration of the subscription.
|
||||
*/
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> options_;
|
||||
typename message_memory_strategy::MessageMemoryStrategy<CallbackMessageT, AllocatorT>::SharedPtr
|
||||
typename message_memory_strategy::MessageMemoryStrategy<ROSMessageType, AllocatorT>::SharedPtr
|
||||
message_memory_strategy_;
|
||||
|
||||
/// Component which computes and publishes topic statistics for this subscriber
|
||||
SubscriptionTopicStatisticsSharedPtr subscription_topic_statistics_{nullptr};
|
||||
};
|
||||
|
||||
@@ -17,23 +17,29 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
#include "rcl/event_callback.h"
|
||||
#include "rcl/subscription.h"
|
||||
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
#include "rclcpp/any_subscription_callback.hpp"
|
||||
#include "rclcpp/detail/cpp_callback_trampoline.hpp"
|
||||
#include "rclcpp/experimental/intra_process_manager.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/message_info.hpp"
|
||||
#include "rclcpp/network_flow_endpoint.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/qos_event.hpp"
|
||||
#include "rclcpp/serialized_message.hpp"
|
||||
#include "rclcpp/subscription_content_filter_options.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -61,12 +67,15 @@ class SubscriptionBase : public std::enable_shared_from_this<SubscriptionBase>
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase)
|
||||
|
||||
/// Default constructor.
|
||||
/// Constructor.
|
||||
/**
|
||||
* This accepts rcl_subscription_options_t instead of rclcpp::SubscriptionOptions because
|
||||
* rclcpp::SubscriptionOptions::to_rcl_subscription_options depends on the message type.
|
||||
*
|
||||
* \param[in] node_base NodeBaseInterface pointer used in parts of the setup.
|
||||
* \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] subscription_options Options for the subscription.
|
||||
* \param[in] is_serialized is true if the message will be delivered still serialized
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
@@ -77,7 +86,7 @@ public:
|
||||
const rcl_subscription_options_t & subscription_options,
|
||||
bool is_serialized = false);
|
||||
|
||||
/// Default destructor.
|
||||
/// Destructor.
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~SubscriptionBase();
|
||||
|
||||
@@ -95,15 +104,16 @@ public:
|
||||
get_subscription_handle() const;
|
||||
|
||||
/// Get all the QoS event handlers associated with this subscription.
|
||||
/** \return The vector of QoS event handlers. */
|
||||
/** \return The map of QoS event handlers. */
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
|
||||
const
|
||||
std::unordered_map<rcl_subscription_event_type_t, std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
|
||||
get_event_handlers() 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
|
||||
* can only be resolved after the creation of the subscription, 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.
|
||||
@@ -179,6 +189,13 @@ public:
|
||||
void
|
||||
handle_message(std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info) = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
|
||||
const rclcpp::MessageInfo & message_info) = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
@@ -263,6 +280,252 @@ public:
|
||||
bool
|
||||
exchange_in_use_by_wait_set_state(void * pointer_to_subscription_part, bool in_use_state);
|
||||
|
||||
/// Get network flow endpoints
|
||||
/**
|
||||
* Describes network flow endpoints that this subscription is receiving messages on
|
||||
* \return vector of NetworkFlowEndpoint
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::NetworkFlowEndpoint>
|
||||
get_network_flow_endpoints() const;
|
||||
|
||||
/// Set a callback to be called when each new message is received.
|
||||
/**
|
||||
* The callback receives a size_t which is the number of messages received
|
||||
* since the last time this callback was called.
|
||||
* Normally this is 1, but can be > 1 if messages were received before any
|
||||
* callback was set.
|
||||
*
|
||||
* Since this callback is called from the middleware, you should aim to make
|
||||
* it fast and not blocking.
|
||||
* If you need to do a lot of work or wait for some other event, you should
|
||||
* spin it off to another thread, otherwise you risk blocking the middleware.
|
||||
*
|
||||
* Calling it again will clear any previously set callback.
|
||||
*
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* If you want more information available in the callback, like the subscription
|
||||
* or other information, you may use a lambda with captures or std::bind.
|
||||
*
|
||||
* \sa rmw_subscription_set_on_new_message_callback
|
||||
* \sa rcl_subscription_set_on_new_message_callback
|
||||
*
|
||||
* \param[in] callback functor to be called when a new message is received
|
||||
*/
|
||||
void
|
||||
set_on_new_message_callback(std::function<void(size_t)> callback)
|
||||
{
|
||||
if (!callback) {
|
||||
throw std::invalid_argument(
|
||||
"The callback passed to set_on_new_message_callback "
|
||||
"is not callable.");
|
||||
}
|
||||
|
||||
auto new_callback =
|
||||
[callback, this](size_t number_of_messages) {
|
||||
try {
|
||||
callback(number_of_messages);
|
||||
} catch (const std::exception & exception) {
|
||||
RCLCPP_ERROR_STREAM(
|
||||
node_logger_,
|
||||
"rclcpp::SubscriptionBase@" << this <<
|
||||
" caught " << rmw::impl::cpp::demangle(exception) <<
|
||||
" exception in user-provided callback for the 'on new message' callback: " <<
|
||||
exception.what());
|
||||
} catch (...) {
|
||||
RCLCPP_ERROR_STREAM(
|
||||
node_logger_,
|
||||
"rclcpp::SubscriptionBase@" << this <<
|
||||
" caught unhandled exception in user-provided callback " <<
|
||||
"for the 'on new message' callback");
|
||||
}
|
||||
};
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
|
||||
// Set it temporarily to the new callback, while we replace the old one.
|
||||
// This two-step setting, prevents a gap where the old std::function has
|
||||
// been replaced but the middleware hasn't been told about the new one yet.
|
||||
set_on_new_message_callback(
|
||||
rclcpp::detail::cpp_callback_trampoline<const void *, size_t>,
|
||||
static_cast<const void *>(&new_callback));
|
||||
|
||||
// Store the std::function to keep it in scope, also overwrites the existing one.
|
||||
on_new_message_callback_ = new_callback;
|
||||
|
||||
// Set it again, now using the permanent storage.
|
||||
set_on_new_message_callback(
|
||||
rclcpp::detail::cpp_callback_trampoline<const void *, size_t>,
|
||||
static_cast<const void *>(&on_new_message_callback_));
|
||||
}
|
||||
|
||||
/// Unset the callback registered for new messages, if any.
|
||||
void
|
||||
clear_on_new_message_callback()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
|
||||
if (on_new_message_callback_) {
|
||||
set_on_new_message_callback(nullptr, nullptr);
|
||||
on_new_message_callback_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a callback to be called when each new intra-process message is received.
|
||||
/**
|
||||
* The callback receives a size_t which is the number of messages received
|
||||
* since the last time this callback was called.
|
||||
* Normally this is 1, but can be > 1 if messages were received before any
|
||||
* callback was set.
|
||||
*
|
||||
* Calling it again will clear any previously set callback.
|
||||
*
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* If you want more information available in the callback, like the subscription
|
||||
* or other information, you may use a lambda with captures or std::bind.
|
||||
*
|
||||
* \sa rclcpp::SubscriptionIntraProcessBase::set_on_ready_callback
|
||||
*
|
||||
* \param[in] callback functor to be called when a new message is received
|
||||
*/
|
||||
void
|
||||
set_on_new_intra_process_message_callback(std::function<void(size_t)> callback)
|
||||
{
|
||||
if (!use_intra_process_) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Calling set_on_new_intra_process_message_callback for subscription with IPC disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!callback) {
|
||||
throw std::invalid_argument(
|
||||
"The callback passed to set_on_new_intra_process_message_callback "
|
||||
"is not callable.");
|
||||
}
|
||||
|
||||
// The on_ready_callback signature has an extra `int` argument used to disambiguate between
|
||||
// possible different entities within a generic waitable.
|
||||
// We hide that detail to users of this method.
|
||||
std::function<void(size_t, int)> new_callback = std::bind(callback, std::placeholders::_1);
|
||||
subscription_intra_process_->set_on_ready_callback(new_callback);
|
||||
}
|
||||
|
||||
/// Unset the callback registered for new intra-process messages, if any.
|
||||
void
|
||||
clear_on_new_intra_process_message_callback()
|
||||
{
|
||||
if (!use_intra_process_) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Calling clear_on_new_intra_process_message_callback for subscription with IPC disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
subscription_intra_process_->clear_on_ready_callback();
|
||||
}
|
||||
|
||||
/// Set a callback to be called when each new qos event instance occurs.
|
||||
/**
|
||||
* The callback receives a size_t which is the number of events that occurred
|
||||
* since the last time this callback was called.
|
||||
* Normally this is 1, but can be > 1 if events occurred before any
|
||||
* callback was set.
|
||||
*
|
||||
* Since this callback is called from the middleware, you should aim to make
|
||||
* it fast and not blocking.
|
||||
* If you need to do a lot of work or wait for some other event, you should
|
||||
* spin it off to another thread, otherwise you risk blocking the middleware.
|
||||
*
|
||||
* Calling it again will clear any previously set callback.
|
||||
*
|
||||
* An exception will be thrown if the callback is not callable.
|
||||
*
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* If you want more information available in the callback, like the qos event
|
||||
* or other information, you may use a lambda with captures or std::bind.
|
||||
*
|
||||
* \sa rclcpp::QOSEventHandlerBase::set_on_ready_callback
|
||||
*
|
||||
* \param[in] callback functor to be called when a new event occurs
|
||||
* \param[in] event_type identifier for the qos event we want to attach the callback to
|
||||
*/
|
||||
void
|
||||
set_on_new_qos_event_callback(
|
||||
std::function<void(size_t)> callback,
|
||||
rcl_subscription_event_type_t event_type)
|
||||
{
|
||||
if (event_handlers_.count(event_type) == 0) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Calling set_on_new_qos_event_callback for non registered subscription event_type");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!callback) {
|
||||
throw std::invalid_argument(
|
||||
"The callback passed to set_on_new_qos_event_callback "
|
||||
"is not callable.");
|
||||
}
|
||||
|
||||
// The on_ready_callback signature has an extra `int` argument used to disambiguate between
|
||||
// possible different entities within a generic waitable.
|
||||
// We hide that detail to users of this method.
|
||||
std::function<void(size_t, int)> new_callback = std::bind(callback, std::placeholders::_1);
|
||||
event_handlers_[event_type]->set_on_ready_callback(new_callback);
|
||||
}
|
||||
|
||||
/// Unset the callback registered for new qos events, if any.
|
||||
void
|
||||
clear_on_new_qos_event_callback(rcl_subscription_event_type_t event_type)
|
||||
{
|
||||
if (event_handlers_.count(event_type) == 0) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Calling clear_on_new_qos_event_callback for non registered event_type");
|
||||
return;
|
||||
}
|
||||
|
||||
event_handlers_[event_type]->clear_on_ready_callback();
|
||||
}
|
||||
|
||||
/// Check if content filtered topic feature of the subscription instance is enabled.
|
||||
/**
|
||||
* \return boolean flag indicating if the content filtered topic of this subscription is enabled.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
is_cft_enabled() const;
|
||||
|
||||
/// Set the filter expression and expression parameters for the subscription.
|
||||
/**
|
||||
* \param[in] filter_expression A filter expression to set.
|
||||
* \sa ContentFilterOptions::filter_expression
|
||||
* An empty string ("") will clear the content filter setting of the subscription.
|
||||
* \param[in] expression_parameters Array of expression parameters to set.
|
||||
* \sa ContentFilterOptions::expression_parameters
|
||||
* \throws RCLBadAlloc if memory cannot be allocated
|
||||
* \throws RCLError if an unexpect error occurs
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_content_filter(
|
||||
const std::string & filter_expression,
|
||||
const std::vector<std::string> & expression_parameters = {});
|
||||
|
||||
/// Get the filter expression and expression parameters for the subscription.
|
||||
/**
|
||||
* \return rclcpp::ContentFilterOptions The content filter options to get.
|
||||
* \throws RCLBadAlloc if memory cannot be allocated
|
||||
* \throws RCLError if an unexpect error occurs
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::ContentFilterOptions
|
||||
get_content_filter() const;
|
||||
|
||||
protected:
|
||||
template<typename EventCallbackT>
|
||||
void
|
||||
@@ -277,7 +540,7 @@ protected:
|
||||
get_subscription_handle(),
|
||||
event_type);
|
||||
qos_events_in_use_by_wait_set_.insert(std::make_pair(handler.get(), false));
|
||||
event_handlers_.emplace_back(handler);
|
||||
event_handlers_.insert(std::make_pair(event_type, handler));
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
@@ -287,17 +550,24 @@ protected:
|
||||
bool
|
||||
matches_any_intra_process_publishers(const rmw_gid_t * sender_gid) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_on_new_message_callback(rcl_event_callback_t callback, const void * user_data);
|
||||
|
||||
rclcpp::node_interfaces::NodeBaseInterface * const node_base_;
|
||||
|
||||
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_;
|
||||
rclcpp::Logger node_logger_;
|
||||
|
||||
std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;
|
||||
std::unordered_map<rcl_subscription_event_type_t,
|
||||
std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;
|
||||
|
||||
bool use_intra_process_;
|
||||
IntraProcessManagerWeakPtr weak_ipm_;
|
||||
uint64_t intra_process_subscription_id_;
|
||||
std::shared_ptr<rclcpp::experimental::SubscriptionIntraProcessBase> subscription_intra_process_;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(SubscriptionBase)
|
||||
@@ -309,6 +579,9 @@ private:
|
||||
std::atomic<bool> intra_process_subscription_waitable_in_use_by_wait_set_{false};
|
||||
std::unordered_map<rclcpp::QOSEventHandlerBase *,
|
||||
std::atomic<bool>> qos_events_in_use_by_wait_set_;
|
||||
|
||||
std::recursive_mutex callback_mutex_;
|
||||
std::function<void(size_t)> on_new_message_callback_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2022 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_CONTENT_FILTER_OPTIONS_HPP_
|
||||
#define RCLCPP__SUBSCRIPTION_CONTENT_FILTER_OPTIONS_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Options to configure content filtered topic in the subscription.
|
||||
struct ContentFilterOptions
|
||||
{
|
||||
/// Filter expression is similar to the WHERE part of an SQL clause.
|
||||
std::string filter_expression;
|
||||
/**
|
||||
* Expression parameters is the tokens placeholder ‘parameters’ (i.e., "%n" tokens begin from 0)
|
||||
* in the filter_expression. The maximum expression_parameters size is 100.
|
||||
*/
|
||||
std::vector<std::string> expression_parameters;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__SUBSCRIPTION_CONTENT_FILTER_OPTIONS_HPP_
|
||||
@@ -25,14 +25,14 @@
|
||||
#include "rosidl_typesupport_cpp/message_type_support.hpp"
|
||||
|
||||
#include "rclcpp/any_subscription_callback.hpp"
|
||||
#include "rclcpp/intra_process_buffer_type.hpp"
|
||||
#include "rclcpp/get_message_type_support_handle.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/subscription.hpp"
|
||||
#include "rclcpp/subscription_options.hpp"
|
||||
#include "rclcpp/subscription_traits.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/topic_statistics/subscription_topic_statistics.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -68,32 +68,29 @@ struct SubscriptionFactory
|
||||
* \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.
|
||||
* \param[in] subscription_topic_Optinal stats callback for topic_statistics
|
||||
* \param[in] subscription_topic_stats Optional stats callback for topic_statistics
|
||||
*/
|
||||
template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT,
|
||||
typename CallbackMessageT =
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
|
||||
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
AllocatorT
|
||||
>>
|
||||
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType,
|
||||
typename ROSMessageType = typename SubscriptionT::ROSMessageType
|
||||
>
|
||||
SubscriptionFactory
|
||||
create_subscription_factory(
|
||||
CallbackT && callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
|
||||
typename MessageMemoryStrategyT::SharedPtr msg_mem_strat,
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>>
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>
|
||||
subscription_topic_stats = nullptr
|
||||
)
|
||||
{
|
||||
auto allocator = options.get_allocator();
|
||||
|
||||
using rclcpp::AnySubscriptionCallback;
|
||||
AnySubscriptionCallback<CallbackMessageT, AllocatorT> any_subscription_callback(allocator);
|
||||
AnySubscriptionCallback<MessageT, AllocatorT> any_subscription_callback(*allocator);
|
||||
any_subscription_callback.set(std::forward<CallbackT>(callback));
|
||||
|
||||
SubscriptionFactory factory {
|
||||
@@ -107,9 +104,9 @@ create_subscription_factory(
|
||||
using rclcpp::Subscription;
|
||||
using rclcpp::SubscriptionBase;
|
||||
|
||||
auto sub = Subscription<CallbackMessageT, AllocatorT>::make_shared(
|
||||
auto sub = Subscription<MessageT, AllocatorT>::make_shared(
|
||||
node_base,
|
||||
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
|
||||
rclcpp::get_message_type_support_handle<MessageT>(),
|
||||
topic_name,
|
||||
qos,
|
||||
any_subscription_callback,
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
@@ -26,6 +27,8 @@
|
||||
#include "rclcpp/intra_process_setting.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/qos_event.hpp"
|
||||
#include "rclcpp/qos_overriding_options.hpp"
|
||||
#include "rclcpp/subscription_content_filter_options.hpp"
|
||||
#include "rclcpp/topic_statistics_state.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -44,6 +47,11 @@ struct SubscriptionOptionsBase
|
||||
/// True to ignore local publications.
|
||||
bool ignore_local_publications = false;
|
||||
|
||||
/// Require middleware to generate unique network flow endpoints
|
||||
/// Disabled by default
|
||||
rmw_unique_network_flow_endpoints_requirement_t require_unique_network_flow_endpoints =
|
||||
RMW_UNIQUE_NETWORK_FLOW_ENDPOINTS_NOT_REQUIRED;
|
||||
|
||||
/// The callback group for this subscription. NULL to use the default callback group.
|
||||
rclcpp::CallbackGroup::SharedPtr callback_group = nullptr;
|
||||
|
||||
@@ -66,21 +74,30 @@ struct SubscriptionOptionsBase
|
||||
// Topic to which topic statistics get published when enabled. Defaults to /statistics.
|
||||
std::string publish_topic = "/statistics";
|
||||
|
||||
// Topic statistics publication period in ms. Defaults to one minute.
|
||||
// Topic statistics publication period in ms. Defaults to one second.
|
||||
// Only values greater than zero are allowed.
|
||||
std::chrono::milliseconds publish_period{std::chrono::seconds(1)};
|
||||
};
|
||||
|
||||
TopicStatisticsOptions topic_stats_options;
|
||||
|
||||
QosOverridingOptions qos_overriding_options;
|
||||
|
||||
ContentFilterOptions content_filter_options;
|
||||
};
|
||||
|
||||
/// Structure containing optional configuration for Subscriptions.
|
||||
template<typename Allocator>
|
||||
struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
|
||||
{
|
||||
static_assert(
|
||||
std::is_void_v<typename std::allocator_traits<Allocator>::value_type>,
|
||||
"Subscription allocator value type must be void");
|
||||
|
||||
/// Optional custom allocator.
|
||||
std::shared_ptr<Allocator> allocator = nullptr;
|
||||
|
||||
SubscriptionOptionsWithAllocator<Allocator>() {}
|
||||
SubscriptionOptionsWithAllocator() {}
|
||||
|
||||
/// Constructor using base class as input.
|
||||
explicit SubscriptionOptionsWithAllocator(
|
||||
@@ -98,30 +115,68 @@ struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
|
||||
to_rcl_subscription_options(const rclcpp::QoS & qos) const
|
||||
{
|
||||
rcl_subscription_options_t result = rcl_subscription_get_default_options();
|
||||
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.allocator = this->get_rcl_allocator();
|
||||
result.qos = qos.get_rmw_qos_profile();
|
||||
result.rmw_subscription_options.ignore_local_publications = this->ignore_local_publications;
|
||||
result.rmw_subscription_options.require_unique_network_flow_endpoints =
|
||||
this->require_unique_network_flow_endpoints;
|
||||
|
||||
// Apply payload to rcl_subscription_options if necessary.
|
||||
if (rmw_implementation_payload && rmw_implementation_payload->has_been_customized()) {
|
||||
rmw_implementation_payload->modify_rmw_subscription_options(result.rmw_subscription_options);
|
||||
}
|
||||
|
||||
// Copy content_filter_options into rcl_subscription_options.
|
||||
if (!content_filter_options.filter_expression.empty()) {
|
||||
std::vector<const char *> cstrings =
|
||||
get_c_vector_string(content_filter_options.expression_parameters);
|
||||
rcl_ret_t ret = rcl_subscription_options_set_content_filter_options(
|
||||
get_c_string(content_filter_options.filter_expression),
|
||||
cstrings.size(),
|
||||
cstrings.data(),
|
||||
&result);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(
|
||||
ret, "failed to set content_filter_options");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Get the allocator, creating one if needed.
|
||||
std::shared_ptr<Allocator>
|
||||
get_allocator() const
|
||||
{
|
||||
if (!this->allocator) {
|
||||
return std::make_shared<Allocator>();
|
||||
if (!allocator_storage_) {
|
||||
allocator_storage_ = std::make_shared<Allocator>();
|
||||
}
|
||||
return allocator_storage_;
|
||||
}
|
||||
return this->allocator;
|
||||
}
|
||||
|
||||
private:
|
||||
using PlainAllocator =
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc<char>;
|
||||
|
||||
rcl_allocator_t
|
||||
get_rcl_allocator() const
|
||||
{
|
||||
if (!plain_allocator_storage_) {
|
||||
plain_allocator_storage_ =
|
||||
std::make_shared<PlainAllocator>(*this->get_allocator());
|
||||
}
|
||||
return rclcpp::allocator::get_rcl_allocator<char>(*plain_allocator_storage_);
|
||||
}
|
||||
|
||||
// This is a temporal workaround, to make sure that get_allocator()
|
||||
// always returns a copy of the same allocator.
|
||||
mutable std::shared_ptr<Allocator> allocator_storage_;
|
||||
|
||||
// This is a temporal workaround, to keep the plain allocator that backs
|
||||
// up the rcl allocator returned in rcl_subscription_options_t alive.
|
||||
mutable std::shared_ptr<PlainAllocator> plain_allocator_storage_;
|
||||
};
|
||||
|
||||
using SubscriptionOptions = SubscriptionOptionsWithAllocator<std::allocator<void>>;
|
||||
|
||||
@@ -64,7 +64,7 @@ struct is_serialized_callback
|
||||
template<typename MessageT>
|
||||
struct extract_message_type
|
||||
{
|
||||
using type = typename std::remove_cv<MessageT>::type;
|
||||
using type = typename std::remove_cv_t<std::remove_reference_t<MessageT>>;
|
||||
};
|
||||
|
||||
template<typename MessageT>
|
||||
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
* \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);
|
||||
|
||||
/// Copy constructor
|
||||
RCLCPP_PUBLIC
|
||||
@@ -66,12 +66,11 @@ public:
|
||||
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
|
||||
* \param clock_type clock type
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
explicit Time(const rcl_time_point_t & time_point);
|
||||
@@ -91,6 +90,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);
|
||||
@@ -213,6 +218,7 @@ private:
|
||||
/**
|
||||
* \throws std::overflow_error if addition leads to overflow
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
Time
|
||||
operator+(const rclcpp::Duration & lhs, const rclcpp::Time & rhs);
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "rcl_interfaces/msg/parameter_event.hpp"
|
||||
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/executors.hpp"
|
||||
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
|
||||
|
||||
|
||||
@@ -32,6 +33,20 @@ namespace rclcpp
|
||||
{
|
||||
class Clock;
|
||||
|
||||
/**
|
||||
* Time source that will drive the attached clocks.
|
||||
*
|
||||
* If the attached node `use_sim_time` parameter is `true`, the attached clocks will
|
||||
* be updated based on messages received.
|
||||
*
|
||||
* The subscription to the clock topic created by the time source can have it's qos reconfigured
|
||||
* using parameter overrides, particularly the following ones are accepted:
|
||||
*
|
||||
* - qos_overrides./clock.depth
|
||||
* - qos_overrides./clock.durability
|
||||
* - qos_overrides./clock.history
|
||||
* - qos_overrides./clock.reliability
|
||||
*/
|
||||
class TimeSource
|
||||
{
|
||||
public:
|
||||
@@ -40,25 +55,43 @@ public:
|
||||
* The node will be attached to the time source.
|
||||
*
|
||||
* \param node std::shared pointer to a initialized node
|
||||
* \param qos QoS that will be used when creating a `/clock` subscription.
|
||||
* \param use_clock_thread whether to spin the attached node in a separate thread
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
explicit TimeSource(rclcpp::Node::SharedPtr node);
|
||||
explicit TimeSource(
|
||||
rclcpp::Node::SharedPtr node,
|
||||
const rclcpp::QoS & qos = rclcpp::ClockQoS(),
|
||||
bool use_clock_thread = true);
|
||||
|
||||
/// Empty constructor
|
||||
/**
|
||||
* An Empty TimeSource class
|
||||
*
|
||||
* \param qos QoS that will be used when creating a `/clock` subscription.
|
||||
* \param use_clock_thread whether to spin the attached node in a separate thread.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
TimeSource();
|
||||
explicit TimeSource(
|
||||
const rclcpp::QoS & qos = rclcpp::ClockQoS(),
|
||||
bool use_clock_thread = true);
|
||||
|
||||
/// Attack node to the time source.
|
||||
// The TimeSource is uncopyable
|
||||
TimeSource(const TimeSource &) = delete;
|
||||
TimeSource & operator=(const TimeSource &) = delete;
|
||||
|
||||
// The TimeSource is moveable
|
||||
TimeSource(TimeSource &&) = default;
|
||||
TimeSource & operator=(TimeSource &&) = default;
|
||||
|
||||
/// Attach node to the time source.
|
||||
/**
|
||||
* \param node std::shared pointer to a initialized node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void attachNode(rclcpp::Node::SharedPtr node);
|
||||
|
||||
/// Attack node to the time source.
|
||||
/// Attach node to the time source.
|
||||
/**
|
||||
* If the parameter `use_sim_time` is `true` then the source time is the simulation time,
|
||||
* otherwise the source time is defined by the system.
|
||||
@@ -93,80 +126,33 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
void attachClock(rclcpp::Clock::SharedPtr clock);
|
||||
|
||||
/// Detach a clock to the time source
|
||||
/// Detach a clock from the time source
|
||||
RCLCPP_PUBLIC
|
||||
void detachClock(rclcpp::Clock::SharedPtr clock);
|
||||
|
||||
/// Get whether a separate clock thread is used or not
|
||||
RCLCPP_PUBLIC
|
||||
bool get_use_clock_thread();
|
||||
|
||||
/// Set whether to use a separate clock thread or not
|
||||
RCLCPP_PUBLIC
|
||||
void set_use_clock_thread(bool use_clock_thread);
|
||||
|
||||
/// Check if the clock thread is joinable
|
||||
RCLCPP_PUBLIC
|
||||
bool clock_thread_is_joinable();
|
||||
|
||||
/// TimeSource Destructor
|
||||
RCLCPP_PUBLIC
|
||||
~TimeSource();
|
||||
|
||||
private:
|
||||
// Preserve the node reference
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_;
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_;
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_;
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_;
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_;
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
|
||||
class NodeState;
|
||||
std::shared_ptr<NodeState> node_state_;
|
||||
|
||||
// Store (and update on node attach) logger for logging.
|
||||
Logger logger_;
|
||||
|
||||
// The subscription for the clock callback
|
||||
using MessageT = rosgraph_msgs::msg::Clock;
|
||||
using Alloc = std::allocator<void>;
|
||||
using SubscriptionT = rclcpp::Subscription<MessageT, Alloc>;
|
||||
std::shared_ptr<SubscriptionT> clock_subscription_;
|
||||
std::mutex clock_sub_lock_;
|
||||
|
||||
// The clock callback itself
|
||||
void clock_cb(const rosgraph_msgs::msg::Clock::SharedPtr msg);
|
||||
|
||||
// Create the subscription for the clock topic
|
||||
void create_clock_sub();
|
||||
|
||||
// Destroy the subscription for the clock topic
|
||||
void destroy_clock_sub();
|
||||
|
||||
// Parameter Event subscription
|
||||
using ParamMessageT = rcl_interfaces::msg::ParameterEvent;
|
||||
using ParamSubscriptionT = rclcpp::Subscription<ParamMessageT, Alloc>;
|
||||
std::shared_ptr<ParamSubscriptionT> parameter_subscription_;
|
||||
|
||||
// Callback for parameter updates
|
||||
void on_parameter_event(const rcl_interfaces::msg::ParameterEvent::SharedPtr event);
|
||||
|
||||
// An enum to hold the parameter state
|
||||
enum UseSimTimeParameterState {UNSET, SET_TRUE, SET_FALSE};
|
||||
UseSimTimeParameterState parameter_state_;
|
||||
|
||||
// An internal method to use in the clock callback that iterates and enables all clocks
|
||||
void enable_ros_time();
|
||||
// An internal method to use in the clock callback that iterates and disables all clocks
|
||||
void disable_ros_time();
|
||||
|
||||
// Internal helper functions used inside iterators
|
||||
static void enable_ros_time(rclcpp::Clock::SharedPtr clock);
|
||||
static void disable_ros_time(rclcpp::Clock::SharedPtr clock);
|
||||
static void set_clock(
|
||||
const builtin_interfaces::msg::Time::SharedPtr msg,
|
||||
bool set_ros_time_enabled,
|
||||
rclcpp::Clock::SharedPtr clock);
|
||||
|
||||
// Local storage of validity of ROS time
|
||||
// This is needed when new clocks are added.
|
||||
bool ros_time_active_;
|
||||
// Last set message to be passed to newly registered clocks
|
||||
rosgraph_msgs::msg::Clock::SharedPtr last_msg_set_;
|
||||
|
||||
// A lock to protect iterating the associated_clocks_ field.
|
||||
std::mutex clock_list_lock_;
|
||||
// A vector to store references to associated clocks.
|
||||
std::vector<rclcpp::Clock::SharedPtr> associated_clocks_;
|
||||
// A handler for the use_sim_time parameter callback.
|
||||
rclcpp::node_interfaces::OnSetParametersCallbackHandle::SharedPtr sim_time_cb_handler_ = nullptr;
|
||||
// Preserve the arguments received by the constructor for reuse at runtime
|
||||
bool constructed_use_clock_thread_;
|
||||
rclcpp::QoS constructed_qos_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -91,6 +91,17 @@ public:
|
||||
void
|
||||
reset();
|
||||
|
||||
/// Indicate that we're about to execute the callback.
|
||||
/**
|
||||
* The multithreaded executor takes advantage of this to avoid scheduling
|
||||
* the callback multiple times.
|
||||
*
|
||||
* \return `true` if the callback should be executed, `false` if the timer was canceled.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual bool
|
||||
call() = 0;
|
||||
|
||||
/// Call the callback function when the timer signal is emitted.
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
@@ -102,7 +113,8 @@ public:
|
||||
|
||||
/// Check how long the timer has until its next scheduled callback.
|
||||
/**
|
||||
* \return A std::chrono::duration representing the relative time until the next callback.
|
||||
* \return A std::chrono::duration representing the relative time until the next callback
|
||||
* or std::chrono::nanoseconds::max() if the timer is canceled.
|
||||
* \throws std::runtime_error if the rcl_timer_get_time_until_next_call returns a failure
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
@@ -166,6 +178,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,
|
||||
@@ -175,12 +188,12 @@ public:
|
||||
{
|
||||
TRACEPOINT(
|
||||
rclcpp_timer_callback_added,
|
||||
(const void *)get_timer_handle().get(),
|
||||
(const void *)&callback_);
|
||||
static_cast<const void *>(get_timer_handle().get()),
|
||||
static_cast<const void *>(&callback_));
|
||||
TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
(const void *)&callback_,
|
||||
get_symbol(callback_));
|
||||
static_cast<const void *>(&callback_),
|
||||
tracetools::get_symbol(callback_));
|
||||
}
|
||||
|
||||
/// Default destructor.
|
||||
@@ -191,22 +204,31 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* \sa rclcpp::TimerBase::execute_callback
|
||||
* \throws std::runtime_error if it failed to notify timer that callback occurred
|
||||
* \sa rclcpp::TimerBase::call
|
||||
* \throws std::runtime_error if it failed to notify timer that callback will occurr
|
||||
*/
|
||||
void
|
||||
execute_callback() override
|
||||
bool
|
||||
call() override
|
||||
{
|
||||
rcl_ret_t ret = rcl_timer_call(timer_handle_.get());
|
||||
if (ret == RCL_RET_TIMER_CANCELED) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (ret != RCL_RET_OK) {
|
||||
throw std::runtime_error("Failed to notify timer that callback occurred");
|
||||
}
|
||||
TRACEPOINT(callback_start, (const void *)&callback_, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \sa rclcpp::TimerBase::execute_callback
|
||||
*/
|
||||
void
|
||||
execute_callback() override
|
||||
{
|
||||
TRACEPOINT(callback_start, static_cast<const void *>(&callback_), false);
|
||||
execute_callback_delegate<>();
|
||||
TRACEPOINT(callback_end, (const void *)&callback_);
|
||||
TRACEPOINT(callback_end, static_cast<const void *>(&callback_));
|
||||
}
|
||||
|
||||
// void specialization
|
||||
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
|
||||
/// Set the timer used to publish statistics messages.
|
||||
/**
|
||||
* \param measurement_timer the timer to fire the publisher, created by the node
|
||||
* \param publisher_timer the timer to fire the publisher, created by the node
|
||||
*/
|
||||
void set_publisher_timer(rclcpp::TimerBase::SharedPtr publisher_timer)
|
||||
{
|
||||
@@ -127,7 +127,7 @@ public:
|
||||
/**
|
||||
* This method acquires a lock to prevent race conditions to collectors list.
|
||||
*/
|
||||
virtual void publish_message()
|
||||
virtual void publish_message_and_reset_measurements()
|
||||
{
|
||||
std::vector<MetricsMessage> msgs;
|
||||
rclcpp::Time window_end{get_current_nanoseconds_since_epoch()};
|
||||
@@ -136,6 +136,7 @@ public:
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (auto & collector : subscriber_statistics_collectors_) {
|
||||
const auto collected_stats = collector->GetStatisticsResults();
|
||||
collector->ClearCurrentMeasurements();
|
||||
|
||||
auto message = libstatistics_collector::collector::GenerateStatisticMessage(
|
||||
node_name_,
|
||||
|
||||
201
rclcpp/include/rclcpp/type_adapter.hpp
Normal file
201
rclcpp/include/rclcpp/type_adapter.hpp
Normal file
@@ -0,0 +1,201 @@
|
||||
// Copyright 2021 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__TYPE_ADAPTER_HPP_
|
||||
#define RCLCPP__TYPE_ADAPTER_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Template structure used to adapt custom, user-defined types to ROS types.
|
||||
/**
|
||||
* Adapting a custom, user-defined type to a ROS type allows that custom type
|
||||
* to be used when publishing and subscribing in ROS.
|
||||
*
|
||||
* In order to adapt a custom type to a ROS type, the user must create a
|
||||
* template specialization of this structure for the custom type.
|
||||
* In that specialization they must:
|
||||
*
|
||||
* - change `is_specialized` to `std::true_type`,
|
||||
* - specify the custom type with `using custom_type = ...`,
|
||||
* - specify the ROS type with `using ros_message_type = ...`,
|
||||
* - provide static convert functions with the signatures:
|
||||
* - static void convert_to_ros(const custom_type &, ros_message_type &)
|
||||
* - static void convert_to_custom(const ros_message_type &, custom_type &)
|
||||
*
|
||||
* The convert functions must convert from one type to the other.
|
||||
*
|
||||
* For example, here is a theoretical example for adapting `std::string` to the
|
||||
* `std_msgs::msg::String` ROS message type:
|
||||
*
|
||||
* template<>
|
||||
* struct rclcpp::TypeAdapter<std::string, std_msgs::msg::String>
|
||||
* {
|
||||
* using is_specialized = std::true_type;
|
||||
* using custom_type = std::string;
|
||||
* using ros_message_type = std_msgs::msg::String;
|
||||
*
|
||||
* static
|
||||
* void
|
||||
* convert_to_ros_message(
|
||||
* const custom_type & source,
|
||||
* ros_message_type & destination)
|
||||
* {
|
||||
* destination.data = source;
|
||||
* }
|
||||
*
|
||||
* static
|
||||
* void
|
||||
* convert_to_custom(
|
||||
* const ros_message_type & source,
|
||||
* custom_type & destination)
|
||||
* {
|
||||
* destination = source.data;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* The adapter can then be used when creating a publisher or subscription,
|
||||
* e.g.:
|
||||
*
|
||||
* using MyAdaptedType = TypeAdapter<std::string, std_msgs::msg::String>;
|
||||
* auto pub = node->create_publisher<MyAdaptedType>("topic", 10);
|
||||
* auto sub = node->create_subscription<MyAdaptedType>(
|
||||
* "topic",
|
||||
* 10,
|
||||
* [](const std::string & msg) {...});
|
||||
*
|
||||
* You can also be more declarative by using the adapt_type::as metafunctions,
|
||||
* which are a bit less ambiguous to read:
|
||||
*
|
||||
* using AdaptedType = rclcpp::adapt_type<std::string>::as<std_msgs::msg::String>;
|
||||
* auto pub = node->create_publisher<AdaptedType>(...);
|
||||
*
|
||||
* If you wish, you may associate a custom type with a single ROS message type,
|
||||
* allowing you to be a bit more brief when creating entities, e.g.:
|
||||
*
|
||||
* // First you must declare the association, this is similar to how you
|
||||
* // would avoid using the namespace in C++ by doing `using std::vector;`.
|
||||
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(std::string, std_msgs::msg::String);
|
||||
*
|
||||
* // Then you can create things with just the custom type, and the ROS
|
||||
* // message type is implied based on the previous statement.
|
||||
* auto pub = node->create_publisher<std::string>(...);
|
||||
*/
|
||||
template<typename CustomType, typename ROSMessageType = void, class Enable = void>
|
||||
struct TypeAdapter
|
||||
{
|
||||
using is_specialized = std::false_type;
|
||||
using custom_type = CustomType;
|
||||
// In this case, the CustomType is the only thing given, or there is no specialization.
|
||||
// Assign ros_message_type to CustomType for the former case.
|
||||
using ros_message_type = CustomType;
|
||||
};
|
||||
|
||||
/// Helper template to determine if a type is a TypeAdapter, false specialization.
|
||||
template<typename T>
|
||||
struct is_type_adapter : std::false_type {};
|
||||
|
||||
/// Helper template to determine if a type is a TypeAdapter, true specialization.
|
||||
template<typename ... Ts>
|
||||
struct is_type_adapter<TypeAdapter<Ts...>>: std::true_type {};
|
||||
|
||||
/// Identity specialization for TypeAdapter.
|
||||
template<typename T>
|
||||
struct TypeAdapter<T, void, std::enable_if_t<is_type_adapter<T>::value>>: T {};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename CustomType, typename ROSMessageType>
|
||||
struct assert_type_pair_is_specialized_type_adapter
|
||||
{
|
||||
using type_adapter = TypeAdapter<CustomType, ROSMessageType>;
|
||||
static_assert(
|
||||
type_adapter::is_specialized::value,
|
||||
"No type adapter for this custom type/ros message type pair");
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// Template metafunction that can make the type being adapted explicit.
|
||||
template<typename CustomType>
|
||||
struct adapt_type
|
||||
{
|
||||
template<typename ROSMessageType>
|
||||
using as = typename ::rclcpp::detail::assert_type_pair_is_specialized_type_adapter<
|
||||
CustomType,
|
||||
ROSMessageType
|
||||
>::type_adapter;
|
||||
};
|
||||
|
||||
/// Implicit type adapter used as a short hand way to create something with just the custom type.
|
||||
/**
|
||||
* This is used when creating a publisher or subscription using just the custom
|
||||
* type in conjunction with RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE().
|
||||
* For example:
|
||||
*
|
||||
* #include "type_adapter_for_std_string_to_std_msgs_String.hpp"
|
||||
*
|
||||
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(std::string, std_msgs::msg::String);
|
||||
*
|
||||
* int main(...) {
|
||||
* // ...
|
||||
* auto pub = node->create_publisher<std::string>(...);
|
||||
* }
|
||||
*
|
||||
* \sa TypeAdapter for more examples.
|
||||
*/
|
||||
template<typename CustomType>
|
||||
struct ImplicitTypeAdapter
|
||||
{
|
||||
using is_specialized = std::false_type;
|
||||
};
|
||||
|
||||
/// Specialization of TypeAdapter for ImplicitTypeAdapter.
|
||||
/**
|
||||
* This allows for things like this:
|
||||
*
|
||||
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(std::string, std_msgs::msg::String);
|
||||
* auto pub = node->create_publisher<std::string>("topic", 10);
|
||||
*
|
||||
*/
|
||||
template<typename T>
|
||||
struct TypeAdapter<T, void, std::enable_if_t<ImplicitTypeAdapter<T>::is_specialized::value>>
|
||||
: ImplicitTypeAdapter<T>
|
||||
{};
|
||||
|
||||
/// Assigns the custom type implicitly to the given custom type/ros message type pair.
|
||||
/**
|
||||
* Note: this macro needs to be used in the root namespace.
|
||||
* We cannot use ::rclcpp to protect against this, due to how GCC interprets the
|
||||
* syntax, see: https://stackoverflow.com/a/2781537
|
||||
*
|
||||
* \sa TypeAdapter
|
||||
* \sa ImplicitTypeAdapter
|
||||
*/
|
||||
#define RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(CustomType, ROSMessageType) \
|
||||
template<> \
|
||||
struct rclcpp::ImplicitTypeAdapter<CustomType> \
|
||||
: public rclcpp::TypeAdapter<CustomType, ROSMessageType> \
|
||||
{ \
|
||||
static_assert( \
|
||||
is_specialized::value, \
|
||||
"Cannot use custom type as ros type when there is no TypeAdapter for that pair"); \
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__TYPE_ADAPTER_HPP_
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user