-
Notifications
You must be signed in to change notification settings - Fork 263
Qos configurability #635
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
Merged
Merged
Qos configurability #635
Changes from 22 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
fdad6e3
Add QoSOverridingOptions
ivanpauno 3300d10
Fix Node.get_parameters_by_prefix() type annotation
ivanpauno 67ac124
Add helper classmethod for using default policies
ivanpauno 80781c9
Add test cases
ivanpauno 5fbc30c
Please linters + improve test setup and teardown
ivanpauno 5e6235b
Declare qos overrides parameters in create_publisher/create_subscript…
ivanpauno b18d0ba
qos -> QoS
ivanpauno ef1e7bb
qos -> QoS
ivanpauno fa434f9
qos -> QoS
ivanpauno 2e5297f
qos -> QoS
ivanpauno 9b06ffa
qos -> QoS
ivanpauno b34a04c
style
ivanpauno 51bddf4
qos -> QoS
ivanpauno fad4467
qos -> QoS
ivanpauno 85a8cb7
grammar
ivanpauno 589d2a2
qos -> QoS
ivanpauno 0c5e7ce
bug
ivanpauno 17c834a
qos -> QoS
ivanpauno 50700ac
Fix _get_qos_policy_parameter type annotation
ivanpauno 523dd59
qos -> QoS
ivanpauno 812d44f
_declare_qos_parameteres -> _declare_qos_parameters
ivanpauno c4f109b
Fix after rebasing
ivanpauno 751a601
Handle exception gracefully
ivanpauno 59951a3
Suppress exception context
ivanpauno 9fce94b
flake8
ivanpauno f8ba331
Add test to CMakeLists
ivanpauno 0446d24
Use declare_or_get logic
ivanpauno a9fc10f
Improve testing
ivanpauno 6b6f2c2
Add error message in validation callback result
ivanpauno File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,177 @@ | ||
| # Copyright 2020 Open Source Robotics Foundation, Inc. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| from typing import Callable | ||
| from typing import Iterable | ||
| from typing import Optional | ||
| from typing import Text | ||
| from typing import Type | ||
| from typing import TYPE_CHECKING | ||
| from typing import Union | ||
|
|
||
| from rcl_interfaces.msg import ParameterDescriptor | ||
|
|
||
| import rclpy | ||
| from rclpy.duration import Duration | ||
| from rclpy.parameter import Parameter | ||
| from rclpy.publisher import Publisher | ||
| from rclpy.qos import QoSPolicyKind | ||
| from rclpy.qos import QoSProfile | ||
| from rclpy.subscription import Subscription | ||
|
|
||
| if TYPE_CHECKING: | ||
| from rclpy.node import Node | ||
|
|
||
|
|
||
| class InvalidQosOverridesError(Exception): | ||
| pass | ||
|
|
||
|
|
||
| class QoSOverridingOptions: | ||
| """Options to customize QoS parameter overrides.""" | ||
|
|
||
| def __init__( | ||
| self, | ||
| policy_kinds: Iterable[QoSPolicyKind], | ||
| *, | ||
| callback: Optional[Callable[[QoSProfile], bool]] = None, | ||
| entity_id: Optional[Text] = None | ||
| ): | ||
| """ | ||
| Construct a QoSOverridingOptions object. | ||
|
|
||
| :param policy_kinds: QoS kinds that will have a declared parameter. | ||
| :param callback: Callback that will be used to validate the QoS profile | ||
| after the paramter overrides get applied. | ||
| :param entity_id: Optional identifier, to disambiguate in the case that different QoS | ||
| policies for the same topic are desired. | ||
| """ | ||
| self._policy_kinds = policy_kinds | ||
| self._callback = callback | ||
| self._entity_id = entity_id | ||
|
|
||
| @property | ||
| def policy_kinds(self) -> Iterable[QoSPolicyKind]: | ||
| """Get QoS policy kinds that will have a parameter override.""" | ||
| return self._policy_kinds | ||
|
|
||
| @property | ||
| def callback(self) -> Optional[Callable[[QoSProfile], bool]]: | ||
| """Get the validation callback.""" | ||
| return self._callback | ||
|
|
||
| @property | ||
| def entity_id(self) -> Optional[Text]: | ||
| """Get the optional entity ID.""" | ||
| return self._entity_id | ||
|
|
||
| @classmethod | ||
| def with_default_policies( | ||
| cls, *, | ||
| callback: Optional[Callable[[QoSProfile], bool]] = None, | ||
| entity_id: Optional[Text] = None | ||
| ) -> 'QoSOverridingOptions': | ||
| return cls( | ||
| policy_kinds=(QoSPolicyKind.HISTORY, QoSPolicyKind.DEPTH, QoSPolicyKind.RELIABILITY), | ||
| callback=callback, | ||
| entity_id=entity_id, | ||
| ) | ||
|
|
||
|
|
||
| def _declare_qos_parameters( | ||
| entity_type: Union[Type[Publisher], Type[Subscription]], | ||
| node: 'Node', | ||
| topic_name: Text, | ||
| qos: QoSProfile, | ||
| options: QoSOverridingOptions | ||
| ) -> QoSProfile: | ||
| """ | ||
| Declare QoS parameters for a Publisher or a Subscription. | ||
|
|
||
| :param entity_type: Either `rclpy.node.Publisher` or `rclpy.node.Subscription`. | ||
| :param node: Node used to declare the parameters. | ||
| :param topic_name: Topic name of the entity being created. | ||
| :param qos: Default QoS settings of the entity being created, that will be overridden | ||
| with the user provided QoS parameter overrides. | ||
| :param options: Options that indicates which parameters are going to be declared. | ||
| """ | ||
| if not issubclass(entity_type, (Publisher, Subscription)): | ||
| raise TypeError('Argument `entity_type` should be a subclass of Publisher or Subscription') | ||
| entity_type_str = 'publisher' if issubclass(entity_type, Publisher) else 'subscription' | ||
| id_suffix = '' if options.entity_id is None else f'_{options.entity_id}' | ||
| name = f'qos_overrides.{topic_name}.{entity_type_str}{id_suffix}.' '{}' | ||
| description = '{}' f' for {entity_type_str} `{topic_name}` with id `{options.entity_id}`' | ||
| allowed_policies = _get_allowed_policies(entity_type) | ||
| for policy in options.policy_kinds: | ||
| if policy not in allowed_policies: | ||
| continue | ||
| policy_name = policy.name.lower() | ||
| descriptor = ParameterDescriptor() | ||
| descriptor.description = description.format(policy_name) | ||
| descriptor.read_only = True | ||
| param = node.declare_parameter( | ||
| name.format(policy_name), | ||
| _get_qos_policy_parameter(qos, policy), | ||
| descriptor) | ||
| _override_qos_policy_with_param(qos, policy, param) | ||
| if options.callback is not None and not options.callback(qos): | ||
| raise InvalidQosOverridesError( | ||
| description.format('Provided QoS overrides') + ', are not valid') | ||
|
|
||
|
|
||
| def _get_allowed_policies(entity_type: Union[Type[Publisher], Type[Subscription]]): | ||
| allowed_policies = list(QoSPolicyKind.__members__.values()) | ||
| if issubclass(entity_type, Subscription): | ||
| allowed_policies.remove(QoSPolicyKind.LIFESPAN) | ||
| return allowed_policies | ||
|
|
||
|
|
||
| def _get_qos_policy_parameter(qos: QoSProfile, policy: QoSPolicyKind) -> Union[str, int, bool]: | ||
| value = getattr(qos, policy.name.lower()) | ||
| if policy in ( | ||
| QoSPolicyKind.LIVELINESS, QoSPolicyKind.RELIABILITY, | ||
| QoSPolicyKind.HISTORY, QoSPolicyKind.DURABILITY | ||
ivanpauno marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ): | ||
| value = value.name.lower() | ||
| if value == 'unknown': | ||
| raise ValueError('User provided QoS profile is invalid') | ||
| if policy in ( | ||
| QoSPolicyKind.LIFESPAN, QoSPolicyKind.DEADLINE, QoSPolicyKind.LIVELINESS_LEASE_DURATION | ||
| ): | ||
| value = value.nanoseconds() | ||
| return value | ||
|
|
||
|
|
||
| def _override_qos_policy_with_param(qos: QoSProfile, policy: QoSPolicyKind, param: Parameter): | ||
| value = param.value | ||
| policy_name = policy.name.lower() | ||
| if policy in ( | ||
| QoSPolicyKind.LIVELINESS, QoSPolicyKind.RELIABILITY, | ||
| QoSPolicyKind.HISTORY, QoSPolicyKind.DURABILITY | ||
| ): | ||
| def capitalize_first_letter(x): | ||
| return x[0].upper() + x[1:] | ||
| # e.g. `policy=QosPolicyKind.LIVELINESS` -> `policy_enum_class=rclpy.qos.LivelinessPolicy` | ||
| policy_enum_class = getattr( | ||
| rclpy.qos, f'{capitalize_first_letter(policy_name)}Policy') | ||
| try: | ||
| value = policy_enum_class[value.upper()] | ||
| except KeyError: | ||
| raise RuntimeError( | ||
| f'Unexpected QoS override for policy `{policy.name.lower()}`: `{value}`') | ||
| if policy in ( | ||
| QoSPolicyKind.LIFESPAN, QoSPolicyKind.DEADLINE, QoSPolicyKind.LIVELINESS_LEASE_DURATION | ||
| ): | ||
| value = Duration(nanoseconds=value) | ||
| setattr(qos, policy.name.lower(), value) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.