-
Notifications
You must be signed in to change notification settings - Fork 161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
QoS autodetection #613
QoS autodetection #613
Conversation
Signed-off-by: Gonzalo de Pedro <gonzalo@depedro.com.ar>
Things to do:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM after making user provided qos settings have higher precedence than the autodetected ones.
I've also left some minor comments
The TODOs can be left for a follow-up, but here some comments:
Autodetect when nodes come and go
I would rather do what was commented here:
Maybe we don't have to detect when a new publisher joins exactly, rather we can detect when there's a new publisher with an incompatible QoS. There is a callback API that can let us know when a new incompatible publisher is detected (see this example).
I actually think that we're installing an "incompatible qos" event handler by default, so maybe nothing is needed here (?).
You can try to force an incompatibility and see if you get a warning.
Consider using 'QoS Profile compatibility" API
I'm not sure if that will help much here.
ros2topic/ros2topic/verb/echo.py
Outdated
@@ -82,6 +84,59 @@ def add_arguments(self, parser, cli_name): | |||
parser.add_argument( | |||
'--raw', action='store_true', help='Echo the raw binary representation') | |||
|
|||
def chooseQoS(self, node, args): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: snake_case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addresed in 8efa6ed
ros2topic/ros2topic/verb/echo.py
Outdated
except NotImplementedError as e: | ||
return str(e) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this needed?
get_publishers_info_by_topic
should be implemented everywhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's not implemented, it seems like a bug and we should just let the exception propagate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addresed in 8efa6ed
ros2topic/ros2topic/verb/echo.py
Outdated
|
||
try: | ||
for info in node.get_publishers_info_by_topic(args.topic_name): | ||
node_count += 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this is the publishers count, not the node count
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addresed in 8efa6ed
ros2topic/ros2topic/verb/echo.py
Outdated
with NodeStrategy(args) as node: | ||
|
||
qos_profile = self.chooseQoS(node, args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ideally, if the user provided an argument that should be preferred over the autodetection logic.
I mean one of these:
ros2cli/ros2topic/ros2topic/api/__init__.py
Lines 189 to 219 in 56db2ba
parser.add_argument( | |
'--qos-profile', | |
choices=rclpy.qos.QoSPresetProfiles.short_keys(), | |
default=default_preset, | |
help='Quality of service preset profile to {} with (default: {})' | |
.format(verb, default_preset)) | |
default_profile = rclpy.qos.QoSPresetProfiles.get_from_short_key( | |
default_preset) | |
parser.add_argument( | |
'--qos-depth', metavar='N', type=int, default=-1, | |
help='Queue size setting to {} with ' | |
'(overrides depth value of --qos-profile option)' | |
.format(verb)) | |
parser.add_argument( | |
'--qos-history', | |
choices=rclpy.qos.QoSHistoryPolicy.short_keys(), | |
help='History of samples setting to {} with ' | |
'(overrides history value of --qos-profile option, default: {})' | |
.format(verb, default_profile.history.short_key)) | |
parser.add_argument( | |
'--qos-reliability', | |
choices=rclpy.qos.QoSReliabilityPolicy.short_keys(), | |
help='Quality of service reliability setting to {} with ' | |
'(overrides reliability value of --qos-profile option, default: {})' | |
.format(verb, default_profile.reliability.short_key)) | |
parser.add_argument( | |
'--qos-durability', | |
choices=rclpy.qos.QoSDurabilityPolicy.short_keys(), | |
help='Quality of service durability setting to {} with ' | |
'(overrides durability value of --qos-profile option, default: {})' | |
.format(verb, default_profile.durability.short_key)) |
@jacobperron what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to handle that I would delete the defaults from the argument and make them optional, so you can check if they were set or not
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. The CLI options should have priority over the auto-detection logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made some changes to give CLI options priority over auto-detection logic
Addresed in 8efa6ed
ros2topic/ros2topic/verb/echo.py
Outdated
except NotImplementedError as e: | ||
return str(e) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's not implemented, it seems like a bug and we should just let the exception propagate.
ros2topic/ros2topic/verb/echo.py
Outdated
with NodeStrategy(args) as node: | ||
|
||
qos_profile = self.chooseQoS(node, args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. The CLI options should have priority over the auto-detection logic.
I agree with @ivanpauno about the following two points:
Just produce a warning if a new publisher joins with incompatible QoS (don't update the subscriber QoS). Maybe we don't have to implement the warning as Ivan points out.
I think the API we could reuse would be this proposed RMW API: ros2/rmw#304 Since that API doesn't exist (yet), we could just add a comment referencing the proposal. |
Signed-off-by: Gonzalo de Pedro <gonzalo@depedro.com.ar>
I'm looking at the failing tests |
Signed-off-by: Gonzalo de Pedro <gonzalo@depedro.com.ar>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
I left some minor comments that can be ignored.
We should wait until a galactic
branch is created before merging though.
ros2topic/ros2topic/verb/echo.py
Outdated
for info in node.get_publishers_info_by_topic(args.topic_name): | ||
publishers_count += 1 | ||
if (info.qos_profile.reliability == | ||
QoSReliabilityPolicy.RELIABLE): | ||
reliability_reliable_endpoints_count += 1 | ||
if (info.qos_profile.durability == | ||
QoSDurabilityPolicy.TRANSIENT_LOCAL): | ||
durability_transient_local_endpoints_count += 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor style comment, feel free to ignore:
this is a bit more readable (though it iters twice over the list):
for info in node.get_publishers_info_by_topic(args.topic_name): | |
publishers_count += 1 | |
if (info.qos_profile.reliability == | |
QoSReliabilityPolicy.RELIABLE): | |
reliability_reliable_endpoints_count += 1 | |
if (info.qos_profile.durability == | |
QoSDurabilityPolicy.TRANSIENT_LOCAL): | |
durability_transient_local_endpoints_count += 1 | |
pubs_info = node.get_publishers_info_by_topic(args.topic_name) | |
publishers_count = len(pubs_info) | |
reliability_reliable_endpoints_count = sum( | |
info.qos_profile.reliability == QoSReliabilityPolicy.RELIABLE for info in pubs_info) | |
durability_transient_local_endpoints_count = sum( | |
info.qos_profile.durability == QoSDurabilityPolicy.TRANSIENT_LOCAL for info in pubs_info) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing this out. I made some changes based on your comments, but not exactly the one you suggested.
Addressed in: afb4873
ros2topic/ros2topic/verb/echo.py
Outdated
'Some, but not all, publisher are offering ' | ||
'QoSReliabilityPolicy.RELIABLE. Falling back to ' | ||
'QoSReliabilityPolicy.BEST_EFFORT as it will connect ' | ||
'to all publishers', end='\n\n' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there any benefit of using end
?
do we need to print this message?
should we use stderr?
'to all publishers', end='\n\n' | |
'to all publishers\n\n' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in: afb4873
ros2topic/ros2topic/verb/echo.py
Outdated
'Some, but not all, publisher are offering ' | ||
'QoSDurabilityPolicy.TRANSIENT_LOCAL. Falling back to ' | ||
'QoSDurabilityPolicy.VOLATILE as it will connect ' | ||
'to all publishers', end='\n\n' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comments here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in: afb4873
ros2topic/ros2topic/api/__init__.py
Outdated
@@ -182,20 +186,20 @@ def qos_profile_from_short_keys( | |||
|
|||
|
|||
def add_qos_arguments_to_argument_parser( | |||
parser: argparse.ArgumentParser, is_publisher: bool = True, default_preset: str = 'sensor_data' | |||
parser: argparse.ArgumentParser, is_publisher: bool = True, | |||
default_preset: str = default_preset_str |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this argument is unused
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used here
Signed-off-by: Gonzalo de Pedro <gonzalo@depedro.com.ar>
Signed-off-by: Gonzalo de Pedro <gonzalo@depedro.com.ar>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly minor comments; I have some concerns about reusing the argparse logic between the pub
and echo
verbs. The implementation of choose_qos
LGTM.
return qos_profile_from_short_keys(args.qos_profile, | ||
reliability=args.qos_reliability, | ||
durability=args.qos_durability, | ||
depth=args.qos_depth, | ||
history=args.qos_history) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: prefer hanging indents
return qos_profile_from_short_keys(args.qos_profile, | |
reliability=args.qos_reliability, | |
durability=args.qos_durability, | |
depth=args.qos_depth, | |
history=args.qos_history) | |
return qos_profile_from_short_keys( | |
args.qos_profile, | |
reliability=args.qos_reliability, | |
durability=args.qos_durability, | |
depth=args.qos_depth, | |
history=args.qos_history) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed on 160fce7
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The style didn't change here.
Signed-off-by: Gonzalo de Pedro <gonzalo@depedro.com.ar>
ros2topic/ros2topic/verb/echo.py
Outdated
args.qos_durability is not None or | ||
args.qos_depth is not None or | ||
args.qos_history is not None | ||
): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
): | |
): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There a linter error that my suggestion above should fix. LGTM with green CI.
Signed-off-by: Gonzalo de Pedro <gonzalo@depedro.com.ar>
return profile | ||
|
||
|
||
def add_qos_arguments_to_argument_parser( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've found an instance of this function being used in the wild. In the future, we should be mindful of removing API from this file since it appears as part of the public API (unless we otherwise state that users should't rely on it).
First iteration. Connects to #594
Signed-off-by: Gonzalo de Pedro gonzalo@depedro.com.ar