diff --git a/ubuntu/ros2/iron/heartbeat/py/CHANGELOG.rst b/ubuntu/ros2/iron/heartbeat/py/CHANGELOG.rst new file mode 100755 index 0000000..edd2882 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/CHANGELOG.rst @@ -0,0 +1,230 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package demo_nodes_py +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +0.34.0 (2024-04-26) +------------------- + +0.33.2 (2024-03-28) +------------------- +* Update maintainer list in package.xml files (`#665 `_) +* Contributors: Michael Jeronimo + +0.33.1 (2024-02-07) +------------------- + +0.33.0 (2024-01-24) +------------------- + +0.32.1 (2023-12-26) +------------------- + +0.32.0 (2023-11-06) +------------------- + +0.31.1 (2023-09-07) +------------------- + +0.31.0 (2023-08-21) +------------------- + +0.30.1 (2023-07-11) +------------------- + +0.30.0 (2023-06-12) +------------------- +* Change the service introspection parameter off value to 'disabled' (`#634 `_) + With this we can avoid the tricky bits around YAML + interpretation of 'off' as a boolean. +* Contributors: Chris Lalancette + +0.29.0 (2023-06-07) +------------------- +* Add demos for using logger service (`#611 `_) +* Contributors: Barry Xu + +0.28.1 (2023-05-11) +------------------- + +0.28.0 (2023-04-27) +------------------- + +0.27.0 (2023-04-13) +------------------- +* Change all ROS2 -> ROS 2. (`#610 `_) +* Add matched event demo for rclcpp and rclpy (`#607 `_) +* Contributors: Barry Xu, Chris Lalancette + +0.26.0 (2023-04-11) +------------------- +* Enable document generation using rosdoc2 (`#606 `_) +* Contributors: Yadu + +0.25.0 (2023-03-01) +------------------- +* Service introspection (`#602 `_) +* Contributors: Chris Lalancette + +0.24.1 (2023-02-24) +------------------- + +0.24.0 (2023-02-14) +------------------- +* Added README.md for demo_nodes_py (`#600 `_) +* [rolling] Update maintainers - 2022-11-07 (`#589 `_) +* Contributors: Audrow Nash, Gary Bey + +0.23.0 (2022-11-02) +------------------- +* Demo for pre and post set parameter callback support (`#565 `_) +* Contributors: Deepanshu Bansal + +0.22.0 (2022-09-13) +------------------- +* Add demo for rclpy parameter client (`#566 `_) +* Exit with code 0 if ExternalShutdownException is raised (`#581 `_) +* Contributors: Brian, Jacob Perron + +0.21.0 (2022-04-29) +------------------- + +0.20.1 (2022-04-08) +------------------- + +0.20.0 (2022-03-01) +------------------- +* Cleanups in demo_nodes_py. (`#555 `_) +* Contributors: Chris Lalancette + +0.19.0 (2022-01-14) +------------------- + +0.18.0 (2021-12-17) +------------------- +* Update maintainers to Audrow Nash and Michael Jeronimo (`#543 `_) +* Fixed typo executor -> executors (`#542 `_) +* Update python nodes SIGINT handling (`#539 `_) +* Contributors: Audrow Nash, Ivan Santiago Paunovic, ori155 + +0.17.0 (2021-10-18) +------------------- + +0.16.0 (2021-08-11) +------------------- + +0.15.0 (2021-05-14) +------------------- + +0.14.2 (2021-04-26) +------------------- + +0.14.1 (2021-04-19) +------------------- +* Use underscores instead of dashes in setup.cfg (`#502 `_) +* Contributors: Ivan Santiago Paunovic + +0.14.0 (2021-04-06) +------------------- + +0.13.0 (2021-03-25) +------------------- + +0.12.1 (2021-03-18) +------------------- + +0.12.0 (2021-01-25) +------------------- + +0.11.0 (2020-12-10) +------------------- +* Update deprecated qos policy value names (`#468 `_) +* Update the package.xml files with the latest Open Robotics maintainers (`#466 `_) +* Contributors: Ivan Santiago Paunovic, Michael Jeronimo + +0.10.1 (2020-09-21) +------------------- + +0.10.0 (2020-06-17) +------------------- + +0.9.3 (2020-06-01) +------------------ + +0.9.2 (2020-05-26) +------------------ + +0.9.1 (2020-05-12) +------------------ + +0.9.0 (2020-04-30) +------------------ +* more verbose test_flake8 error messages (same as `ros2/launch_ros#135 `_) +* Contributors: Dirk Thomas + +0.8.4 (2019-11-19) +------------------ +* Make demos handle SIGINT gracefully. (`#377 `_) +* Contributors: Michel Hidalgo + +0.8.3 (2019-11-11) +------------------ + +0.8.2 (2019-11-08) +------------------ + +0.8.1 (2019-10-23) +------------------ +* Prevent argparse from parsing ROS args in Python demo nodes. (`#396 `_) +* Update setup.py versions +* Contributors: Jacob Perron, Michel Hidalgo + +0.8.0 (2019-09-26) +------------------ + +0.7.6 (2019-05-30) +------------------ + +0.7.5 (2019-05-29) +------------------ +* Fix reliable option for Python QoS demo (`#352 `_) +* Contributors: Jacob Perron + +0.7.4 (2019-05-20) +------------------ +* Fix deprecation warnings (`#334 `_) +* Contributors: Jacob Perron + +0.7.3 (2019-05-10) +------------------ + +0.7.2 (2019-05-08) +------------------ + +0.7.1 (2019-04-26) +------------------ + +0.7.0 (2019-04-14) +------------------ + +0.6.2 (2019-01-15) +------------------ +* Added serialized listener demo for python (`#287 `_) +* Contributors: Joseph Duchesne + +0.6.1 (2018-12-13) +------------------ + +0.6.0 (2018-12-07) +------------------ +* Updated package maintainer. (`#286 `_) +* Removed now redundant args=sys.argv (`#274 `_) +* Contributors: Michael Carroll, Mikael Arguedas + +0.5.1 (2018-06-28) +------------------ + +0.5.0 (2018-06-27) +------------------ +* Updated demos so that they support remapping arguments to python nodes by passing arguments to rclpy from argparse. (`#252 `_ and `#253 `_) +* Updated client demo to handle multiple requests. (`#228 `_) +* Contributors: Dirk Thomas, Mikael Arguedas, Shane Loretz, dhood diff --git a/ubuntu/ros2/iron/heartbeat/py/README.md b/ubuntu/ros2/iron/heartbeat/py/README.md new file mode 100755 index 0000000..88f5a7c --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/README.md @@ -0,0 +1,278 @@ +## **What Is This?** + +This package contains the following ROS 2 nodes: + +1. `listener` +2. `talker` +3. `add_two_ints_client` +4. `add_two_ints_server` +5. `set_parameters_callback` +6. `add_two_ints_client_async` +7. `listener_qos` +8. `talker_qos` +9. `listerner_serialized` +10. `async_param_client` + +## **Build** + +Run this command to build the package: + +```bash +colcon build --packages-select demo_nodes_py +``` + +## **Run** + +### Basic Listener & Talker + +This runs `talker` and `listener` ROS 2 nodes which exchange the following string with incremeting integer: + +> PUBLISHER sending Heartbeat messages: + +```bash +# Open new terminal +ros2 run demo_nodes_py talker +``` + +```bash +# Open new terminal +ros2 run demo_nodes_py listener +``` + +![](img/talker_listener.png) + +### Server & Client + +This runs `add_two_ints_client` and `add_two_ints_server` ROS 2 client and server where the server processes two integers sent from the client and publishes its sum to the client to be printed out. + +#### Server + +```bash +# Open new terminal +ros2 run demo_nodes_py add_two_ints_server +``` + +#### Client [Synchronous] + +```bash +# Open new terminal +ros2 run demo_nodes_py add_two_ints_client +``` + +#### Client [Asynchronous] + +```bash +# Open new terminal +ros2 run demo_nodes_py add_two_ints_client_async +``` +![](img/server_client.png) + +### QoS Listener & Talker + +Similar to previous, this runs `talker_qos` and `listener_qos` ROS 2 nodes which exchange messages using the Quality of Service (QoS) best effort policy. + +```bash +# Open new terminal +ros2 run demo_nodes_py talker_qos +``` + +```bash +# Open new terminal +ros2 run demo_nodes_py listener_qos +``` + +![](img/qos_listener_talker.png) + + +### Set Parameters Callback + +This runs `set_parameters_callback` ROS 2 node which triggers a callback when ROS 2 double parameter `param1` is set. The callback then sets ROS 2 double parameter `param2` to a fixed `4.0`: + +```bash +# Open new terminal +ros2 run demo_nodes_py set_parameters_callback +``` + +![](img/set_parameters_callback.png) + +### Asynchronous Parameter Client + +This runs `async_param_client` ROS 2 node which demonstrates how to set, list, get, load and delete parameters from the parameter blackboard in an asynchronous manner: + +```bash +# Open new terminal +ros2 run demo_nodes_cpp parameter_blackboard +``` + +> For more details on parameter_blackboard, please refer to demo_nodes_cpp README.md. + +```bash +# Open new terminal +ros2 run demo_nodes_py async_param_client +``` + +### Serialized Subscriber + +This runs `serialized_subscriber` ROS 2 node that subscribes to the ROS 2 topic, `/chatter`, and deserializes as well as prints out received strings: + +```bash +# Open new terminal +ros2 run demo_nodes_py listener_serialized +``` + +![](img/serialized_subscriber.png) + +## **Verify** + +### Basic Listener & Talker + +When executed correctly, strings should be printed to the terminal similar to what is shown below: + +```bash +# In terminal running talker +[INFO] [1674573022.635075580] [talker]: Publishing: "Hello World: 0" +[INFO] [1674573023.593075728] [talker]: Publishing: "Hello World: 1" +[INFO] [1674573024.592438479] [talker]: Publishing: "Hello World: 2" +``` + +```bash +# In terminal running listener +[INFO] [1674573025.634810166] [listener]: I heard: [Hello World: 0] +[INFO] [1674573026.596392653] [listener]: I heard: [Hello World: 1] +[INFO] [1674573027.596400384] [listener]: I heard: [Hello World: 2] +#... +``` + +### Basic Server & Client + +When executed correctly, strings should be printed to the terminal similar to what is shown below: + +#### Server +```bash +# In terminal running add_two_ints_server +[INFO] [1674553268.912391774] [add_two_ints_server]: Incoming request +a: 2 b: 3 +``` + +#### Client [Synchronous] +```bash +# In terminal running add_two_ints_client +[INFO] [1674553268.912602310] [add_two_ints_client]: Result of add_two_ints: 5 +``` + +#### Client [Asynchronous] +```bash +# In terminal running add_two_ints_client_async +[INFO] [1674553718.598690033] [add_two_ints_client]: Result of add_two_ints: 5 +``` + +### QoS Listener & Talker + +When executed correctly, strings should be printed to the terminal similar to what is shown below: + +```bash +# In terminal running talker_qos +[INFO] [1674572630.018298164] [talker_qos]: Best effort talker +[INFO] [1674572631.021168516] [talker_qos]: Publishing: "Hello World: 0" +[INFO] [1674572632.021072723] [talker_qos]: Publishing: "Hello World: 1" +[INFO] [1674572633.021197421] [talker_qos]: Publishing: "Hello World: 2" +#... +``` + +```bash +# In terminal running listener_qos +[INFO] [1674572639.695289182] [listener_qos]: Best effort listener +[INFO] [1674572640.023684925] [listener_qos]: I heard: [Hello World: 0] +[INFO] [1674572641.023416691] [listener_qos]: I heard: [Hello World: 1] +[INFO] [1674572642.024505343] [listener_qos]: I heard: [Hello World: 2] +#... +``` + +### Set Parameters Callback +#### [Before] +Run `ros2 param get /set_parameters_callback param1` should print the following to the terminal: +```bash +Double value is: 0.0 +``` +Run `ros2 param get /set_parameters_callback param2` should print the following to the terminal: +```bash +Double value is 0.0 +``` +#### [Change] +Run `ros2 param set /set_parameters_callback param1 28.0` should print the following to the terminal: +```bash +Set parameter successful +``` +#### [After] +Run `ros2 param get /set_parameters_callback param1` should print the following to the terminal: +```bash +Double value is: 28.0 +``` +Run `ros2 param get /set_parameters_callback param2` should print the following to the terminal: +```bash +Double value is 4.0 +``` + +### Asynchronous Parameter Client + +Running `ros2 run demo_nodes_cpp parameter_blackboard` should print the output to the terminal similar to what is shown below: + +```bash +# In terminal running async_param_client +[INFO] [1674574042.977327137] [async_param_client]: Setting parameters: +[INFO] [1674574042.978217825] [async_param_client]: int_parameter: +[INFO] [1674574042.978474500] [async_param_client]: successful: True +[INFO] [1674574042.978718655] [async_param_client]: bool_parameter: +[INFO] [1674574042.978960532] [async_param_client]: successful: True +[INFO] [1674574042.979200840] [async_param_client]: string_parameter: +[INFO] [1674574042.979446692] [async_param_client]: successful: True +[INFO] [1674574042.979687030] [async_param_client]: Listing Parameters: +[INFO] [1674574042.980229123] [async_param_client]: - bool_parameter +[INFO] [1674574042.980476617] [async_param_client]: - int_parameter +[INFO] [1674574042.980717248] [async_param_client]: - string_parameter +[INFO] [1674574042.980956813] [async_param_client]: Getting parameters: +[INFO] [1674574042.981544032] [async_param_client]: - int_parameter: 10 +[INFO] [1674574042.981794832] [async_param_client]: - bool_parameter: False +[INFO] [1674574042.982038493] [async_param_client]: - string_parameter: Fee Fi Fo Fum +[INFO] [1674574042.982281892] [async_param_client]: Loading parameters: +[INFO] [1674574042.987160462] [async_param_client]: other_int_parameter: +[INFO] [1674574042.987431457] [async_param_client]: successful: True +[INFO] [1674574042.987679192] [async_param_client]: value: 0 +[INFO] [1674574042.987920948] [async_param_client]: int_parameter: +[INFO] [1674574042.988162645] [async_param_client]: successful: True +[INFO] [1674574042.988402691] [async_param_client]: value: 12 +[INFO] [1674574042.988643394] [async_param_client]: string_parameter: +[INFO] [1674574042.988884034] [async_param_client]: successful: True +[INFO] [1674574042.989125729] [async_param_client]: value: I smell the blood of an Englishman +[INFO] [1674574042.989365771] [async_param_client]: other_string_parameter: +[INFO] [1674574042.989605537] [async_param_client]: successful: True +[INFO] [1674574042.989847066] [async_param_client]: value: One fish, two fish, Red fish, blue fish +[INFO] [1674574042.990086215] [async_param_client]: bool_parameter: +[INFO] [1674574042.990325361] [async_param_client]: successful: True +[INFO] [1674574042.990565005] [async_param_client]: value: False +[INFO] [1674574042.990804701] [async_param_client]: Deleting parameters: +[INFO] [1674574042.991497679] [async_param_client]: other_int_parameter: +[INFO] [1674574042.991746545] [async_param_client]: successful: True +[INFO] [1674574042.991986980] [async_param_client]: reason: +[INFO] [1674574042.992226951] [async_param_client]: other_string_parameter: +[INFO] [1674574042.992465274] [async_param_client]: successful: True +[INFO] [1674574042.992703259] [async_param_client]: reason: +[INFO] [1674574042.992941566] [async_param_client]: string_parameter: +[INFO] [1674574042.993179508] [async_param_client]: successful: True +[INFO] [1674574042.993416599] [async_param_client]: reason: +``` + +### Serialized Subscriber + +Running `ros2 run demo_nodes_py talker` should print output to the terminal similar to the following: + +```bash +# In terminal running listener_serialized +[INFO] [1674574499.556053634] [serialized_subscriber]: I heard: "b'\x00\x01\x00\x00\x0f\x00\x00\x00Hello World: 0\x00\x00'" +[INFO] [1674574500.506343148] [serialized_subscriber]: I heard: "b'\x00\x01\x00\x00\x0f\x00\x00\x00Hello World: 1\x00\x00'" +[INFO] [1674574501.508214693] [serialized_subscriber]: I heard: "b'\x00\x01\x00\x00\x0f\x00\x00\x00Hello World: 2\x00\x00'" +``` + +## **References** + +1. [About Quality of Service Settings](https://docs.ros.org/en/rolling/Concepts/About-Quality-of-Service-Settings.html) diff --git a/ubuntu/ros2/iron/heartbeat/py/archv/CHANGELOG.rst b/ubuntu/ros2/iron/heartbeat/py/archv/CHANGELOG.rst new file mode 100755 index 0000000..edd2882 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/archv/CHANGELOG.rst @@ -0,0 +1,230 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package demo_nodes_py +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +0.34.0 (2024-04-26) +------------------- + +0.33.2 (2024-03-28) +------------------- +* Update maintainer list in package.xml files (`#665 `_) +* Contributors: Michael Jeronimo + +0.33.1 (2024-02-07) +------------------- + +0.33.0 (2024-01-24) +------------------- + +0.32.1 (2023-12-26) +------------------- + +0.32.0 (2023-11-06) +------------------- + +0.31.1 (2023-09-07) +------------------- + +0.31.0 (2023-08-21) +------------------- + +0.30.1 (2023-07-11) +------------------- + +0.30.0 (2023-06-12) +------------------- +* Change the service introspection parameter off value to 'disabled' (`#634 `_) + With this we can avoid the tricky bits around YAML + interpretation of 'off' as a boolean. +* Contributors: Chris Lalancette + +0.29.0 (2023-06-07) +------------------- +* Add demos for using logger service (`#611 `_) +* Contributors: Barry Xu + +0.28.1 (2023-05-11) +------------------- + +0.28.0 (2023-04-27) +------------------- + +0.27.0 (2023-04-13) +------------------- +* Change all ROS2 -> ROS 2. (`#610 `_) +* Add matched event demo for rclcpp and rclpy (`#607 `_) +* Contributors: Barry Xu, Chris Lalancette + +0.26.0 (2023-04-11) +------------------- +* Enable document generation using rosdoc2 (`#606 `_) +* Contributors: Yadu + +0.25.0 (2023-03-01) +------------------- +* Service introspection (`#602 `_) +* Contributors: Chris Lalancette + +0.24.1 (2023-02-24) +------------------- + +0.24.0 (2023-02-14) +------------------- +* Added README.md for demo_nodes_py (`#600 `_) +* [rolling] Update maintainers - 2022-11-07 (`#589 `_) +* Contributors: Audrow Nash, Gary Bey + +0.23.0 (2022-11-02) +------------------- +* Demo for pre and post set parameter callback support (`#565 `_) +* Contributors: Deepanshu Bansal + +0.22.0 (2022-09-13) +------------------- +* Add demo for rclpy parameter client (`#566 `_) +* Exit with code 0 if ExternalShutdownException is raised (`#581 `_) +* Contributors: Brian, Jacob Perron + +0.21.0 (2022-04-29) +------------------- + +0.20.1 (2022-04-08) +------------------- + +0.20.0 (2022-03-01) +------------------- +* Cleanups in demo_nodes_py. (`#555 `_) +* Contributors: Chris Lalancette + +0.19.0 (2022-01-14) +------------------- + +0.18.0 (2021-12-17) +------------------- +* Update maintainers to Audrow Nash and Michael Jeronimo (`#543 `_) +* Fixed typo executor -> executors (`#542 `_) +* Update python nodes SIGINT handling (`#539 `_) +* Contributors: Audrow Nash, Ivan Santiago Paunovic, ori155 + +0.17.0 (2021-10-18) +------------------- + +0.16.0 (2021-08-11) +------------------- + +0.15.0 (2021-05-14) +------------------- + +0.14.2 (2021-04-26) +------------------- + +0.14.1 (2021-04-19) +------------------- +* Use underscores instead of dashes in setup.cfg (`#502 `_) +* Contributors: Ivan Santiago Paunovic + +0.14.0 (2021-04-06) +------------------- + +0.13.0 (2021-03-25) +------------------- + +0.12.1 (2021-03-18) +------------------- + +0.12.0 (2021-01-25) +------------------- + +0.11.0 (2020-12-10) +------------------- +* Update deprecated qos policy value names (`#468 `_) +* Update the package.xml files with the latest Open Robotics maintainers (`#466 `_) +* Contributors: Ivan Santiago Paunovic, Michael Jeronimo + +0.10.1 (2020-09-21) +------------------- + +0.10.0 (2020-06-17) +------------------- + +0.9.3 (2020-06-01) +------------------ + +0.9.2 (2020-05-26) +------------------ + +0.9.1 (2020-05-12) +------------------ + +0.9.0 (2020-04-30) +------------------ +* more verbose test_flake8 error messages (same as `ros2/launch_ros#135 `_) +* Contributors: Dirk Thomas + +0.8.4 (2019-11-19) +------------------ +* Make demos handle SIGINT gracefully. (`#377 `_) +* Contributors: Michel Hidalgo + +0.8.3 (2019-11-11) +------------------ + +0.8.2 (2019-11-08) +------------------ + +0.8.1 (2019-10-23) +------------------ +* Prevent argparse from parsing ROS args in Python demo nodes. (`#396 `_) +* Update setup.py versions +* Contributors: Jacob Perron, Michel Hidalgo + +0.8.0 (2019-09-26) +------------------ + +0.7.6 (2019-05-30) +------------------ + +0.7.5 (2019-05-29) +------------------ +* Fix reliable option for Python QoS demo (`#352 `_) +* Contributors: Jacob Perron + +0.7.4 (2019-05-20) +------------------ +* Fix deprecation warnings (`#334 `_) +* Contributors: Jacob Perron + +0.7.3 (2019-05-10) +------------------ + +0.7.2 (2019-05-08) +------------------ + +0.7.1 (2019-04-26) +------------------ + +0.7.0 (2019-04-14) +------------------ + +0.6.2 (2019-01-15) +------------------ +* Added serialized listener demo for python (`#287 `_) +* Contributors: Joseph Duchesne + +0.6.1 (2018-12-13) +------------------ + +0.6.0 (2018-12-07) +------------------ +* Updated package maintainer. (`#286 `_) +* Removed now redundant args=sys.argv (`#274 `_) +* Contributors: Michael Carroll, Mikael Arguedas + +0.5.1 (2018-06-28) +------------------ + +0.5.0 (2018-06-27) +------------------ +* Updated demos so that they support remapping arguments to python nodes by passing arguments to rclpy from argparse. (`#252 `_ and `#253 `_) +* Updated client demo to handle multiple requests. (`#228 `_) +* Contributors: Dirk Thomas, Mikael Arguedas, Shane Loretz, dhood diff --git a/ubuntu/ros2/iron/heartbeat/py/archv/README.md b/ubuntu/ros2/iron/heartbeat/py/archv/README.md new file mode 100755 index 0000000..88f5a7c --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/archv/README.md @@ -0,0 +1,278 @@ +## **What Is This?** + +This package contains the following ROS 2 nodes: + +1. `listener` +2. `talker` +3. `add_two_ints_client` +4. `add_two_ints_server` +5. `set_parameters_callback` +6. `add_two_ints_client_async` +7. `listener_qos` +8. `talker_qos` +9. `listerner_serialized` +10. `async_param_client` + +## **Build** + +Run this command to build the package: + +```bash +colcon build --packages-select demo_nodes_py +``` + +## **Run** + +### Basic Listener & Talker + +This runs `talker` and `listener` ROS 2 nodes which exchange the following string with incremeting integer: + +> PUBLISHER sending Heartbeat messages: + +```bash +# Open new terminal +ros2 run demo_nodes_py talker +``` + +```bash +# Open new terminal +ros2 run demo_nodes_py listener +``` + +![](img/talker_listener.png) + +### Server & Client + +This runs `add_two_ints_client` and `add_two_ints_server` ROS 2 client and server where the server processes two integers sent from the client and publishes its sum to the client to be printed out. + +#### Server + +```bash +# Open new terminal +ros2 run demo_nodes_py add_two_ints_server +``` + +#### Client [Synchronous] + +```bash +# Open new terminal +ros2 run demo_nodes_py add_two_ints_client +``` + +#### Client [Asynchronous] + +```bash +# Open new terminal +ros2 run demo_nodes_py add_two_ints_client_async +``` +![](img/server_client.png) + +### QoS Listener & Talker + +Similar to previous, this runs `talker_qos` and `listener_qos` ROS 2 nodes which exchange messages using the Quality of Service (QoS) best effort policy. + +```bash +# Open new terminal +ros2 run demo_nodes_py talker_qos +``` + +```bash +# Open new terminal +ros2 run demo_nodes_py listener_qos +``` + +![](img/qos_listener_talker.png) + + +### Set Parameters Callback + +This runs `set_parameters_callback` ROS 2 node which triggers a callback when ROS 2 double parameter `param1` is set. The callback then sets ROS 2 double parameter `param2` to a fixed `4.0`: + +```bash +# Open new terminal +ros2 run demo_nodes_py set_parameters_callback +``` + +![](img/set_parameters_callback.png) + +### Asynchronous Parameter Client + +This runs `async_param_client` ROS 2 node which demonstrates how to set, list, get, load and delete parameters from the parameter blackboard in an asynchronous manner: + +```bash +# Open new terminal +ros2 run demo_nodes_cpp parameter_blackboard +``` + +> For more details on parameter_blackboard, please refer to demo_nodes_cpp README.md. + +```bash +# Open new terminal +ros2 run demo_nodes_py async_param_client +``` + +### Serialized Subscriber + +This runs `serialized_subscriber` ROS 2 node that subscribes to the ROS 2 topic, `/chatter`, and deserializes as well as prints out received strings: + +```bash +# Open new terminal +ros2 run demo_nodes_py listener_serialized +``` + +![](img/serialized_subscriber.png) + +## **Verify** + +### Basic Listener & Talker + +When executed correctly, strings should be printed to the terminal similar to what is shown below: + +```bash +# In terminal running talker +[INFO] [1674573022.635075580] [talker]: Publishing: "Hello World: 0" +[INFO] [1674573023.593075728] [talker]: Publishing: "Hello World: 1" +[INFO] [1674573024.592438479] [talker]: Publishing: "Hello World: 2" +``` + +```bash +# In terminal running listener +[INFO] [1674573025.634810166] [listener]: I heard: [Hello World: 0] +[INFO] [1674573026.596392653] [listener]: I heard: [Hello World: 1] +[INFO] [1674573027.596400384] [listener]: I heard: [Hello World: 2] +#... +``` + +### Basic Server & Client + +When executed correctly, strings should be printed to the terminal similar to what is shown below: + +#### Server +```bash +# In terminal running add_two_ints_server +[INFO] [1674553268.912391774] [add_two_ints_server]: Incoming request +a: 2 b: 3 +``` + +#### Client [Synchronous] +```bash +# In terminal running add_two_ints_client +[INFO] [1674553268.912602310] [add_two_ints_client]: Result of add_two_ints: 5 +``` + +#### Client [Asynchronous] +```bash +# In terminal running add_two_ints_client_async +[INFO] [1674553718.598690033] [add_two_ints_client]: Result of add_two_ints: 5 +``` + +### QoS Listener & Talker + +When executed correctly, strings should be printed to the terminal similar to what is shown below: + +```bash +# In terminal running talker_qos +[INFO] [1674572630.018298164] [talker_qos]: Best effort talker +[INFO] [1674572631.021168516] [talker_qos]: Publishing: "Hello World: 0" +[INFO] [1674572632.021072723] [talker_qos]: Publishing: "Hello World: 1" +[INFO] [1674572633.021197421] [talker_qos]: Publishing: "Hello World: 2" +#... +``` + +```bash +# In terminal running listener_qos +[INFO] [1674572639.695289182] [listener_qos]: Best effort listener +[INFO] [1674572640.023684925] [listener_qos]: I heard: [Hello World: 0] +[INFO] [1674572641.023416691] [listener_qos]: I heard: [Hello World: 1] +[INFO] [1674572642.024505343] [listener_qos]: I heard: [Hello World: 2] +#... +``` + +### Set Parameters Callback +#### [Before] +Run `ros2 param get /set_parameters_callback param1` should print the following to the terminal: +```bash +Double value is: 0.0 +``` +Run `ros2 param get /set_parameters_callback param2` should print the following to the terminal: +```bash +Double value is 0.0 +``` +#### [Change] +Run `ros2 param set /set_parameters_callback param1 28.0` should print the following to the terminal: +```bash +Set parameter successful +``` +#### [After] +Run `ros2 param get /set_parameters_callback param1` should print the following to the terminal: +```bash +Double value is: 28.0 +``` +Run `ros2 param get /set_parameters_callback param2` should print the following to the terminal: +```bash +Double value is 4.0 +``` + +### Asynchronous Parameter Client + +Running `ros2 run demo_nodes_cpp parameter_blackboard` should print the output to the terminal similar to what is shown below: + +```bash +# In terminal running async_param_client +[INFO] [1674574042.977327137] [async_param_client]: Setting parameters: +[INFO] [1674574042.978217825] [async_param_client]: int_parameter: +[INFO] [1674574042.978474500] [async_param_client]: successful: True +[INFO] [1674574042.978718655] [async_param_client]: bool_parameter: +[INFO] [1674574042.978960532] [async_param_client]: successful: True +[INFO] [1674574042.979200840] [async_param_client]: string_parameter: +[INFO] [1674574042.979446692] [async_param_client]: successful: True +[INFO] [1674574042.979687030] [async_param_client]: Listing Parameters: +[INFO] [1674574042.980229123] [async_param_client]: - bool_parameter +[INFO] [1674574042.980476617] [async_param_client]: - int_parameter +[INFO] [1674574042.980717248] [async_param_client]: - string_parameter +[INFO] [1674574042.980956813] [async_param_client]: Getting parameters: +[INFO] [1674574042.981544032] [async_param_client]: - int_parameter: 10 +[INFO] [1674574042.981794832] [async_param_client]: - bool_parameter: False +[INFO] [1674574042.982038493] [async_param_client]: - string_parameter: Fee Fi Fo Fum +[INFO] [1674574042.982281892] [async_param_client]: Loading parameters: +[INFO] [1674574042.987160462] [async_param_client]: other_int_parameter: +[INFO] [1674574042.987431457] [async_param_client]: successful: True +[INFO] [1674574042.987679192] [async_param_client]: value: 0 +[INFO] [1674574042.987920948] [async_param_client]: int_parameter: +[INFO] [1674574042.988162645] [async_param_client]: successful: True +[INFO] [1674574042.988402691] [async_param_client]: value: 12 +[INFO] [1674574042.988643394] [async_param_client]: string_parameter: +[INFO] [1674574042.988884034] [async_param_client]: successful: True +[INFO] [1674574042.989125729] [async_param_client]: value: I smell the blood of an Englishman +[INFO] [1674574042.989365771] [async_param_client]: other_string_parameter: +[INFO] [1674574042.989605537] [async_param_client]: successful: True +[INFO] [1674574042.989847066] [async_param_client]: value: One fish, two fish, Red fish, blue fish +[INFO] [1674574042.990086215] [async_param_client]: bool_parameter: +[INFO] [1674574042.990325361] [async_param_client]: successful: True +[INFO] [1674574042.990565005] [async_param_client]: value: False +[INFO] [1674574042.990804701] [async_param_client]: Deleting parameters: +[INFO] [1674574042.991497679] [async_param_client]: other_int_parameter: +[INFO] [1674574042.991746545] [async_param_client]: successful: True +[INFO] [1674574042.991986980] [async_param_client]: reason: +[INFO] [1674574042.992226951] [async_param_client]: other_string_parameter: +[INFO] [1674574042.992465274] [async_param_client]: successful: True +[INFO] [1674574042.992703259] [async_param_client]: reason: +[INFO] [1674574042.992941566] [async_param_client]: string_parameter: +[INFO] [1674574042.993179508] [async_param_client]: successful: True +[INFO] [1674574042.993416599] [async_param_client]: reason: +``` + +### Serialized Subscriber + +Running `ros2 run demo_nodes_py talker` should print output to the terminal similar to the following: + +```bash +# In terminal running listener_serialized +[INFO] [1674574499.556053634] [serialized_subscriber]: I heard: "b'\x00\x01\x00\x00\x0f\x00\x00\x00Hello World: 0\x00\x00'" +[INFO] [1674574500.506343148] [serialized_subscriber]: I heard: "b'\x00\x01\x00\x00\x0f\x00\x00\x00Hello World: 1\x00\x00'" +[INFO] [1674574501.508214693] [serialized_subscriber]: I heard: "b'\x00\x01\x00\x00\x0f\x00\x00\x00Hello World: 2\x00\x00'" +``` + +## **References** + +1. [About Quality of Service Settings](https://docs.ros.org/en/rolling/Concepts/About-Quality-of-Service-Settings.html) diff --git a/ubuntu/ros2/iron/heartbeat/py/archv/img/qos_listener_talker.png b/ubuntu/ros2/iron/heartbeat/py/archv/img/qos_listener_talker.png new file mode 100755 index 0000000..ba7db98 Binary files /dev/null and b/ubuntu/ros2/iron/heartbeat/py/archv/img/qos_listener_talker.png differ diff --git a/ubuntu/ros2/iron/heartbeat/py/archv/img/serialized_subscriber.png b/ubuntu/ros2/iron/heartbeat/py/archv/img/serialized_subscriber.png new file mode 100755 index 0000000..7ec0692 Binary files /dev/null and b/ubuntu/ros2/iron/heartbeat/py/archv/img/serialized_subscriber.png differ diff --git a/ubuntu/ros2/iron/heartbeat/py/archv/img/server_client.png b/ubuntu/ros2/iron/heartbeat/py/archv/img/server_client.png new file mode 100755 index 0000000..89ebe43 Binary files /dev/null and b/ubuntu/ros2/iron/heartbeat/py/archv/img/server_client.png differ diff --git a/ubuntu/ros2/iron/heartbeat/py/archv/img/set_parameters_callback.png b/ubuntu/ros2/iron/heartbeat/py/archv/img/set_parameters_callback.png new file mode 100755 index 0000000..d3388f4 Binary files /dev/null and b/ubuntu/ros2/iron/heartbeat/py/archv/img/set_parameters_callback.png differ diff --git a/ubuntu/ros2/iron/heartbeat/py/archv/img/talker_listener.png b/ubuntu/ros2/iron/heartbeat/py/archv/img/talker_listener.png new file mode 100755 index 0000000..2360de4 Binary files /dev/null and b/ubuntu/ros2/iron/heartbeat/py/archv/img/talker_listener.png differ diff --git a/ubuntu/ros2/iron/heartbeat/py/archv/package.xml b/ubuntu/ros2/iron/heartbeat/py/archv/package.xml new file mode 100755 index 0000000..5577b8c --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/archv/package.xml @@ -0,0 +1,34 @@ + + + + demo_nodes_py + 0.34.0 + + Python nodes which were previously in the ros2/examples repository but are now just used for demo purposes. + + + Aditya Pande + Audrow Nash + + Apache License 2.0 + + Esteve Fernandez + Mabel Zhang + Michael Carroll + Mikael Arguedas + + ament_index_python + example_interfaces + rclpy + rcl_interfaces + std_msgs + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + + ament_python + + diff --git a/ubuntu/ros2/iron/heartbeat/py/archv/resource/demo_nodes_py b/ubuntu/ros2/iron/heartbeat/py/archv/resource/demo_nodes_py new file mode 100755 index 0000000..e69de29 diff --git a/ubuntu/ros2/iron/heartbeat/py/archv/setup.cfg b/ubuntu/ros2/iron/heartbeat/py/archv/setup.cfg new file mode 100755 index 0000000..33295bb --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/archv/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/demo_nodes_py +[install] +install_scripts=$base/lib/demo_nodes_py diff --git a/ubuntu/ros2/iron/heartbeat/py/archv/setup.py b/ubuntu/ros2/iron/heartbeat/py/archv/setup.py new file mode 100755 index 0000000..48636e1 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/archv/setup.py @@ -0,0 +1,50 @@ +from setuptools import find_packages +from setuptools import setup + +package_name = 'demo_nodes_py' + +setup( + name=package_name, + version='0.34.0', + packages=find_packages(exclude=['test']), + data_files=[ + ('share/ament_index/resource_index/packages', ['resource/' + package_name]), + ('share/' + package_name, ['package.xml', 'demo_nodes_py/parameters/params.yaml']), + ], + install_requires=['setuptools'], + zip_safe=True, + author='Esteve Fernandez', + author_email='esteve@osrfoundation.org', + maintainer='Aditya Pande, Audrow Nash, Michael Jeronimo', + maintainer_email='aditya.pande@openrobotics.org, audrow@openrobotics.org, michael.jeronimo@openrobotics.org', # noqa: E501 + keywords=['ROS'], + classifiers=[ + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python', + 'Topic :: Software Development', + ], + description=( + 'Python nodes which were previously in the ros2/examples repository ' + 'but are now just used for demo purposes.' + ), + license='Apache License, Version 2.0', + tests_require=['pytest'], + entry_points={ + 'console_scripts': [ + 'listener = demo_nodes_py.topics.listener:main', + 'talker = demo_nodes_py.topics.talker:main', + 'listener_qos = demo_nodes_py.topics.listener_qos:main', + 'talker_qos = demo_nodes_py.topics.talker_qos:main', + 'listener_serialized = demo_nodes_py.topics.listener_serialized:main', + 'add_two_ints_client = demo_nodes_py.services.add_two_ints_client:main', + 'add_two_ints_client_async = demo_nodes_py.services.add_two_ints_client_async:main', + 'add_two_ints_server = demo_nodes_py.services.add_two_ints_server:main', + 'async_param_client = demo_nodes_py.parameters.async_param_client:main', + 'set_parameters_callback = demo_nodes_py.parameters.set_parameters_callback:main', + 'introspection = demo_nodes_py.services.introspection:main', + 'matched_event_detect = demo_nodes_py.events.matched_event_detect:main', + 'use_logger_service = demo_nodes_py.logging.use_logger_service:main', + ], + }, +) diff --git a/ubuntu/ros2/iron/heartbeat/py/archv/test/test_copyright.py b/ubuntu/ros2/iron/heartbeat/py/archv/test/test_copyright.py new file mode 100755 index 0000000..cc8ff03 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/archv/test/test_copyright.py @@ -0,0 +1,23 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_copyright.main import main +import pytest + + +@pytest.mark.copyright +@pytest.mark.linter +def test_copyright(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found errors' diff --git a/ubuntu/ros2/iron/heartbeat/py/archv/test/test_flake8.py b/ubuntu/ros2/iron/heartbeat/py/archv/test/test_flake8.py new file mode 100755 index 0000000..27ee107 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/archv/test/test_flake8.py @@ -0,0 +1,25 @@ +# Copyright 2017 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_flake8.main import main_with_errors +import pytest + + +@pytest.mark.flake8 +@pytest.mark.linter +def test_flake8(): + rc, errors = main_with_errors(argv=[]) + assert rc == 0, \ + 'Found %d code style errors / warnings:\n' % len(errors) + \ + '\n'.join(errors) diff --git a/ubuntu/ros2/iron/heartbeat/py/archv/test/test_pep257.py b/ubuntu/ros2/iron/heartbeat/py/archv/test/test_pep257.py new file mode 100755 index 0000000..b234a38 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/archv/test/test_pep257.py @@ -0,0 +1,23 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_pep257.main import main +import pytest + + +@pytest.mark.linter +@pytest.mark.pep257 +def test_pep257(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found code style errors / warnings' diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/__init__.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/events/__init__.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/events/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/events/matched_event_detect.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/events/matched_event_detect.py new file mode 100755 index 0000000..3917197 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/events/matched_event_detect.py @@ -0,0 +1,205 @@ +# Copyright 2023 Sony Group 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. + +import rclpy +from rclpy.event_handler import PublisherEventCallbacks +from rclpy.event_handler import QoSPublisherMatchedInfo +from rclpy.event_handler import QoSSubscriptionMatchedInfo +from rclpy.event_handler import SubscriptionEventCallbacks +from rclpy.executors import SingleThreadedExecutor +from rclpy.node import Node +from rclpy.publisher import Publisher +from rclpy.subscription import Subscription +from rclpy.task import Future +from std_msgs.msg import String + +""" +This demo program shows detected matched event. +Matched event occurs when publisher and subscription establishes the connection. + +Class MatchedEventDetectNode output connection information of publisher and subscription. +Class MultiSubNode is used to create/destroy subscription to connect/disconnect the publisher of +MatchedEventDetectNode. +Class MultiPubNode is used to created/destroy publisher to connect/disconnect the subscription +of MatchedEventDetectNode. +""" + + +class MatchedEventDetectNode(Node): + + def __init__(self, pub_topic_name: String, sub_topic_name: String): + super().__init__('matched_event_detection_node') + self.__any_subscription_connected = False # used for publisher event + self.__any_publisher_connected = False # used for subscription event + + pub_event_callback = PublisherEventCallbacks(matched=self.__pub_matched_event_callback) + self.pub = self.create_publisher(String, pub_topic_name, 10, + event_callbacks=pub_event_callback) + + sub_event_callback = SubscriptionEventCallbacks(matched=self.__sub_matched_event_callback) + self.sub = self.create_subscription(String, sub_topic_name, lambda msg: ..., + 10, event_callbacks=sub_event_callback) + + def __pub_matched_event_callback(self, info: QoSPublisherMatchedInfo): + if self.__any_subscription_connected: + if info.current_count == 0: + self.get_logger().info('Last subscription is disconnected.') + self.__any_subscription_connected = False + else: + self.get_logger().info('The changed number of connected subscription is ' + + str(info.current_count_change) + ' and ' + 'current number of connected subscription is ' + + str(info.current_count)) + else: + if info.current_count != 0: + self.get_logger().info('First subscription is connected.') + self.__any_subscription_connected = True + + self.future.set_result(True) + + def __sub_matched_event_callback(self, info: QoSSubscriptionMatchedInfo): + if self.__any_publisher_connected: + if info.current_count == 0: + self.get_logger().info('Last publisher is disconnected.') + self.__any_publisher_connected = False + else: + self.get_logger().info('The changed number of connected publisher is ' + + str(info.current_count_change) + ' and current ' + 'number of connected publisher is ' + + str(info.current_count)) + else: + if info.current_count != 0: + self.get_logger().info('First publisher is connected.') + self.__any_publisher_connected = True + + self.future.set_result(True) + + def get_future(self): + self.future = Future() + return self.future + + +class MultiSubNode(Node): + + def __init__(self, topic_name: String): + super().__init__('multi_sub_node') + self.__subs = [] + self.__topic_name = topic_name + + def create_one_sub(self) -> Subscription: + self.get_logger().info('Create a new subscription.') + sub = self.create_subscription(String, self.__topic_name, lambda msg: ..., 10) + self.__subs.append(sub) + return sub + + def destroy_one_sub(self, sub: Subscription): + + if sub in self.__subs: + self.get_logger().info('Destroy a subscription.') + self.__subs.remove(sub) + self.destroy_subscription(sub) + + +class MultiPubNode(Node): + + def __init__(self, topic_name: String): + super().__init__('multi_pub_node') + self.__pubs = [] + self.__topic_name = topic_name + + def create_one_pub(self) -> Publisher: + self.get_logger().info('Create a new publisher.') + pub = self.create_publisher(String, self.__topic_name, 10) + self.__pubs.append(pub) + return pub + + def destroy_one_pub(self, pub: Publisher): + + if pub in self.__pubs: + self.get_logger().info('Destroy a publisher.') + self.__pubs.remove(pub) + self.destroy_publisher(pub) + + +def main(args=None): + rclpy.init(args=args) + + topic_name_for_detect_pub_matched_event = 'pub_topic_matched_event_detect' + topic_name_for_detect_sub_matched_event = 'sub_topic_matched_event_detect' + + matched_event_detect_node = MatchedEventDetectNode( + topic_name_for_detect_pub_matched_event, topic_name_for_detect_sub_matched_event) + multi_subs_node = MultiSubNode(topic_name_for_detect_pub_matched_event) + multi_pubs_node = MultiPubNode(topic_name_for_detect_sub_matched_event) + + maximum_wait_time = 10 # 10s + + executor = SingleThreadedExecutor() + + executor.add_node(matched_event_detect_node) + executor.add_node(multi_subs_node) + executor.add_node(multi_pubs_node) + + # MatchedEventDetectNode will output: + # First subscription is connected. + sub1 = multi_subs_node.create_one_sub() + executor.spin_until_future_complete(matched_event_detect_node.get_future(), maximum_wait_time) + + # MatchedEventDetectNode will output: + # The changed number of connected subscription is 1 and current number of connected + # subscription is 2. + sub2 = multi_subs_node.create_one_sub() + executor.spin_until_future_complete(matched_event_detect_node.get_future(), maximum_wait_time) + + # MatchedEventDetectNode will output: + # The changed number of connected subscription is -1 and current number of connected + # subscription is 1. + multi_subs_node.destroy_one_sub(sub1) + executor.spin_until_future_complete(matched_event_detect_node.get_future(), maximum_wait_time) + + # MatchedEventDetectNode will output: + # Last subscription is disconnected. + multi_subs_node.destroy_one_sub(sub2) + executor.spin_until_future_complete(matched_event_detect_node.get_future(), maximum_wait_time) + + # MatchedEventDetectNode will output: + # First publisher is connected. + pub1 = multi_pubs_node.create_one_pub() + executor.spin_until_future_complete(matched_event_detect_node.get_future(), maximum_wait_time) + + # MatchedEventDetectNode will output: + # The changed number of connected publisher is 1 and current number of connected publisher + # is 2. + pub2 = multi_pubs_node.create_one_pub() + executor.spin_until_future_complete(matched_event_detect_node.get_future(), maximum_wait_time) + + # MatchedEventDetectNode will output: + # The changed number of connected publisher is -1 and current number of connected publisher + # is 1. + multi_pubs_node.destroy_one_pub(pub1) + executor.spin_until_future_complete(matched_event_detect_node.get_future(), maximum_wait_time) + + # MatchedEventDetectNode will output: + # Last publisher is disconnected. + multi_pubs_node.destroy_one_pub(pub2) + executor.spin_until_future_complete(matched_event_detect_node.get_future(), maximum_wait_time) + + multi_pubs_node.destroy_node() + multi_subs_node.destroy_node() + matched_event_detect_node.destroy_node() + rclpy.try_shutdown() + + +if __name__ == '__main__': + main() diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/parameters/__init__.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/parameters/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/parameters/async_param_client.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/parameters/async_param_client.py new file mode 100755 index 0000000..1893d29 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/parameters/async_param_client.py @@ -0,0 +1,103 @@ +# 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. +import os + +from ament_index_python import get_package_share_directory +import rclpy +import rclpy.context +from rclpy.parameter import Parameter +from rclpy.parameter import parameter_dict_from_yaml_file +from rclpy.parameter import parameter_value_to_python +from rclpy.parameter_client import AsyncParameterClient + + +def main(args=None): + rclpy.init() + node = rclpy.create_node('async_param_client') + client = AsyncParameterClient(node, 'parameter_blackboard') + if not client.wait_for_services(3.0): + raise RuntimeError( + 'parameter_blackboard is not available, start parameter blackboard with ' + '`ros2 run demo_nodes_cpp parameter_blackboard` before running this demo') + param_list = ['int_parameter', 'bool_parameter', 'string_parameter'] + + node.get_logger().info('Setting parameters:') + future = client.set_parameters([ + Parameter('int_parameter', Parameter.Type.INTEGER, 10), + Parameter('bool_parameter', Parameter.Type.BOOL, False), + Parameter('string_parameter', Parameter.Type.STRING, 'Fee Fi Fo Fum'), ]) + rclpy.spin_until_future_complete(node, future) + set_parameters_result = future.result() + if set_parameters_result is not None: + for i, v in enumerate(set_parameters_result.results): + node.get_logger().info(f' {param_list[i]}:') + node.get_logger().info(f' successful: {v.successful}') + else: + node.get_logger().error(f'Error setting parameters: {future.exception()}') + + node.get_logger().info('Listing Parameters:') + future = client.list_parameters(param_list, 10) + rclpy.spin_until_future_complete(node, future) + list_parameters_result = future.result() + if list_parameters_result is not None: + for param_name in list_parameters_result.result.names: + node.get_logger().info(f' - {param_name}') + else: + node.get_logger().error(f'Error listing parameters: {future.exception()}') + + node.get_logger().info('Getting parameters:') + future = client.get_parameters(param_list) + rclpy.spin_until_future_complete(node, future) + get_parameters_result = future.result() + if get_parameters_result is not None: + for i, v in enumerate(get_parameters_result.values): + node.get_logger().info(f' - {param_list[i]}: {parameter_value_to_python(v)}') + else: + node.get_logger().error(f'Error getting parameters: {future.exception()}') + + node.get_logger().info('Loading parameters: ') + param_dir = get_package_share_directory('demo_nodes_py') + param_file_path = os.path.join(param_dir, 'params.yaml') + future = client.load_parameter_file(param_file_path) + rclpy.spin_until_future_complete(node, future) + load_parameter_results = future.result() + if load_parameter_results is not None: + param_file_dict = parameter_dict_from_yaml_file(param_file_path) + for i, v in enumerate(param_file_dict.keys()): + node.get_logger().info(f' {v}:') + node.get_logger().info(f' successful: ' + f'{load_parameter_results.results[i].successful}') + node.get_logger().info(f' value: ' + f'{parameter_value_to_python(param_file_dict[v].value)}') + else: + node.get_logger().error(f'Error loading parameters: {future.exception()}') + + node.get_logger().info('Deleting parameters: ') + params_to_delete = ['other_int_parameter', 'other_string_parameter', 'string_parameter'] + future = client.delete_parameters(params_to_delete) + rclpy.spin_until_future_complete(node, future) + delete_parameters_result = future.result() + if delete_parameters_result is not None: + for i, v in enumerate(delete_parameters_result.results): + node.get_logger().info(f' {params_to_delete[i]}:') + node.get_logger().info(f' successful: {v.successful}') + node.get_logger().info(f' reason: {v.reason}') + else: + node.get_logger().error(f'Error deleting parameters: {future.exception()}') + + rclpy.shutdown() + + +if __name__ == '__main__': + main() diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/parameters/params.yaml b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/parameters/params.yaml new file mode 100755 index 0000000..3ea036f --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/parameters/params.yaml @@ -0,0 +1,8 @@ +/param_test_target: + ros__parameters: + other_int_parameter: 0 + int_parameter: 12 + string_parameter: I smell the blood of an Englishman + other_string_parameter: One fish, two fish, Red fish, blue fish + bool_parameter: False + diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/parameters/set_parameters_callback.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/parameters/set_parameters_callback.py new file mode 100755 index 0000000..2e9c815 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/parameters/set_parameters_callback.py @@ -0,0 +1,90 @@ +# 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. + +from rcl_interfaces.msg import SetParametersResult + +import rclpy +from rclpy.executors import ExternalShutdownException +from rclpy.node import Node +from rclpy.parameter import Parameter + + +# Example usage: changing param1 successfully will result in setting of param2. +# ros2 service call /set_parameters_callback/set_parameters rcl_interfaces/srv/SetParameters +# "{parameters: [{name: "param1", value: {type: 3, double_value: 1.0}}]}" + +# node for demonstrating correct usage of pre_set, on_set +# and post_set parameter callbacks +class SetParametersCallback(Node): + + def __init__(self): + super().__init__('set_parameters_callback') + + # tracks 'param1' value + self.internal_tracked_param_1 = self.declare_parameter('param1', 0.0).value + # tracks 'param2' value + self.internal_tracked_param_2 = self.declare_parameter('param2', 0.0).value + + # setting another parameter from the callback is possible + # we expect the callback to be called for param2 + def pre_set_parameter_callback(parameter_list): + modified_parameters = parameter_list.copy() + for param in parameter_list: + if param.name == 'param1': + modified_parameters.append(Parameter('param2', Parameter.Type.DOUBLE, 4.0)) + + return modified_parameters + + # validation callback + def on_set_parameter_callback(parameter_list): + result = SetParametersResult() + for param in parameter_list: + if param.name == 'param1': + result.successful = True + result.reason = 'success param1' + elif param.name == 'param2': + result.successful = True + result.reason = 'success param2' + + return result + + # can change internally tracked class attributes + def post_set_parameter_callback(parameter_list): + for param in parameter_list: + if param.name == 'param1': + self.internal_tracked_param_1 = param.value + elif param.name == 'param2': + self.internal_tracked_param_2 = param.value + + self.add_pre_set_parameters_callback(pre_set_parameter_callback) + self.add_on_set_parameters_callback(on_set_parameter_callback) + self.add_post_set_parameters_callback(post_set_parameter_callback) + + +def main(args=None): + rclpy.init(args=args) + + node = SetParametersCallback() + + try: + rclpy.spin(node) + except (KeyboardInterrupt, ExternalShutdownException): + pass + finally: + node.destroy_node() + rclpy.try_shutdown() + + +if __name__ == '__main__': + main() diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/services/__init__.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/services/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/services/add_two_ints_client.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/services/add_two_ints_client.py new file mode 100755 index 0000000..5c6eab9 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/services/add_two_ints_client.py @@ -0,0 +1,43 @@ +# Copyright 2016 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from example_interfaces.srv import AddTwoInts + +import rclpy + + +def main(args=None): + rclpy.init(args=args) + + node = rclpy.create_node('add_two_ints_client') + + cli = node.create_client(AddTwoInts, 'add_two_ints') + while not cli.wait_for_service(timeout_sec=1.0): + print('service not available, waiting again...') + req = AddTwoInts.Request() + req.a = 2 + req.b = 3 + future = cli.call_async(req) + rclpy.spin_until_future_complete(node, future) + if future.result() is not None: + node.get_logger().info('Result of add_two_ints: %d' % future.result().sum) + else: + node.get_logger().error('Exception while calling service: %r' % future.exception()) + + node.destroy_node() + rclpy.try_shutdown() + + +if __name__ == '__main__': + main() diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/services/add_two_ints_client_async.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/services/add_two_ints_client_async.py new file mode 100755 index 0000000..20296f8 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/services/add_two_ints_client_async.py @@ -0,0 +1,48 @@ +# Copyright 2016 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from example_interfaces.srv import AddTwoInts + +import rclpy + + +def main(args=None): + + rclpy.init(args=args) + + node = rclpy.create_node('add_two_ints_client') + + cli = node.create_client(AddTwoInts, 'add_two_ints') + + req = AddTwoInts.Request() + req.a = 2 + req.b = 3 + while not cli.wait_for_service(timeout_sec=1.0): + print('service not available, waiting again...') + future = cli.call_async(req) + while rclpy.ok(): + rclpy.spin_once(node) + if future.done(): + if future.result() is not None: + node.get_logger().info('Result of add_two_ints: %d' % future.result().sum) + else: + node.get_logger().error('Exception while calling service: %r' % future.exception()) + break + + node.destroy_node() + rclpy.try_shutdown() + + +if __name__ == '__main__': + main() diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/services/add_two_ints_server.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/services/add_two_ints_server.py new file mode 100755 index 0000000..806e41b --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/services/add_two_ints_server.py @@ -0,0 +1,52 @@ +# Copyright 2016 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from example_interfaces.srv import AddTwoInts + +import rclpy +from rclpy.executors import ExternalShutdownException +from rclpy.node import Node + + +class AddTwoIntsServer(Node): + + def __init__(self): + super().__init__('add_two_ints_server') + self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback) + + def add_two_ints_callback(self, request, response): + response.sum = request.a + request.b + self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b)) + + return response + + +def main(args=None): + rclpy.init(args=args) + + node = AddTwoIntsServer() + + try: + rclpy.spin(node) + except (KeyboardInterrupt, ExternalShutdownException): + pass + finally: + # Destroy the node explicitly + # (optional - Done automatically when node is garbage collected) + node.destroy_node() + rclpy.try_shutdown() + + +if __name__ == '__main__': + main() diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/services/introspection.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/services/introspection.py new file mode 100755 index 0000000..15bfe3e --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/services/introspection.py @@ -0,0 +1,211 @@ +# Copyright 2023 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. + +from example_interfaces.srv import AddTwoInts +from rcl_interfaces.msg import SetParametersResult + +import rclpy +from rclpy.executors import ExternalShutdownException +from rclpy.executors import SingleThreadedExecutor +from rclpy.node import Node +from rclpy.parameter import Parameter +from rclpy.qos import qos_profile_system_default +from rclpy.service_introspection import ServiceIntrospectionState + + +# This demo program shows how to configure client and service introspection +# on the fly, by hooking it up to a parameter. This program consists of both +# a client node (IntrospectionClientNode) and a service node (IntrospectionServiceNode). +# At startup time, one of each type of node is created, and added to an executor. +# The IntrospectionServiceNode listens on the '/add_two_ints' service for clients; +# when one connects and sends a request, it adds the two integers and returns the result. +# The IntrospectionClientNode is slightly more complicated. It has a timer callback that +# runs every 500 milliseconds. If the service is not yet ready, no further work +# is done. If the client doesn't currently have a future outstanding, then it +# creates a new AddTwoInts service request, and asynchronously sends it to the service. +# If the client does have a future outstanding, then we check if it is done. +# Once the future has been completed, we forget about the future so we can send +# another request. +# +# The above is a fairly common ROS 2 client and service, but what this program +# is trying to demonstrate is introspection capabilities. +# The IntrospectionClientNode has a string parameter called 'client_configure_introspection', +# and the IntrospectionServiceNode has a string parameter called 'service_configure_introspection'. +# If these are set to 'disabled' (the default), then no introspection happens. +# If these are set to 'metadata' (see details on how to set the parameters below), +# then essential metadata (timestamps, sequence numbers, etc) are sent to a +# hidden topic called /add_two_ints/_service_event. +# +# To see this in action, this program can be run in a couple of different ways: +# +# ros2 run demo_nodes_py introspection +# Since the default for introspection is 'disabled', this is no different than +# a normal client and server. No additional topics will be made, and +# no introspection data will be sent. However, changing the introspection +# configuration dynamically is fully supported. This can be seen by +# running 'ros2 param set /introspection_client client_configure_introspection metadata' +# which will configure the client to start sending service introspection +# metadata to /add_two_ints/_service_event. +# +# ros2 run demo_nodes_py introspection \ +# --ros-args -p client_configure_introspection:=metadata \ +# -p service_configure_introspection:=metadata +# Here we've set both the client and service introspection to metadata, +# so the /add_two_ints/_service_event will be created. Additionally, +# every client request and response and every service acceptance and +# response will be sent to that topic. +# +# In either case, service introspection data can be seen by running: +# ros2 topic echo /add_two_ints/_service_event + +def check_parameter(parameter_list, parameter_name): + result = SetParametersResult() + result.successful = True + for param in parameter_list: + if param.name != parameter_name: + continue + + if param.type_ != Parameter.Type.STRING: + result.successful = False + result.reason = 'must be a string' + break + + if param.value not in ('disabled', 'metadata', 'contents'): + result.successful = False + result.reason = "must be one of 'disabled', 'metadata', or 'contents" + break + + return result + + +class IntrospectionClientNode(Node): + + def on_set_parameters_callback(self, parameter_list): + return check_parameter(parameter_list, 'client_configure_introspection') + + def on_post_set_parameters_callback(self, parameter_list): + for param in parameter_list: + if param.name != 'client_configure_introspection': + continue + + introspection_state = ServiceIntrospectionState.OFF + if param.value == 'disabled': + introspection_state = ServiceIntrospectionState.OFF + elif param.value == 'metadata': + introspection_state = ServiceIntrospectionState.METADATA + elif param.value == 'contents': + introspection_state = ServiceIntrospectionState.CONTENTS + + self.cli.configure_introspection(self.get_clock(), qos_profile_system_default, + introspection_state) + break + + def __init__(self): + super().__init__('introspection_client') + + self.cli = self.create_client(AddTwoInts, 'add_two_ints') + + self.add_on_set_parameters_callback(self.on_set_parameters_callback) + self.add_post_set_parameters_callback(self.on_post_set_parameters_callback) + self.declare_parameter('client_configure_introspection', 'disabled') + + self.timer = self.create_timer(0.5, self.timer_callback) + self.future = None + + def timer_callback(self): + if not self.cli.service_is_ready(): + return + + if self.future is None: + req = AddTwoInts.Request() + req.a = 2 + req.b = 3 + + self.future = self.cli.call_async(req) + + return + + if not self.future.done(): + return + + if self.future.result() is not None: + self.get_logger().info('Result of add_two_ints: %d' % self.future.result().sum) + else: + self.get_logger().error('Exception calling service: %r' % self.future.exception()) + + self.future = None + + +class IntrospectionServiceNode(Node): + + def on_set_parameters_callback(self, parameter_list): + return check_parameter(parameter_list, 'service_configure_introspection') + + def on_post_set_parameters_callback(self, parameter_list): + for param in parameter_list: + if param.name != 'service_configure_introspection': + continue + + introspection_state = ServiceIntrospectionState.OFF + if param.value == 'disabled': + introspection_state = ServiceIntrospectionState.OFF + elif param.value == 'metadata': + introspection_state = ServiceIntrospectionState.METADATA + elif param.value == 'contents': + introspection_state = ServiceIntrospectionState.CONTENTS + + self.srv.configure_introspection(self.get_clock(), qos_profile_system_default, + introspection_state) + break + + def __init__(self): + super().__init__('introspection_service') + + self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback) + + self.add_on_set_parameters_callback(self.on_set_parameters_callback) + self.add_post_set_parameters_callback(self.on_post_set_parameters_callback) + self.declare_parameter('service_configure_introspection', 'disabled') + + def add_two_ints_callback(self, request, response): + response.sum = request.a + request.b + self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b)) + + return response + + +def main(args=None): + rclpy.init(args=args) + + service_node = IntrospectionServiceNode() + + client_node = IntrospectionClientNode() + + executor = SingleThreadedExecutor() + executor.add_node(service_node) + executor.add_node(client_node) + + try: + executor.spin() + except (KeyboardInterrupt, ExternalShutdownException): + executor.remove_node(client_node) + executor.remove_node(service_node) + executor.shutdown() + service_node.destroy_node() + client_node.destroy_node() + rclpy.try_shutdown() + + +if __name__ == '__main__': + main() diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/__init__.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/listener.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/listener.py new file mode 100755 index 0000000..e52354a --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/listener.py @@ -0,0 +1,46 @@ +# Copyright 2016 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import rclpy +from rclpy.executors import ExternalShutdownException +from rclpy.node import Node + +from std_msgs.msg import String + + +class Listener(Node): + + def __init__(self): + super().__init__('listener') + self.sub = self.create_subscription(String, 'chatter', self.chatter_callback, 10) + + def chatter_callback(self, msg): + self.get_logger().info('I heard: [%s]' % msg.data) + + +def main(args=None): + rclpy.init(args=args) + + node = Listener() + try: + rclpy.spin(node) + except (KeyboardInterrupt, ExternalShutdownException): + pass + finally: + node.destroy_node() + rclpy.try_shutdown() + + +if __name__ == '__main__': + main() diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/listener_qos.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/listener_qos.py new file mode 100755 index 0000000..5c9ce74 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/listener_qos.py @@ -0,0 +1,79 @@ +# Copyright 2016 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import sys + +import rclpy +from rclpy.executors import ExternalShutdownException +from rclpy.node import Node +from rclpy.qos import qos_profile_sensor_data +from rclpy.qos import QoSProfile +from rclpy.qos import QoSReliabilityPolicy +from rclpy.utilities import remove_ros_args + +from std_msgs.msg import String + + +class ListenerQos(Node): + + def __init__(self, qos_profile): + super().__init__('listener_qos') + if qos_profile.reliability is QoSReliabilityPolicy.RELIABLE: + self.get_logger().info('Reliable listener') + else: + self.get_logger().info('Best effort listener') + self.sub = self.create_subscription( + String, 'chatter', self.chatter_callback, qos_profile) + + def chatter_callback(self, msg): + self.get_logger().info('I heard: [%s]' % msg.data) + + +def main(argv=sys.argv[1:]): + parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument( + '--reliable', dest='reliable', action='store_true', + help='set qos profile to reliable') + parser.set_defaults(reliable=False) + parser.add_argument( + '-n', '--number_of_cycles', type=int, default=20, + help='max number of spin_once iterations') + args = parser.parse_args(remove_ros_args(args=argv)) + + rclpy.init(args=argv) + + if args.reliable: + custom_qos_profile = QoSProfile( + depth=10, + reliability=QoSReliabilityPolicy.RELIABLE) + else: + custom_qos_profile = qos_profile_sensor_data + + node = ListenerQos(custom_qos_profile) + + cycle_count = 0 + try: + while rclpy.ok() and cycle_count < args.number_of_cycles: + rclpy.spin_once(node) + cycle_count += 1 + except (KeyboardInterrupt, ExternalShutdownException): + pass + finally: + node.destroy_node() + rclpy.try_shutdown() + + +if __name__ == '__main__': + main() diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/listener_serialized.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/listener_serialized.py new file mode 100755 index 0000000..d768cf2 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/listener_serialized.py @@ -0,0 +1,55 @@ +# Copyright 2018 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import rclpy +from rclpy.executors import ExternalShutdownException +from rclpy.node import Node + +from std_msgs.msg import String + + +class SerializedSubscriber(Node): + + def __init__(self): + super().__init__('serialized_subscriber') + self.subscription = self.create_subscription( + String, + 'chatter', + self.listener_callback, + 10, + raw=True) # We're subscribing to the serialized bytes, not std_msgs.msg.String + + def listener_callback(self, msg): + self.get_logger().info('I heard: "%s"' % msg) + + +def main(args=None): + rclpy.init(args=args) + + serialized_subscriber = SerializedSubscriber() + + try: + rclpy.spin(serialized_subscriber) + except (KeyboardInterrupt, ExternalShutdownException): + pass + finally: + # Destroy the node explicitly + # (optional - otherwise it will be done automatically + # when the garbage collector destroys the node object) + serialized_subscriber.destroy_node() + rclpy.try_shutdown() + + +if __name__ == '__main__': + main() diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/talker.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/talker.py new file mode 100755 index 0000000..f935049 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/talker.py @@ -0,0 +1,54 @@ +# Copyright 2016 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import rclpy +from rclpy.executors import ExternalShutdownException +from rclpy.node import Node + +from std_msgs.msg import String + + +class Talker(Node): + + def __init__(self): + super().__init__('talker') + self.i = 0 + self.pub = self.create_publisher(String, 'chatter', 10) + timer_period = 1.0 + self.tmr = self.create_timer(timer_period, self.timer_callback) + + def timer_callback(self): + msg = String() + msg.data = 'Heartbeat messages # {0}'.format(self.i) + self.i += 1 + self.get_logger().info('Publishing: "{0}"'.format(msg.data)) + self.pub.publish(msg) + + +def main(args=None): + rclpy.init(args=args) + + node = Talker() + + try: + rclpy.spin(node) + except (KeyboardInterrupt, ExternalShutdownException): + pass + finally: + node.destroy_node() + rclpy.try_shutdown() + + +if __name__ == '__main__': + main() diff --git a/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/talker_qos.py b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/talker_qos.py new file mode 100755 index 0000000..850c1b6 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/demo_nodes_py/topics/talker_qos.py @@ -0,0 +1,86 @@ +# Copyright 2016 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import sys + +import rclpy +from rclpy.executors import ExternalShutdownException +from rclpy.node import Node +from rclpy.qos import qos_profile_sensor_data +from rclpy.qos import QoSProfile +from rclpy.qos import QoSReliabilityPolicy +from rclpy.utilities import remove_ros_args + +from std_msgs.msg import String + + +class TalkerQos(Node): + + def __init__(self, qos_profile): + super().__init__('talker_qos') + self.i = 0 + if qos_profile.reliability is QoSReliabilityPolicy.RELIABLE: + self.get_logger().info('Reliable talker') + else: + self.get_logger().info('Best effort talker') + self.pub = self.create_publisher(String, 'chatter', qos_profile) + + timer_period = 1.0 + self.tmr = self.create_timer(timer_period, self.timer_callback) + + def timer_callback(self): + msg = String() + msg.data = 'PUBLISHER sending Heartbeat messages: {0}'.format(self.i) + self.i += 1 + self.get_logger().info('Publishing: "{0}"'.format(msg.data)) + self.pub.publish(msg) + + +def main(argv=sys.argv[1:]): + parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument( + '--reliable', dest='reliable', action='store_true', + help='set qos profile to reliable') + parser.set_defaults(reliable=False) + parser.add_argument( + '-n', '--number_of_cycles', type=int, default=20, + help='number of sending attempts') + args = parser.parse_args(remove_ros_args(args=argv)) + + rclpy.init(args=argv) + + if args.reliable: + custom_qos_profile = QoSProfile( + depth=10, + reliability=QoSReliabilityPolicy.RELIABLE) + else: + custom_qos_profile = qos_profile_sensor_data + + node = TalkerQos(custom_qos_profile) + + cycle_count = 0 + try: + while rclpy.ok() and cycle_count < args.number_of_cycles: + rclpy.spin_once(node) + cycle_count += 1 + except (KeyboardInterrupt, ExternalShutdownException): + pass + finally: + node.destroy_node() + rclpy.try_shutdown() + + +if __name__ == '__main__': + main() diff --git a/ubuntu/ros2/iron/heartbeat/py/img/qos_listener_talker.png b/ubuntu/ros2/iron/heartbeat/py/img/qos_listener_talker.png new file mode 100755 index 0000000..ba7db98 Binary files /dev/null and b/ubuntu/ros2/iron/heartbeat/py/img/qos_listener_talker.png differ diff --git a/ubuntu/ros2/iron/heartbeat/py/img/serialized_subscriber.png b/ubuntu/ros2/iron/heartbeat/py/img/serialized_subscriber.png new file mode 100755 index 0000000..7ec0692 Binary files /dev/null and b/ubuntu/ros2/iron/heartbeat/py/img/serialized_subscriber.png differ diff --git a/ubuntu/ros2/iron/heartbeat/py/img/server_client.png b/ubuntu/ros2/iron/heartbeat/py/img/server_client.png new file mode 100755 index 0000000..89ebe43 Binary files /dev/null and b/ubuntu/ros2/iron/heartbeat/py/img/server_client.png differ diff --git a/ubuntu/ros2/iron/heartbeat/py/img/set_parameters_callback.png b/ubuntu/ros2/iron/heartbeat/py/img/set_parameters_callback.png new file mode 100755 index 0000000..d3388f4 Binary files /dev/null and b/ubuntu/ros2/iron/heartbeat/py/img/set_parameters_callback.png differ diff --git a/ubuntu/ros2/iron/heartbeat/py/img/talker_listener.png b/ubuntu/ros2/iron/heartbeat/py/img/talker_listener.png new file mode 100755 index 0000000..2360de4 Binary files /dev/null and b/ubuntu/ros2/iron/heartbeat/py/img/talker_listener.png differ diff --git a/ubuntu/ros2/iron/heartbeat/py/package.xml b/ubuntu/ros2/iron/heartbeat/py/package.xml new file mode 100755 index 0000000..5577b8c --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/package.xml @@ -0,0 +1,34 @@ + + + + demo_nodes_py + 0.34.0 + + Python nodes which were previously in the ros2/examples repository but are now just used for demo purposes. + + + Aditya Pande + Audrow Nash + + Apache License 2.0 + + Esteve Fernandez + Mabel Zhang + Michael Carroll + Mikael Arguedas + + ament_index_python + example_interfaces + rclpy + rcl_interfaces + std_msgs + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + + ament_python + + diff --git a/ubuntu/ros2/iron/heartbeat/py/resource/demo_nodes_py b/ubuntu/ros2/iron/heartbeat/py/resource/demo_nodes_py new file mode 100755 index 0000000..e69de29 diff --git a/ubuntu/ros2/iron/heartbeat/py/setup.cfg b/ubuntu/ros2/iron/heartbeat/py/setup.cfg new file mode 100755 index 0000000..33295bb --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/demo_nodes_py +[install] +install_scripts=$base/lib/demo_nodes_py diff --git a/ubuntu/ros2/iron/heartbeat/py/setup.py b/ubuntu/ros2/iron/heartbeat/py/setup.py new file mode 100755 index 0000000..48636e1 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/setup.py @@ -0,0 +1,50 @@ +from setuptools import find_packages +from setuptools import setup + +package_name = 'demo_nodes_py' + +setup( + name=package_name, + version='0.34.0', + packages=find_packages(exclude=['test']), + data_files=[ + ('share/ament_index/resource_index/packages', ['resource/' + package_name]), + ('share/' + package_name, ['package.xml', 'demo_nodes_py/parameters/params.yaml']), + ], + install_requires=['setuptools'], + zip_safe=True, + author='Esteve Fernandez', + author_email='esteve@osrfoundation.org', + maintainer='Aditya Pande, Audrow Nash, Michael Jeronimo', + maintainer_email='aditya.pande@openrobotics.org, audrow@openrobotics.org, michael.jeronimo@openrobotics.org', # noqa: E501 + keywords=['ROS'], + classifiers=[ + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python', + 'Topic :: Software Development', + ], + description=( + 'Python nodes which were previously in the ros2/examples repository ' + 'but are now just used for demo purposes.' + ), + license='Apache License, Version 2.0', + tests_require=['pytest'], + entry_points={ + 'console_scripts': [ + 'listener = demo_nodes_py.topics.listener:main', + 'talker = demo_nodes_py.topics.talker:main', + 'listener_qos = demo_nodes_py.topics.listener_qos:main', + 'talker_qos = demo_nodes_py.topics.talker_qos:main', + 'listener_serialized = demo_nodes_py.topics.listener_serialized:main', + 'add_two_ints_client = demo_nodes_py.services.add_two_ints_client:main', + 'add_two_ints_client_async = demo_nodes_py.services.add_two_ints_client_async:main', + 'add_two_ints_server = demo_nodes_py.services.add_two_ints_server:main', + 'async_param_client = demo_nodes_py.parameters.async_param_client:main', + 'set_parameters_callback = demo_nodes_py.parameters.set_parameters_callback:main', + 'introspection = demo_nodes_py.services.introspection:main', + 'matched_event_detect = demo_nodes_py.events.matched_event_detect:main', + 'use_logger_service = demo_nodes_py.logging.use_logger_service:main', + ], + }, +) diff --git a/ubuntu/ros2/iron/heartbeat/py/test/test_copyright.py b/ubuntu/ros2/iron/heartbeat/py/test/test_copyright.py new file mode 100755 index 0000000..cc8ff03 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/test/test_copyright.py @@ -0,0 +1,23 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_copyright.main import main +import pytest + + +@pytest.mark.copyright +@pytest.mark.linter +def test_copyright(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found errors' diff --git a/ubuntu/ros2/iron/heartbeat/py/test/test_flake8.py b/ubuntu/ros2/iron/heartbeat/py/test/test_flake8.py new file mode 100755 index 0000000..27ee107 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/test/test_flake8.py @@ -0,0 +1,25 @@ +# Copyright 2017 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_flake8.main import main_with_errors +import pytest + + +@pytest.mark.flake8 +@pytest.mark.linter +def test_flake8(): + rc, errors = main_with_errors(argv=[]) + assert rc == 0, \ + 'Found %d code style errors / warnings:\n' % len(errors) + \ + '\n'.join(errors) diff --git a/ubuntu/ros2/iron/heartbeat/py/test/test_pep257.py b/ubuntu/ros2/iron/heartbeat/py/test/test_pep257.py new file mode 100755 index 0000000..b234a38 --- /dev/null +++ b/ubuntu/ros2/iron/heartbeat/py/test/test_pep257.py @@ -0,0 +1,23 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_pep257.main import main +import pytest + + +@pytest.mark.linter +@pytest.mark.pep257 +def test_pep257(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found code style errors / warnings'