Skip to content

Commit df9a85e

Browse files
jaisontjmm318
andauthored
Demo to show the working of the incompatible_qos event callbacks. (#416)
Signed-off-by: Jaison Titus <[email protected]> Signed-off-by: Miaofei <[email protected]> Co-authored-by: Miaofei <[email protected]>
1 parent 7b736b0 commit df9a85e

File tree

7 files changed

+343
-16
lines changed

7 files changed

+343
-16
lines changed

quality_of_service_demo/README.md

+30-16
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ No Durability QoS demo app exists yet in this package.
2222
The application requires an argument a `deadline_duration` - this is the maximum acceptable time (in positive integer milliseconds) between messages published on the topic.
2323
A `deadline_duration` of `0` means that there is no deadline, so the application will act as a standard Talker/Listener pair.
2424

25-
The Publisher in this demo will pause publishing periodically, which will print deadline expirations from the Subscriber.
25+
The publisher in this demo will pause publishing periodically, which will print deadline expirations from the subscriber.
2626

2727
Run `quality_of_service_demo/deadline -h` for more usage information.
2828

@@ -36,23 +36,23 @@ Examples:
3636

3737
## Lifespan
3838

39-
`quality_of_service_demo/lifespan` demonstrates messages being deleted from a Publisher's outgoing queue once their configured lifespan expires.
39+
`quality_of_service_demo/lifespan` demonstrates messages being deleted from a publisher's outgoing queue once their configured lifespan expires.
4040

41-
The application requires an argument `lifespan_duration` - this is the maximum time (in positive integer milliseconds) that a message will sit in the Publisher's outgoing queue.
41+
The application requires an argument `lifespan_duration` - this is the maximum time (in positive integer milliseconds) that a message will sit in the publisher's outgoing queue.
4242

43-
The Publisher in this demo will publish a preset number of messages, then stop publishing.
44-
A Subscriber will start subscribing after a preset amount of time and may receive _fewer_ backfilled messages than were originally published, because some message can outlast their lifespan and be removed.
43+
The publisher in this demo will publish a preset number of messages, then stop publishing.
44+
A subscriber will start subscribing after a preset amount of time and may receive _fewer_ backfilled messages than were originally published, because some message can outlast their lifespan and be removed.
4545

4646
Run `lifespan -h` for more usage information.
4747

4848
Examples:
4949
* `ros2 run quality_of_service_demo_cpp lifespan 1000 --publish-count 10 --subscribe-after 3000` _or_
5050
* `ros2 run quality_of_service_demo_py lifespan 1000 --publish-count 10 --subscribe-after 3000`
51-
* After a few seconds, you should see (approximately) messages 4-9 printed from the Subscriber.
52-
* The first few messages, with 1 second lifespan, were gone by the time the Subscriber joined after 3 seconds.
51+
* After a few seconds, you should see (approximately) messages 4-9 printed from the subscriber.
52+
* The first few messages, with 1 second lifespan, were gone by the time the subscriber joined after 3 seconds.
5353
* `ros2 run quality_of_service_demo_cpp lifespan 4000 --publish-count 10 --subscribe-after 3000` _or_
5454
* `ros2 run quality_of_service_demo_py lifespan 4000 --publish-count 10 --subscribe-after 3000`
55-
* After a few seconds, you should see all of the messages (0-9) printed from the Subscriber.
55+
* After a few seconds, you should see all of the messages (0-9) printed from the subscriber.
5656
* All messages, with their 4 second lifespan, survived until the subscriber joined.
5757

5858
## Liveliness
@@ -61,11 +61,11 @@ Examples:
6161

6262
The application requires an argument `lease_duration` that specifies how often (in milliseconds) an entity must "check in" to let the system know it's alive.
6363

64-
The Publisher in this demo will assert its liveliness based on passed in options, and be stopped after some amount of time.
65-
When using `AUTOMATIC` liveliness policy, the Publisher is deleted at this time, in order to stop all automatic liveliness assertions in the rmw implementation - therefore only the Subscription will receive a Liveliness event for `AUTOMATIC`.
66-
For `MANUAL` liveliness policies, the Publisher's assertions are stopped, as well as its message publishing.
64+
The publisher in this demo will assert its liveliness based on passed in options, and be stopped after some amount of time.
65+
When using `AUTOMATIC` liveliness policy, the publisher is deleted at this time, in order to stop all automatic liveliness assertions in the rmw implementation - therefore only the Subscription will receive a Liveliness event for `AUTOMATIC`.
66+
For `MANUAL` liveliness policies, the publisher's assertions are stopped, as well as its message publishing.
6767
Publishing a message implicitly counts as asserting liveliness, so publishing is stopped in order to allow the Liveliness lease to lapse.
68-
Therefore in the `MANUAL` policy types, you will see Liveliness events from both Publisher and Subscription (in rmw implementations that implement this feature).
68+
Therefore in the `MANUAL` policy types, you will see Liveliness events from both publisher and Subscription (in rmw implementations that implement this feature).
6969

7070
Run `quality_of_service_demo/liveliness -h` for more usage information.
7171

@@ -77,6 +77,21 @@ Examples:
7777
* `ros2 run quality_of_service_demo_py liveliness 250 --node-assert-period 0 --policy MANUAL_BY_NODE`
7878
* With this configuration, the node never explicitly asserts its liveliness, but it publishes messages (implicitly asserting liveliness) less often (500ms) than the liveliness lease, so you will see alternating alive/not-alive messages as the publisher misses its lease and "becomes alive again" when it publishes, until the talker is stopped.
7979

80+
## Incompatible QoS Offered/Requested
81+
82+
`quality_of_service_demo/incompatible_qos` demonstrates notification from a publisher and a subscriber upon discovering each other that they are not compatible.
83+
84+
The application requires an argument `incompatible_qos_policy_name` that specifies which QoS policy should be made incompatible between the publisher and subscriber as an example.
85+
86+
The publisher in this demo will publish 5 messages, but also output a notification stating that its offered QoS is incompatible with the subscriber that is also created by this demo.
87+
The subscriber in this demo will similarly output a notification stating that its requested QoS is incompatible with the QoS offered by the publisher.
88+
89+
Run `quality_of_service_demo/incompatible_qos -h` for more usage information.
90+
91+
Examples:
92+
* `ros2 run quality_of_service_demo_cpp incompatible_qos durability` _or_
93+
* `ros2 run quality_of_service_demo_py incompatible_qos durability`
94+
8095
## Interactive Quality of Service Demos
8196

8297
These demos allow the user to interactively generate events and see the publisher's and subscriber's responses to these user events.
@@ -112,11 +127,10 @@ While the demo is running, the following commands may be issued in the publisher
112127

113128
### Example Demo Run
114129

115-
Let's start the publisher sending a message every half second, and the publisher and subscriber with both offered and requested Deadline periods and Liveliness lease durations at 1 second
116-
(it is recommended to run these demos using the RTI Connext RMW implementation):
130+
Let's start the publisher sending a message every half second, and the publisher and subscriber with both offered and requested Deadline periods and Liveliness lease durations at 1 second:
117131
```
118-
RMW_IMPLEMENTATION=rmw_connext_cpp ros2 run quality_of_service_demo_cpp interactive_publisher --delay 0.5 --deadline 1 --liveliness MANUAL_BY_TOPIC --lease 1
119-
RMW_IMPLEMENTATION=rmw_connext_cpp ros2 run quality_of_service_demo_cpp interactive_subscriber --deadline 1 --liveliness MANUAL_BY_TOPIC --lease 1
132+
ros2 run quality_of_service_demo_cpp interactive_publisher --delay 0.5 --deadline 1 --liveliness MANUAL_BY_TOPIC --lease 1
133+
ros2 run quality_of_service_demo_cpp interactive_subscriber --deadline 1 --liveliness MANUAL_BY_TOPIC --lease 1
120134
```
121135

122136
You will see the publisher publishing messages and the subscriber receiving them.

quality_of_service_demo/rclcpp/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ qos_demo_executable(lifespan)
3838
qos_demo_executable(deadline)
3939
qos_demo_executable(interactive_publisher)
4040
qos_demo_executable(interactive_subscriber)
41+
qos_demo_executable(incompatible_qos)
4142

4243
if(BUILD_TESTING)
4344
find_package(ament_lint_auto REQUIRED)

quality_of_service_demo/rclcpp/include/quality_of_service_demo/common_nodes.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ class Talker : public rclcpp::Node
5555
*/
5656
void publish();
5757

58+
/// Get the number of messages that have been published by this publisher.
59+
size_t get_published_count() const;
60+
5861
/// Assert the liveliness of the node.
5962
bool assert_node_liveliness() const;
6063

quality_of_service_demo/rclcpp/src/common_nodes.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ Talker::publish()
8484
}
8585
}
8686

87+
size_t
88+
Talker::get_published_count() const
89+
{
90+
return publish_count_;
91+
}
92+
8793
bool
8894
Talker::assert_node_liveliness() const
8995
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <chrono>
16+
#include <memory>
17+
#include <string>
18+
19+
#include "rcutils/cmdline_parser.h"
20+
21+
#include "rclcpp/rclcpp.hpp"
22+
#include "rclcpp/executors/single_threaded_executor.hpp"
23+
24+
#include "std_msgs/msg/string.hpp"
25+
#include "std_msgs/msg/bool.hpp"
26+
27+
#include "quality_of_service_demo/common_nodes.hpp"
28+
29+
void print_usage()
30+
{
31+
std::cout << "Usage:\n";
32+
std::cout << "incompatible_qos [-h] <incompatible_qos_policy_name>\n\n";
33+
std::cout << "required arguments:\n";
34+
std::cout << "incompatible_qos_policy_name: The QoS Policy that should be incompatible between\n"
35+
" the publisher and subscription (durability, deadline,\n"
36+
" liveliness_policy, liveliness_lease_duration,\n"
37+
" or reliability).\n\n";
38+
std::cout << "optional arguments:\n";
39+
std::cout << "-h: Print this help message.\n";
40+
}
41+
42+
int main(int argc, char * argv[])
43+
{
44+
// Force flush of the stdout buffer.
45+
setvbuf(stdout, NULL, _IONBF, BUFSIZ);
46+
47+
// Argument count and usage
48+
if (argc < 2 || rcutils_cli_option_exist(argv, argv + argc, "-h")) {
49+
print_usage();
50+
return 0;
51+
}
52+
53+
// Configuration variables
54+
std::string qos_policy_name(argv[1]);
55+
rclcpp::QoS qos_profile_publisher(10);
56+
rclcpp::QoS qos_profile_subscription(10);
57+
58+
if (qos_policy_name == "durability") {
59+
std::cout << "Durability incompatibility selected.\n"
60+
"Incompatibility condition: publisher durability kind < "
61+
"subscripition durability kind.\n"
62+
"Setting publisher durability to: VOLATILE\n"
63+
"Setting subscription durability to: TRANSIENT_LOCAL\n";
64+
qos_profile_publisher.durability(RMW_QOS_POLICY_DURABILITY_VOLATILE);
65+
qos_profile_subscription.durability(RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL);
66+
} else if (qos_policy_name == "deadline") {
67+
std::cout << "Deadline incompatibility selected.\n"
68+
"Incompatibility condition: publisher deadline > subscription deadline\n"
69+
"Setting publisher deadline to: 2 seconds\n"
70+
"Setting subscription deadline to: 1 second\n";
71+
qos_profile_publisher.deadline(std::chrono::milliseconds(2000));
72+
qos_profile_subscription.deadline(std::chrono::milliseconds(1000));
73+
} else if (qos_policy_name == "liveliness_policy") {
74+
std::cout << "Liveliness Policy incompatibility selected.\n"
75+
"Incompatibility condition: publisher liveliness policy < "
76+
"subscripition liveliness policy.\n"
77+
"Setting publisher liveliness policy to: MANUAL_BY_NODE\n"
78+
"Setting subscription liveliness policy to: MANUAL_BY_TOPIC\n";
79+
qos_profile_publisher.liveliness(RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_NODE);
80+
qos_profile_subscription.liveliness(RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC);
81+
} else if (qos_policy_name == "liveliness_lease_duration") {
82+
std::cout << "Liveliness lease duration incompatibility selected.\n"
83+
"Incompatibility condition: publisher liveliness lease duration > "
84+
"subscription liveliness lease duration\n"
85+
"Setting publisher liveliness lease duration to: 2 seconds\n"
86+
"Setting subscription liveliness lease duration to: 1 seconds\n";
87+
qos_profile_publisher.liveliness_lease_duration(std::chrono::milliseconds(2000));
88+
qos_profile_subscription.liveliness_lease_duration(std::chrono::milliseconds(1000));
89+
} else if (qos_policy_name == "reliability") {
90+
std::cout << "Reliability incompatibility selected.\n"
91+
"Incompatibility condition: publisher reliability < subscripition reliability.\n"
92+
"Setting publisher reliability to: BEST_EFFORT\n"
93+
"Setting subscription reliability to: RELIABLE\n";
94+
qos_profile_publisher.reliability(RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT);
95+
qos_profile_subscription.reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE);
96+
} else {
97+
std::cout << qos_policy_name << " is not a valid qos policy name\n";
98+
print_usage();
99+
return 0;
100+
}
101+
std::cout << "\n";
102+
103+
// Initialization and configuration
104+
rclcpp::init(argc, argv);
105+
const std::string topic("qos_incompatible_qos_chatter");
106+
constexpr size_t num_msgs = 5;
107+
108+
auto talker = std::make_shared<Talker>(qos_profile_publisher, topic, num_msgs);
109+
talker->get_options().event_callbacks.incompatible_qos_callback =
110+
[node = talker.get()](rclcpp::QOSOfferedIncompatibleQoSInfo & event) -> void
111+
{
112+
RCLCPP_INFO(
113+
node->get_logger(),
114+
"Offered incompatible qos - total %d delta %d last_policy_kind: %d",
115+
event.total_count, event.total_count_change, event.last_policy_kind);
116+
};
117+
118+
auto listener = std::make_shared<Listener>(qos_profile_subscription, topic);
119+
listener->get_options().event_callbacks.incompatible_qos_callback =
120+
[node = listener.get()](rclcpp::QOSRequestedIncompatibleQoSInfo & event) -> void
121+
{
122+
RCLCPP_INFO(
123+
node->get_logger(),
124+
"Requested incompatible qos - total %d delta %d last_policy_kind: %d",
125+
event.total_count, event.total_count_change, event.last_policy_kind);
126+
};
127+
128+
try {
129+
talker->initialize();
130+
listener->initialize();
131+
} catch (const rclcpp::UnsupportedEventTypeException & exc) {
132+
std::cout << '\n' << exc.what() << "\n\n"
133+
"Please try this demo using a different RMW implementation\n";
134+
return -1;
135+
}
136+
137+
// Execution
138+
rclcpp::executors::SingleThreadedExecutor executor;
139+
executor.add_node(talker);
140+
executor.add_node(listener);
141+
142+
while (talker->get_published_count() < num_msgs) {
143+
executor.spin_once();
144+
}
145+
146+
// Cleanup
147+
rclcpp::shutdown();
148+
149+
return 0;
150+
}

0 commit comments

Comments
 (0)