Skip to content
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

Merged
merged 8 commits into from
May 5, 2021

Conversation

gonzodepedro
Copy link
Contributor

First iteration. Connects to #594

Signed-off-by: Gonzalo de Pedro gonzalo@depedro.com.ar

Signed-off-by: Gonzalo de Pedro <gonzalo@depedro.com.ar>
@gonzodepedro
Copy link
Contributor Author

gonzodepedro commented Mar 23, 2021

Things to do:

  • Autodetect when nodes come and go
  • Consider using 'QoS Profile compatibility" API

@jacobperron jacobperron self-requested a review March 24, 2021 20:16
Copy link
Member

@ivanpauno ivanpauno left a 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.

@@ -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):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: snake_case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addresed in 8efa6ed

Comment on lines 109 to 110
except NotImplementedError as e:
return str(e)
Copy link
Member

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

Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addresed in 8efa6ed


try:
for info in node.get_publishers_info_by_topic(args.topic_name):
node_count += 1
Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addresed in 8efa6ed

with NodeStrategy(args) as node:

qos_profile = self.chooseQoS(node, args)
Copy link
Member

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:

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?

Copy link
Member

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

Copy link
Member

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.

Copy link
Contributor Author

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

Comment on lines 109 to 110
except NotImplementedError as e:
return str(e)
Copy link
Member

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 Show resolved Hide resolved
with NodeStrategy(args) as node:

qos_profile = self.chooseQoS(node, args)
Copy link
Member

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.

@jacobperron
Copy link
Member

I agree with @ivanpauno about the following two points:

Autodetect when nodes come and go

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.

Consider using 'QoS Profile compatibility" API

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>
@gonzodepedro
Copy link
Contributor Author

I'm looking at the failing tests

Signed-off-by: Gonzalo de Pedro <gonzalo@depedro.com.ar>
@gonzodepedro gonzodepedro marked this pull request as ready for review April 14, 2021 20:19
Copy link
Member

@ivanpauno ivanpauno left a 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.

Comment on lines 103 to 110
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
Copy link
Member

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):

Suggested change
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)

Copy link
Contributor Author

@gonzodepedro gonzodepedro Apr 22, 2021

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

'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'
Copy link
Member

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?

Suggested change
'to all publishers', end='\n\n'
'to all publishers\n\n'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in: afb4873

'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'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comments here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in: afb4873

@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this argument is unused

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used here

Gonzalo de Pedro added 3 commits April 22, 2021 13:04
Signed-off-by: Gonzalo de Pedro <gonzalo@depedro.com.ar>
Signed-off-by: Gonzalo de Pedro <gonzalo@depedro.com.ar>
Signed-off-by: Gonzalo de Pedro <gonzalo@depedro.com.ar>
Copy link
Member

@jacobperron jacobperron left a 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.

ros2topic/ros2topic/verb/echo.py Outdated Show resolved Hide resolved
Comment on lines +92 to +96
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)
Copy link
Member

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

Suggested change
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)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed on 160fce7

Copy link
Member

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.

ros2topic/ros2topic/verb/echo.py Outdated Show resolved Hide resolved
ros2topic/ros2topic/verb/echo.py Outdated Show resolved Hide resolved
ros2topic/ros2topic/verb/echo.py Outdated Show resolved Hide resolved
ros2topic/ros2topic/verb/echo.py Outdated Show resolved Hide resolved
ros2topic/ros2topic/api/__init__.py Outdated Show resolved Hide resolved
ros2topic/ros2topic/api/__init__.py Show resolved Hide resolved
ros2topic/ros2topic/api/__init__.py Outdated Show resolved Hide resolved
Signed-off-by: Gonzalo de Pedro <gonzalo@depedro.com.ar>
args.qos_durability is not None or
args.qos_depth is not None or
args.qos_history is not None
):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
):
):

Copy link
Member

@jacobperron jacobperron left a 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>
@jacobperron
Copy link
Member

jacobperron commented May 4, 2021

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

return profile


def add_qos_arguments_to_argument_parser(
Copy link
Member

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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants