From 0aa7ce7db9a52b696167c5bd2e56af572c722b31 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 11:40:51 +0000 Subject: [PATCH] [CI] Code coverage + pre-commit (backport #1413) (#1414) --- .github/workflows/ci-coverage-build.yml | 52 ------ .github/workflows/ci-format.yml | 23 --- .github/workflows/ci-ros-lint.yml | 57 ------ .github/workflows/humble-coverage-build.yml | 17 ++ .github/workflows/humble-pre-commit.yml | 14 ++ .github/workflows/iron-coverage-build.yml | 17 ++ .github/workflows/iron-pre-commit.yml | 14 ++ .../reusable-ros-tooling-source-build.yml | 56 ------ .github/workflows/rolling-coverage-build.yml | 17 ++ .github/workflows/rolling-pre-commit.yml | 14 ++ .github/workflows/update-pre-commit.yml | 12 ++ .pre-commit-config.yaml | 47 ++--- CONTRIBUTING.md | 5 +- .../controller_manager/__init__.py | 22 +-- .../controller_manager_services.py | 14 +- .../controller_manager/launch_utils.py | 59 ++++--- .../controller_manager/spawner.py | 162 +++++++++++------- .../controller_manager/unspawner.py | 12 +- .../doc/controller_chaining.rst | 2 +- controller_manager/src/controller_manager.cpp | 2 +- .../test/controller_manager_test_common.hpp | 2 +- .../src/mock_components/generic_system.cpp | 2 +- hardware_interface/test/test_handle.cpp | 8 +- .../test/joint_limits_rosparam.launch.py | 4 +- .../joint_limits_interface.hpp | 14 +- .../test/joint_limits_urdf_test.cpp | 3 +- ros2controlcli/ros2controlcli/api/__init__.py | 24 +-- .../ros2controlcli/command/control.py | 6 +- .../verb/list_controller_types.py | 2 +- .../ros2controlcli/verb/list_controllers.py | 57 +++--- .../verb/list_hardware_components.py | 39 +++-- .../verb/list_hardware_interfaces.py | 24 ++- .../ros2controlcli/verb/load_controller.py | 28 +-- .../verb/reload_controller_libraries.py | 6 +- .../verb/set_controller_state.py | 56 +++--- .../ros2controlcli/verb/switch_controllers.py | 54 +++--- .../ros2controlcli/verb/unload_controller.py | 6 +- .../verb/view_controller_chains.py | 144 ++++++++++------ ros2controlcli/setup.py | 72 ++++---- .../test/test_view_controller_chains.py | 8 +- .../controller_manager.py | 98 +++++------ .../rqt_controller_manager/main.py | 4 +- .../rqt_controller_manager/update_combo.py | 3 +- rqt_controller_manager/setup.py | 36 ++-- 44 files changed, 690 insertions(+), 628 deletions(-) delete mode 100644 .github/workflows/ci-coverage-build.yml delete mode 100644 .github/workflows/ci-format.yml delete mode 100644 .github/workflows/ci-ros-lint.yml create mode 100644 .github/workflows/humble-coverage-build.yml create mode 100644 .github/workflows/humble-pre-commit.yml create mode 100644 .github/workflows/iron-coverage-build.yml create mode 100644 .github/workflows/iron-pre-commit.yml delete mode 100644 .github/workflows/reusable-ros-tooling-source-build.yml create mode 100644 .github/workflows/rolling-coverage-build.yml create mode 100644 .github/workflows/rolling-pre-commit.yml create mode 100644 .github/workflows/update-pre-commit.yml diff --git a/.github/workflows/ci-coverage-build.yml b/.github/workflows/ci-coverage-build.yml deleted file mode 100644 index b6cafc31d8..0000000000 --- a/.github/workflows/ci-coverage-build.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Coverage Build -on: - workflow_dispatch: - push: - branches: - - humble - pull_request: - branches: - - humble - -jobs: - coverage: - name: coverage build - runs-on: ubuntu-22.04 - strategy: - fail-fast: false - env: - ROS_DISTRO: humble - steps: - - uses: ros-tooling/setup-ros@v0.7 - with: - required-ros-distributions: ${{ env.ROS_DISTRO }} - - uses: actions/checkout@v4 - - uses: ros-tooling/action-ros-ci@0.3.6 - with: - target-ros2-distro: ${{ env.ROS_DISTRO }} - import-token: ${{ secrets.GITHUB_TOKEN }} - # build all packages listed in the meta package - package-name: - controller_interface - controller_manager - hardware_interface - transmission_interface - - vcs-repo-file-url: | - https://raw.githubusercontent.com/${{ github.repository }}/${{ github.sha }}/ros2_control-not-released.${{ env.ROS_DISTRO }}.repos?token=${{ secrets.GITHUB_TOKEN }} - colcon-defaults: | - { - "build": { - "mixin": ["coverage-gcc"] - } - } - colcon-mixin-repository: https://raw.githubusercontent.com/colcon/colcon-mixin-repository/master/index.yaml - - uses: codecov/codecov-action@v4.0.1 - with: - file: ros_ws/lcov/total_coverage.info - flags: unittests - name: codecov-umbrella - - uses: actions/upload-artifact@v4 - with: - name: colcon-logs-ubuntu-22.04-coverage-humble - path: ros_ws/log diff --git a/.github/workflows/ci-format.yml b/.github/workflows/ci-format.yml deleted file mode 100644 index 563bfc813c..0000000000 --- a/.github/workflows/ci-format.yml +++ /dev/null @@ -1,23 +0,0 @@ -# This is a format job. Pre-commit has a first-party GitHub action, so we use -# that: https://github.com/pre-commit/action - -name: Format - -on: - workflow_dispatch: - pull_request: - -jobs: - pre-commit: - name: Format - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.10' - - name: Install system hooks - run: sudo apt install -qq clang-format-14 cppcheck - - uses: pre-commit/action@v3.0.1 - with: - extra_args: --all-files --hook-stage manual diff --git a/.github/workflows/ci-ros-lint.yml b/.github/workflows/ci-ros-lint.yml deleted file mode 100644 index d89046fc51..0000000000 --- a/.github/workflows/ci-ros-lint.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: ROS Lint -on: - pull_request: - -jobs: - ament_lint: - name: ament_${{ matrix.linter }} - runs-on: ubuntu-22.04 - strategy: - fail-fast: false - matrix: - linter: [cppcheck, copyright, lint_cmake] - env: - AMENT_CPPCHECK_ALLOW_SLOW_VERSIONS: true - steps: - - uses: actions/checkout@v4 - - uses: ros-tooling/setup-ros@v0.7 - - uses: ros-tooling/action-ros-lint@v0.1 - with: - distribution: humble - linter: ${{ matrix.linter }} - package-name: - controller_interface - controller_manager - controller_manager_msgs - hardware_interface - hardware_interface_testing - ros2controlcli - ros2_control - ros2_control_test_assets - transmission_interface - - ament_lint_100: - name: ament_${{ matrix.linter }} - runs-on: ubuntu-22.04 - strategy: - fail-fast: false - matrix: - linter: [cpplint] - steps: - - uses: actions/checkout@v4 - - uses: ros-tooling/setup-ros@v0.7 - - uses: ros-tooling/action-ros-lint@v0.1 - with: - distribution: humble - linter: cpplint - arguments: "--linelength=100 --filter=-whitespace/newline" - package-name: - controller_interface - controller_manager - controller_manager_msgs - hardware_interface - hardware_interface_testing - ros2controlcli - ros2_control - ros2_control_test_assets - transmission_interface diff --git a/.github/workflows/humble-coverage-build.yml b/.github/workflows/humble-coverage-build.yml new file mode 100644 index 0000000000..0910572227 --- /dev/null +++ b/.github/workflows/humble-coverage-build.yml @@ -0,0 +1,17 @@ +name: Coverage Build - Humble +on: + workflow_dispatch: + push: + branches: + - humble + pull_request: + branches: + - humble + +jobs: + coverage_humble: + uses: ros-controls/ros2_control_ci/.github/workflows/reusable-build-coverage.yml@master + secrets: inherit + with: + ros_distro: humble + os_name: ubuntu-22.04 diff --git a/.github/workflows/humble-pre-commit.yml b/.github/workflows/humble-pre-commit.yml new file mode 100644 index 0000000000..be8c84b05b --- /dev/null +++ b/.github/workflows/humble-pre-commit.yml @@ -0,0 +1,14 @@ +name: Pre-Commit - Humble + +on: + workflow_dispatch: + pull_request: + branches: + - humble + +jobs: + pre-commit: + uses: ros-controls/ros2_control_ci/.github/workflows/reusable-pre-commit.yml@master + with: + ros_distro: humble + os_name: ubuntu-22.04 diff --git a/.github/workflows/iron-coverage-build.yml b/.github/workflows/iron-coverage-build.yml new file mode 100644 index 0000000000..d82c52bf51 --- /dev/null +++ b/.github/workflows/iron-coverage-build.yml @@ -0,0 +1,17 @@ +name: Coverage Build - Iron +on: + workflow_dispatch: + push: + branches: + - iron + pull_request: + branches: + - iron + +jobs: + coverage_iron: + uses: ros-controls/ros2_control_ci/.github/workflows/reusable-build-coverage.yml@master + secrets: inherit + with: + ros_distro: iron + os_name: ubuntu-22.04 diff --git a/.github/workflows/iron-pre-commit.yml b/.github/workflows/iron-pre-commit.yml new file mode 100644 index 0000000000..60ad26d073 --- /dev/null +++ b/.github/workflows/iron-pre-commit.yml @@ -0,0 +1,14 @@ +name: Pre-Commit - Iron + +on: + workflow_dispatch: + pull_request: + branches: + - iron + +jobs: + pre-commit: + uses: ros-controls/ros2_control_ci/.github/workflows/reusable-pre-commit.yml@master + with: + ros_distro: iron + os_name: ubuntu-22.04 diff --git a/.github/workflows/reusable-ros-tooling-source-build.yml b/.github/workflows/reusable-ros-tooling-source-build.yml deleted file mode 100644 index 42e3800837..0000000000 --- a/.github/workflows/reusable-ros-tooling-source-build.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Reusable industrial_ci Workflow with Cache -# Reusable action to simplify dealing with ROS/ROS2 industrial_ci builds with cache -# author: Denis Štogl - -on: - workflow_call: - inputs: - ros_distro: - description: 'ROS2 distribution name' - required: true - type: string - ref: - description: 'Reference on which the repo should be checkout. Usually is this name of a branch or a tag.' - required: true - type: string - ros2_repo_branch: - description: 'Branch in the ros2/ros2 repozitory from which ".repos" should be used. Possible values: master (Rolling), humble, galactic, foxy.' - default: 'master' - required: false - type: string - -jobs: - reusable_ros_tooling_source_build: - name: ${{ inputs.ros_distro }} ubuntu-22.04 - runs-on: ubuntu-22.04 - strategy: - fail-fast: false - steps: - - uses: ros-tooling/setup-ros@v0.7 - with: - required-ros-distributions: ${{ inputs.ros_distro }} - - uses: actions/checkout@v4 - with: - ref: ${{ inputs.ref }} - - uses: ros-tooling/action-ros-ci@0.3.6 - with: - target-ros2-distro: ${{ inputs.ros_distro }} - # build all packages listed in the meta package - ref: ${{ inputs.ref }} # otherwise the default branch is used for scheduled workflows - package-name: - controller_interface - controller_manager - controller_manager_msgs - hardware_interface - ros2controlcli - ros2_control - ros2_control_test_assets - transmission_interface - vcs-repo-file-url: | - https://raw.githubusercontent.com/ros2/ros2/${{ inputs.ros2_repo_branch }}/ros2.repos - https://raw.githubusercontent.com/${{ github.repository }}/${{ github.sha }}/ros2_control.${{ inputs.ros_distro }}.repos?token=${{ secrets.GITHUB_TOKEN }} - colcon-mixin-repository: https://raw.githubusercontent.com/colcon/colcon-mixin-repository/master/index.yaml - - uses: actions/upload-artifact@v4 - with: - name: colcon-logs-ubuntu-22.04 - path: ros_ws/log diff --git a/.github/workflows/rolling-coverage-build.yml b/.github/workflows/rolling-coverage-build.yml new file mode 100644 index 0000000000..4d4750c54c --- /dev/null +++ b/.github/workflows/rolling-coverage-build.yml @@ -0,0 +1,17 @@ +name: Coverage Build - Rolling +on: + workflow_dispatch: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + coverage_rolling: + uses: ros-controls/ros2_control_ci/.github/workflows/reusable-build-coverage.yml@master + secrets: inherit + with: + ros_distro: rolling + os_name: ubuntu-22.04 diff --git a/.github/workflows/rolling-pre-commit.yml b/.github/workflows/rolling-pre-commit.yml new file mode 100644 index 0000000000..9c87311bd7 --- /dev/null +++ b/.github/workflows/rolling-pre-commit.yml @@ -0,0 +1,14 @@ +name: Pre-Commit - Rolling + +on: + workflow_dispatch: + pull_request: + branches: + - master + +jobs: + pre-commit: + uses: ros-controls/ros2_control_ci/.github/workflows/reusable-pre-commit.yml@master + with: + ros_distro: rolling + os_name: ubuntu-22.04 diff --git a/.github/workflows/update-pre-commit.yml b/.github/workflows/update-pre-commit.yml new file mode 100644 index 0000000000..8b9545dff1 --- /dev/null +++ b/.github/workflows/update-pre-commit.yml @@ -0,0 +1,12 @@ +name: Auto Update pre-commit +# Update pre-commit config and create PR if changes are detected +# author: Christoph Fröhlich + +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * 0' # Run every Sunday at midnight + +jobs: + auto_update_and_create_pr: + uses: ros-controls/ros2_control_ci/.github/workflows/reusable-update-pre-commit.yml@master diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b7972346fb..6da427c6ee 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: # Standard hooks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-added-large-files - id: check-ast @@ -36,33 +36,35 @@ repos: # Python hooks - repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 + rev: v3.15.1 hooks: - id: pyupgrade args: [--py36-plus] # PyDocStyle - repo: https://github.com/PyCQA/pydocstyle - rev: 6.2.2 + rev: 6.3.0 hooks: - id: pydocstyle args: ["--ignore=D100,D101,D102,D103,D104,D105,D106,D107,D203,D212,D404"] + - repo: https://github.com/psf/black + rev: 24.2.0 + hooks: + - id: black + args: ["--line-length=99"] + - repo: https://github.com/pycqa/flake8 - rev: 6.0.0 + rev: 7.0.0 hooks: - id: flake8 args: ["--extend-ignore=E501"] # CPP hooks - - repo: local + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v17.0.6 hooks: - id: clang-format - name: clang-format - description: Format files with ClangFormat. - entry: clang-format-14 - language: system - files: \.(c|cc|cxx|cpp|frag|glsl|h|hpp|hxx|ih|ispc|ipp|java|js|m|proto|vert)$ args: ['-fallback-style=none', '-i'] - repo: local @@ -70,18 +72,15 @@ repos: - id: ament_cppcheck name: ament_cppcheck description: Static code analysis of C/C++ files. - stages: [commit] - entry: ament_cppcheck + entry: env AMENT_CPPCHECK_ALLOW_SLOW_VERSIONS=1 ament_cppcheck language: system files: \.(h\+\+|h|hh|hxx|hpp|cuh|c|cc|cpp|cu|c\+\+|cxx|tpp|txx)$ - # Maybe use https://github.com/cpplint/cpplint instead - repo: local hooks: - id: ament_cpplint name: ament_cpplint description: Static code analysis of C/C++ files. - stages: [commit] entry: ament_cpplint language: system files: \.(h\+\+|h|hh|hxx|hpp|cuh|c|cc|cpp|cu|c\+\+|cxx|tpp|txx)$ @@ -93,7 +92,6 @@ repos: - id: ament_lint_cmake name: ament_lint_cmake description: Check format of CMakeLists.txt files. - stages: [commit] entry: ament_lint_cmake language: system files: CMakeLists\.txt$ @@ -104,7 +102,6 @@ repos: - id: ament_copyright name: ament_copyright description: Check if copyright notice is available in all files. - stages: [commit] entry: ament_copyright language: system @@ -117,7 +114,7 @@ repos: exclude: CHANGELOG\.rst$ - repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.9.0 + rev: v1.10.0 hooks: - id: rst-backticks exclude: CHANGELOG\.rst$ @@ -127,8 +124,18 @@ repos: # Spellcheck in comments and docs # skipping of *.svg files is not working... - repo: https://github.com/codespell-project/codespell - rev: v2.2.2 + rev: v2.2.6 hooks: - id: codespell - args: ['--write-changes'] - exclude: CHANGELOG\.rst|\.(svg|pyc)$ + args: ['--write-changes', '--uri-ignore-words-list=ist', '-L manuel'] + exclude: CHANGELOG\.rst|\.(svg|pyc|drawio)$ + + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.28.0 + hooks: + - id: check-github-workflows + args: ["--verbose"] + - id: check-github-actions + args: ["--verbose"] + - id: check-dependabot + args: ["--verbose"] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d9cdc27041..df91cfbf20 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,7 +54,9 @@ As this project, by default, uses the default GitHub issue labels ## Licensing -Any contribution that you make to this repository will be under the Apache 2 License, as dictated by that [license]: +Any contribution that you make to this repository will +be under the Apache 2 License, as dictated by that +[license](http://www.apache.org/licenses/LICENSE-2.0.html): ~~~ 5. Submission of Contributions. Unless You explicitly state otherwise, @@ -69,4 +71,3 @@ Any contribution that you make to this repository will be under the Apache 2 Lic [issues]: https://github.com/ros-controls/ros2_control/issues [closed-issues]: https://github.com/ros-controls/ros2_control/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20 [help-wanted]: https://github.com/ros-controls/ros2_control/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22 -[license]: http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/controller_manager/controller_manager/__init__.py b/controller_manager/controller_manager/__init__.py index 849dee7fc0..f49bed4d34 100644 --- a/controller_manager/controller_manager/__init__.py +++ b/controller_manager/controller_manager/__init__.py @@ -22,18 +22,18 @@ reload_controller_libraries, set_hardware_component_state, switch_controllers, - unload_controller + unload_controller, ) __all__ = [ - 'configure_controller', - 'list_controller_types', - 'list_controllers', - 'list_hardware_components', - 'list_hardware_interfaces', - 'load_controller', - 'reload_controller_libraries', - 'set_hardware_component_state', - 'switch_controllers', - 'unload_controller', + "configure_controller", + "list_controller_types", + "list_controllers", + "list_hardware_components", + "list_hardware_interfaces", + "load_controller", + "reload_controller_libraries", + "set_hardware_component_state", + "switch_controllers", + "unload_controller", ] diff --git a/controller_manager/controller_manager/controller_manager_services.py b/controller_manager/controller_manager/controller_manager_services.py index 91c7ab7517..cbc40541b6 100644 --- a/controller_manager/controller_manager/controller_manager_services.py +++ b/controller_manager/controller_manager/controller_manager_services.py @@ -33,17 +33,18 @@ def service_caller(node, service_name, service_type, request, service_timeout=10 if not cli.service_is_ready(): node.get_logger().debug( - f'waiting {service_timeout} seconds for service {service_name} to become available...') + f"waiting {service_timeout} seconds for service {service_name} to become available..." + ) if not cli.wait_for_service(service_timeout): - raise RuntimeError(f'Could not contact service {service_name}') + raise RuntimeError(f"Could not contact service {service_name}") - node.get_logger().debug(f'requester: making request: {request}\n') + node.get_logger().debug(f"requester: making request: {request}\n") future = cli.call_async(request) rclpy.spin_until_future_complete(node, future) if future.result() is not None: return future.result() else: - raise RuntimeError(f'Exception while calling service: {future.exception()}') + raise RuntimeError(f"Exception while calling service: {future.exception()}") def configure_controller(node, controller_manager_name, controller_name, service_timeout=10.0): @@ -156,8 +157,9 @@ def switch_controllers( request.strictness = SwitchController.Request.BEST_EFFORT request.activate_asap = activate_asap request.timeout = rclpy.duration.Duration(seconds=timeout).to_msg() - return service_caller(node, f'{controller_manager_name}/switch_controller', - SwitchController, request) + return service_caller( + node, f"{controller_manager_name}/switch_controller", SwitchController, request + ) def unload_controller(node, controller_manager_name, controller_name, service_timeout=10.0): diff --git a/controller_manager/controller_manager/launch_utils.py b/controller_manager/controller_manager/launch_utils.py index e9adfaa50f..5a23c02cec 100644 --- a/controller_manager/controller_manager/launch_utils.py +++ b/controller_manager/controller_manager/launch_utils.py @@ -19,9 +19,9 @@ from launch_ros.actions import Node -def generate_load_controller_launch_description(controller_name, - controller_type=None, - controller_params_file=None): +def generate_load_controller_launch_description( + controller_name, controller_type=None, controller_params_file=None +): """ Generate launch description for loading a controller using spawner. @@ -44,38 +44,53 @@ def generate_load_controller_launch_description(controller_name, """ declare_controller_mgr_name = DeclareLaunchArgument( - 'controller_manager_name', default_value='controller_manager', - description='Controller manager node name' + "controller_manager_name", + default_value="controller_manager", + description="Controller manager node name", ) declare_unload_on_kill = DeclareLaunchArgument( - 'unload_on_kill', default_value='false', - description='Wait until the node is interrupted and then unload controller' + "unload_on_kill", + default_value="false", + description="Wait until the node is interrupted and then unload controller", ) spawner_arguments = [ controller_name, - '--controller-manager', - LaunchConfiguration('controller_manager_name') + "--controller-manager", + LaunchConfiguration("controller_manager_name"), ] if controller_type: - spawner_arguments += ['--controller-type', controller_type] + spawner_arguments += ["--controller-type", controller_type] if controller_params_file: - spawner_arguments += ['--param-file', controller_params_file] + spawner_arguments += ["--param-file", controller_params_file] # Setting --unload-on-kill if launch arg unload_on_kill is "true" # See https://github.com/ros2/launch/issues/290 - spawner_arguments += [PythonExpression( - ['"--unload-on-kill"', ' if "true" == "', - LaunchConfiguration('unload_on_kill'), '" else ""'] - )] + spawner_arguments += [ + PythonExpression( + [ + '"--unload-on-kill"', + ' if "true" == "', + LaunchConfiguration("unload_on_kill"), + '" else ""', + ] + ) + ] - spawner = Node(package='controller_manager', executable='spawner', - arguments=spawner_arguments, shell=True, output='screen') + spawner = Node( + package="controller_manager", + executable="spawner", + arguments=spawner_arguments, + shell=True, + output="screen", + ) - return LaunchDescription([ - declare_controller_mgr_name, - declare_unload_on_kill, - spawner, - ]) + return LaunchDescription( + [ + declare_controller_mgr_name, + declare_unload_on_kill, + spawner, + ] + ) diff --git a/controller_manager/controller_manager/spawner.py b/controller_manager/controller_manager/spawner.py index 44c31ef001..6f4bb19892 100644 --- a/controller_manager/controller_manager/spawner.py +++ b/controller_manager/controller_manager/spawner.py @@ -20,8 +20,13 @@ import time import warnings -from controller_manager import configure_controller, list_controllers, \ - load_controller, switch_controllers, unload_controller +from controller_manager import ( + configure_controller, + list_controllers, + load_controller, + switch_controllers, + unload_controller, +) import rclpy from rcl_interfaces.msg import Parameter @@ -35,15 +40,15 @@ class bcolors: - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKCYAN = '\033[96m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' - UNDERLINE = '\033[4m' + HEADER = "\033[95m" + OKBLUE = "\033[94m" + OKCYAN = "\033[96m" + OKGREEN = "\033[92m" + WARNING = "\033[93m" + FAIL = "\033[91m" + ENDC = "\033[0m" + BOLD = "\033[1m" + UNDERLINE = "\033[4m" def first_match(iterable, predicate): @@ -56,21 +61,22 @@ def wait_for_value_or(function, node, timeout, default, description): if result: return result node.get_logger().info( - f'Waiting for {description}', - throttle_duration_sec=2, skip_first=True) + f"Waiting for {description}", throttle_duration_sec=2, skip_first=True + ) time.sleep(0.2) return default def combine_name_and_namespace(name_and_namespace): node_name, namespace = name_and_namespace - return namespace + ('' if namespace.endswith('/') else '/') + node_name + return namespace + ("" if namespace.endswith("/") else "/") + node_name def find_node_and_namespace(node, full_node_name): node_names_and_namespaces = node.get_node_names_and_namespaces() - return first_match(node_names_and_namespaces, - lambda n: combine_name_and_namespace(n) == full_node_name) + return first_match( + node_names_and_namespaces, lambda n: combine_name_and_namespace(n) == full_node_name + ) def has_service_names(node, node_name, node_namespace, service_names): @@ -84,29 +90,37 @@ def has_service_names(node, node_name, node_namespace, service_names): def wait_for_controller_manager(node, controller_manager, timeout_duration): # List of service names from controller_manager we wait for service_names = ( - f'{controller_manager}/configure_controller', - f'{controller_manager}/list_controllers', - f'{controller_manager}/list_controller_types', - f'{controller_manager}/list_hardware_components', - f'{controller_manager}/list_hardware_interfaces', - f'{controller_manager}/load_controller', - f'{controller_manager}/reload_controller_libraries', - f'{controller_manager}/switch_controller', - f'{controller_manager}/unload_controller' + f"{controller_manager}/configure_controller", + f"{controller_manager}/list_controllers", + f"{controller_manager}/list_controller_types", + f"{controller_manager}/list_hardware_components", + f"{controller_manager}/list_hardware_interfaces", + f"{controller_manager}/load_controller", + f"{controller_manager}/reload_controller_libraries", + f"{controller_manager}/switch_controller", + f"{controller_manager}/unload_controller", ) # Wait for controller_manager timeout = node.get_clock().now() + Duration(seconds=timeout_duration) node_and_namespace = wait_for_value_or( lambda: find_node_and_namespace(node, controller_manager), - node, timeout, None, f'\'{controller_manager}\' node to exist') + node, + timeout, + None, + f"'{controller_manager}' node to exist", + ) # Wait for the services if the node was found if node_and_namespace: node_name, namespace = node_and_namespace return wait_for_value_or( lambda: has_service_names(node, node_name, namespace, service_names), - node, timeout, False, f"'{controller_manager}' services to be available") + node, + timeout, + False, + f"'{controller_manager}' services to be available", + ) return False @@ -122,36 +136,59 @@ def main(args=None): parser = argparse.ArgumentParser() parser.add_argument("controller_names", help="List of controllers", nargs="+") parser.add_argument( - '-c', '--controller-manager', help='Name of the controller manager ROS node', - default='controller_manager', required=False) + "-c", + "--controller-manager", + help="Name of the controller manager ROS node", + default="controller_manager", + required=False, + ) parser.add_argument( - '-p', '--param-file', - help='Controller param file to be loaded into controller node before configure', - required=False) + "-p", + "--param-file", + help="Controller param file to be loaded into controller node before configure", + required=False, + ) parser.add_argument( - '-n', '--namespace', - help='Namespace for the controller', default='', - required=False) + "-n", "--namespace", help="Namespace for the controller", default="", required=False + ) parser.add_argument( - '--load-only', help='Only load the controller and leave unconfigured.', - action='store_true', required=False) + "--load-only", + help="Only load the controller and leave unconfigured.", + action="store_true", + required=False, + ) parser.add_argument( - '--stopped', help='Load and configure the controller, however do not activate them', - action='store_true', required=False) + "--stopped", + help="Load and configure the controller, however do not activate them", + action="store_true", + required=False, + ) parser.add_argument( - '--inactive', help='Load and configure the controller, however do not activate them', - action='store_true', required=False) + "--inactive", + help="Load and configure the controller, however do not activate them", + action="store_true", + required=False, + ) parser.add_argument( - '-t', '--controller-type', - help='If not provided it should exist in the controller manager namespace', - default=None, required=False) + "-t", + "--controller-type", + help="If not provided it should exist in the controller manager namespace", + default=None, + required=False, + ) parser.add_argument( - '-u', '--unload-on-kill', - help='Wait until this application is interrupted and unload controller', - action='store_true') + "-u", + "--unload-on-kill", + help="Wait until this application is interrupted and unload controller", + action="store_true", + ) parser.add_argument( - '--controller-manager-timeout', - help='Time to wait for the controller manager', required=False, default=10, type=int) + "--controller-manager-timeout", + help="Time to wait for the controller manager", + required=False, + default=10, + type=int, + ) parser.add_argument( "--activate-as-group", help="Activates all the parsed controllers list together instead of one by one." @@ -176,15 +213,16 @@ def main(args=None): if not controller_manager_name.startswith("/"): spawner_namespace = node.get_namespace() - if spawner_namespace != '/': + if spawner_namespace != "/": controller_manager_name = f"{spawner_namespace}/{controller_manager_name}" else: controller_manager_name = f"/{controller_manager_name}" try: - if not wait_for_controller_manager(node, controller_manager_name, - controller_manager_timeout): - node.get_logger().error('Controller manager not available') + if not wait_for_controller_manager( + node, controller_manager_name, controller_manager_timeout + ): + node.get_logger().error("Controller manager not available") return 1 for controller_name in controller_names: @@ -329,7 +367,7 @@ def main(args=None): return 0 try: - node.get_logger().info('Waiting until interrupt to unload controllers') + node.get_logger().info("Waiting until interrupt to unload controllers") while True: time.sleep(1) except KeyboardInterrupt: @@ -340,29 +378,29 @@ def main(args=None): node, controller_manager_name, controller_names, [], True, True, 5.0 ) if not ret.ok: - node.get_logger().error('Failed to deactivate controller') + node.get_logger().error("Failed to deactivate controller") return 1 - node.get_logger().info('Deactivated controller') + node.get_logger().info("Deactivated controller") elif args.stopped: node.get_logger().warn('"--stopped" flag is deprecated use "--inactive" instead') - ret = unload_controller( - node, controller_manager_name, controller_name) + ret = unload_controller(node, controller_manager_name, controller_name) if not ret.ok: - node.get_logger().error('Failed to unload controller') + node.get_logger().error("Failed to unload controller") return 1 - node.get_logger().info('Unloaded controller') + node.get_logger().info("Unloaded controller") return 0 finally: rclpy.shutdown() -if __name__ == '__main__': +if __name__ == "__main__": warnings.warn( "'spawner.py' is deprecated, please use 'spawner' (without .py extension)", - DeprecationWarning) + DeprecationWarning, + ) ret = main() sys.exit(ret) diff --git a/controller_manager/controller_manager/unspawner.py b/controller_manager/controller_manager/unspawner.py index efa385e0a5..28d94dfe58 100644 --- a/controller_manager/controller_manager/unspawner.py +++ b/controller_manager/controller_manager/unspawner.py @@ -30,8 +30,12 @@ def main(args=None): parser = argparse.ArgumentParser() parser.add_argument("controller_names", help="Name of the controller", nargs="+") parser.add_argument( - '-c', '--controller-manager', help='Name of the controller manager ROS node', - default='/controller_manager', required=False) + "-c", + "--controller-manager", + help="Name of the controller manager ROS node", + default="/controller_manager", + required=False, + ) command_line_args = rclpy.utilities.remove_ros_args(args=sys.argv)[1:] args = parser.parse_args(command_line_args) @@ -58,10 +62,10 @@ def main(args=None): rclpy.shutdown() -if __name__ == '__main__': +if __name__ == "__main__": warnings.warn( "'unspawner.py' is deprecated, please use 'unspawner' (without .py extension)", - DeprecationWarning + DeprecationWarning, ) ret = main() sys.exit(ret) diff --git a/controller_manager/doc/controller_chaining.rst b/controller_manager/doc/controller_chaining.rst index 363eeb70f7..c594e51a7b 100644 --- a/controller_manager/doc/controller_chaining.rst +++ b/controller_manager/doc/controller_chaining.rst @@ -76,7 +76,7 @@ One can also think of it as an actual chain, you can not add a chain link or bre Debugging outputs ---------------------------- -Flag ``unavailable`` if the reference interface does not provide much information about anything at the moment. So don't get confused by it. The reason we have it are internal implementation reasons irelevant for the usage. +Flag ``unavailable`` if the reference interface does not provide much information about anything at the moment. So don't get confused by it. The reason we have it are internal implementation reasons irrelevant for the usage. Closing remarks diff --git a/controller_manager/src/controller_manager.cpp b/controller_manager/src/controller_manager.cpp index 4a73414a66..fdd123bf47 100644 --- a/controller_manager/src/controller_manager.cpp +++ b/controller_manager/src/controller_manager.cpp @@ -337,7 +337,7 @@ void ControllerManager::robot_description_callback(const std_msgs::msg::String & RCLCPP_INFO(get_logger(), "Received robot description file."); RCLCPP_DEBUG( get_logger(), "'Content of robot description file: %s", robot_description.data.c_str()); - // TODO(Manuel): errors should probably be caught since we don't want controller_manager node + // TODO(mamueluth): errors should probably be caught since we don't want controller_manager node // to die if a non valid urdf is passed. However, should maybe be fine tuned. try { diff --git a/controller_manager/test/controller_manager_test_common.hpp b/controller_manager/test/controller_manager_test_common.hpp index cb90fe8dbd..5e6e02029f 100644 --- a/controller_manager/test/controller_manager_test_common.hpp +++ b/controller_manager/test/controller_manager_test_common.hpp @@ -89,7 +89,7 @@ class ControllerManagerFixture : public ::testing::Test } else { - // TODO(Manuel) : passing via topic not working in test setup, tested cm does + // TODO(mamueluth) : passing via topic not working in test setup, tested cm does // not receive msg. Have to check this... // this is just a workaround to skip passing diff --git a/hardware_interface/src/mock_components/generic_system.cpp b/hardware_interface/src/mock_components/generic_system.cpp index 0a88be99cd..3d10f16b5a 100644 --- a/hardware_interface/src/mock_components/generic_system.cpp +++ b/hardware_interface/src/mock_components/generic_system.cpp @@ -144,7 +144,7 @@ CallbackReturn GenericSystem::on_init(const hardware_interface::HardwareInfo & i custom_interface_with_following_offset_ = it->second; } } - // its extremlly unprobably that std::distance results int this value - therefore default + // its extremlly improbably that std::distance results int this value - therefore default index_custom_interface_with_following_offset_ = std::numeric_limits::max(); // Initialize storage for standard interfaces diff --git a/hardware_interface/test/test_handle.cpp b/hardware_interface/test/test_handle.cpp index 16ca710e9d..da8258c643 100644 --- a/hardware_interface/test/test_handle.cpp +++ b/hardware_interface/test/test_handle.cpp @@ -27,9 +27,7 @@ constexpr auto FOO_INTERFACE = "FooInterface"; TEST(TestHandle, command_interface) { double value = 1.337; - CommandInterface interface { - JOINT_NAME, FOO_INTERFACE, &value - }; + CommandInterface interface{JOINT_NAME, FOO_INTERFACE, &value}; EXPECT_DOUBLE_EQ(interface.get_value(), value); EXPECT_NO_THROW(interface.set_value(0.0)); EXPECT_DOUBLE_EQ(interface.get_value(), 0.0); @@ -38,9 +36,7 @@ TEST(TestHandle, command_interface) TEST(TestHandle, state_interface) { double value = 1.337; - StateInterface interface { - JOINT_NAME, FOO_INTERFACE, &value - }; + StateInterface interface{JOINT_NAME, FOO_INTERFACE, &value}; EXPECT_DOUBLE_EQ(interface.get_value(), value); // interface.set_value(5); compiler error, no set_value function } diff --git a/joint_limits/test/joint_limits_rosparam.launch.py b/joint_limits/test/joint_limits_rosparam.launch.py index c808954207..c92f51f863 100644 --- a/joint_limits/test/joint_limits_rosparam.launch.py +++ b/joint_limits/test/joint_limits_rosparam.launch.py @@ -33,9 +33,7 @@ def generate_test_description(): package="joint_limits", executable="joint_limits_rosparam_test", output="screen", - parameters=[ - os.path.join(joint_limits_path, "test", "joint_limits_rosparam.yaml") - ], + parameters=[os.path.join(joint_limits_path, "test", "joint_limits_rosparam.yaml")], ) return ( LaunchDescription( diff --git a/joint_limits_interface/include/joint_limits_interface/joint_limits_interface.hpp b/joint_limits_interface/include/joint_limits_interface/joint_limits_interface.hpp index 9a901c3d94..50dd8e88bb 100644 --- a/joint_limits_interface/include/joint_limits_interface/joint_limits_interface.hpp +++ b/joint_limits_interface/include/joint_limits_interface/joint_limits_interface.hpp @@ -17,14 +17,6 @@ #ifndef JOINT_LIMITS_INTERFACE__JOINT_LIMITS_INTERFACE_HPP_ #define JOINT_LIMITS_INTERFACE__JOINT_LIMITS_INTERFACE_HPP_ -#include -#include - -#include -#include - -#include - #include #include #include @@ -32,6 +24,12 @@ #include #include +#include +#include +#include +#include +#include + #include "joint_limits_interface/joint_limits.hpp" #include "joint_limits_interface/joint_limits_interface_exception.hpp" diff --git a/joint_limits_interface/test/joint_limits_urdf_test.cpp b/joint_limits_interface/test/joint_limits_urdf_test.cpp index 55effc7117..24a943ebc3 100644 --- a/joint_limits_interface/test/joint_limits_urdf_test.cpp +++ b/joint_limits_interface/test/joint_limits_urdf_test.cpp @@ -15,11 +15,10 @@ /// \author Adolfo Rodriguez Tsouroukdissian #include +#include #include -#include - class JointLimitsUrdfTest : public ::testing::Test { public: diff --git a/ros2controlcli/ros2controlcli/api/__init__.py b/ros2controlcli/ros2controlcli/api/__init__.py index 9729b90c7b..df522670ec 100644 --- a/ros2controlcli/ros2controlcli/api/__init__.py +++ b/ros2controlcli/ros2controlcli/api/__init__.py @@ -33,19 +33,19 @@ def service_caller(service_name, service_type, request): cli = node.create_client(service_type, service_name) if not cli.service_is_ready(): - node.get_logger().debug(f'waiting for service {service_name} to become available...') + node.get_logger().debug(f"waiting for service {service_name} to become available...") if not cli.wait_for_service(2.0): - raise RuntimeError(f'Could not contact service {service_name}') + raise RuntimeError(f"Could not contact service {service_name}") - node.get_logger().debug(f'requester: making request: { repr(request) }\n') + node.get_logger().debug(f"requester: making request: { repr(request) }\n") future = cli.call_async(request) rclpy.spin_until_future_complete(node, future) if future.result() is not None: return future.result() else: future_exception = future.exception() - raise RuntimeError(f'Exception while calling service: { repr(future_exception) }') + raise RuntimeError(f"Exception while calling service: { repr(future_exception) }") finally: node.destroy_node() rclpy.shutdown() @@ -59,14 +59,14 @@ def __call__(self, prefix, parsed_args, **kwargs): parameter_names = call_list_parameters( node=node, node_name=parsed_args.controller_manager ) - suffix = '.type' + suffix = ".type" return [n[: -len(suffix)] for n in parameter_names if n.endswith(suffix)] class LoadedControllerNameCompleter: """Callable returning a list of loaded controllers.""" - def __init__(self, valid_states=['active', 'inactive', 'configured', 'unconfigured']): + def __init__(self, valid_states=["active", "inactive", "configured", "unconfigured"]): self.valid_states = valid_states def __call__(self, prefix, parsed_args, **kwargs): @@ -78,13 +78,13 @@ def __call__(self, prefix, parsed_args, **kwargs): def add_controller_mgr_parsers(parser): """Parser arguments to get controller manager node name, defaults to /controller_manager.""" arg = parser.add_argument( - '-c', - '--controller-manager', - help='Name of the controller manager ROS node', - default='/controller_manager', + "-c", + "--controller-manager", + help="Name of the controller manager ROS node", + default="/controller_manager", required=False, ) - arg.completer = NodeNameCompleter(include_hidden_nodes_key='include_hidden_nodes') + arg.completer = NodeNameCompleter(include_hidden_nodes_key="include_hidden_nodes") parser.add_argument( - '--include-hidden-nodes', action='store_true', help='Consider hidden nodes as well' + "--include-hidden-nodes", action="store_true", help="Consider hidden nodes as well" ) diff --git a/ros2controlcli/ros2controlcli/command/control.py b/ros2controlcli/ros2controlcli/command/control.py index 6ca4386709..dad12f2065 100644 --- a/ros2controlcli/ros2controlcli/command/control.py +++ b/ros2controlcli/ros2controlcli/command/control.py @@ -23,15 +23,15 @@ class ControlCommand(CommandExtension): def add_arguments(self, parser, cli_name): self._subparser = parser # get verb extensions and let them add their arguments - add_subparsers_on_demand(parser, cli_name, '_verb', 'ros2controlcli.verb', required=False) + add_subparsers_on_demand(parser, cli_name, "_verb", "ros2controlcli.verb", required=False) def main(self, *, parser, args): - if not hasattr(args, '_verb'): + if not hasattr(args, "_verb"): # in case no verb was passed self._subparser.print_help() return 0 - extension = getattr(args, '_verb') + extension = getattr(args, "_verb") # call the verb's main method return extension.main(args=args) diff --git a/ros2controlcli/ros2controlcli/verb/list_controller_types.py b/ros2controlcli/ros2controlcli/verb/list_controller_types.py index 7b4e778f8f..086b820124 100644 --- a/ros2controlcli/ros2controlcli/verb/list_controller_types.py +++ b/ros2controlcli/ros2controlcli/verb/list_controller_types.py @@ -33,6 +33,6 @@ def main(self, *, args): response = list_controller_types(node, args.controller_manager) types_and_classes = zip(response.types, response.base_classes) for c in types_and_classes: - print(f'{c[0]:70s} {c[1]}') + print(f"{c[0]:70s} {c[1]}") return 0 diff --git a/ros2controlcli/ros2controlcli/verb/list_controllers.py b/ros2controlcli/ros2controlcli/verb/list_controllers.py index 3628ef936c..31ce814865 100644 --- a/ros2controlcli/ros2controlcli/verb/list_controllers.py +++ b/ros2controlcli/ros2controlcli/verb/list_controllers.py @@ -33,26 +33,26 @@ def print_controller_state(c, args): print(f"{c.name:20s}[{c.type:20s}] {state_color}{c.state:10s}{bcolors.ENDC}") if args.claimed_interfaces or args.verbose: - print('\tclaimed interfaces:') + print("\tclaimed interfaces:") for claimed_interface in c.claimed_interfaces: - print(f'\t\t{claimed_interface}') + print(f"\t\t{claimed_interface}") if args.required_command_interfaces or args.verbose: - print('\trequired command interfaces:') + print("\trequired command interfaces:") for required_command_interface in c.required_command_interfaces: - print(f'\t\t{required_command_interface}') + print(f"\t\t{required_command_interface}") if args.required_state_interfaces or args.verbose: - print('\trequired state interfaces:') + print("\trequired state interfaces:") for required_state_interface in c.required_state_interfaces: - print(f'\t\t{required_state_interface}') + print(f"\t\t{required_state_interface}") if args.chained_interfaces or args.verbose: - print('\tchained to interfaces:') + print("\tchained to interfaces:") for connection in c.chain_connections: for reference in connection.reference_interfaces: - print(f'\t\t{reference:20s}') + print(f"\t\t{reference:20s}") if args.reference_interfaces or args.verbose: - print('\texported reference interfaces:') + print("\texported reference interfaces:") for reference_interfaces in c.reference_interfaces: - print(f'\t\t{reference_interfaces}') + print(f"\t\t{reference_interfaces}") class ListControllersVerb(VerbExtension): @@ -61,34 +61,35 @@ class ListControllersVerb(VerbExtension): def add_arguments(self, parser, cli_name): add_arguments(parser) parser.add_argument( - '--claimed-interfaces', - action='store_true', - help='List controller\'s claimed interfaces', + "--claimed-interfaces", + action="store_true", + help="List controller's claimed interfaces", ) parser.add_argument( - '--required-state-interfaces', - action='store_true', - help='List controller\'s required state interfaces', + "--required-state-interfaces", + action="store_true", + help="List controller's required state interfaces", ) parser.add_argument( - '--required-command-interfaces', - action='store_true', - help='List controller\'s required command interfaces', + "--required-command-interfaces", + action="store_true", + help="List controller's required command interfaces", ) parser.add_argument( - '--chained-interfaces', - action='store_true', - help='List interfaces that the controllers are chained to', + "--chained-interfaces", + action="store_true", + help="List interfaces that the controllers are chained to", ) parser.add_argument( - '--reference-interfaces', - action='store_true', - help='List controller\'s exported references', + "--reference-interfaces", + action="store_true", + help="List controller's exported references", ) parser.add_argument( - '--verbose', '-v', - action='store_true', - help='List controller\'s claimed interfaces, required state interfaces and required command interfaces', + "--verbose", + "-v", + action="store_true", + help="List controller's claimed interfaces, required state interfaces and required command interfaces", ) add_controller_mgr_parsers(parser) diff --git a/ros2controlcli/ros2controlcli/verb/list_hardware_components.py b/ros2controlcli/ros2controlcli/verb/list_hardware_components.py index abce8bfb04..9ba3f91280 100644 --- a/ros2controlcli/ros2controlcli/verb/list_hardware_components.py +++ b/ros2controlcli/ros2controlcli/verb/list_hardware_components.py @@ -27,9 +27,10 @@ class ListHardwareComponentsVerb(VerbExtension): def add_arguments(self, parser, cli_name): add_arguments(parser) parser.add_argument( - '--verbose', '-v', - action='store_true', - help='List hardware components with command and state interfaces', + "--verbose", + "-v", + action="store_true", + help="List hardware components with command and state interfaces", ) add_controller_mgr_parsers(parser) @@ -38,39 +39,43 @@ def main(self, *, args): hardware_components = list_hardware_components(node, args.controller_manager) for idx, component in enumerate(hardware_components.component): - print(f'Hardware Component {idx+1}\n\tname: {component.name}\n\ttype: {component.type}') - if hasattr(component, 'plugin_name'): + print( + f"Hardware Component {idx+1}\n\tname: {component.name}\n\ttype: {component.type}" + ) + if hasattr(component, "plugin_name"): plugin_name = component.plugin_name # Keep compatibility to the obsolete filed name in Humble - elif hasattr(component, 'class_type'): + elif hasattr(component, "class_type"): plugin_name = component.class_type else: - plugin_name = f'{bcolors.WARNING}plugin name missing!{bcolors.ENDC}' + plugin_name = f"{bcolors.WARNING}plugin name missing!{bcolors.ENDC}" - print(f'\tplugin name: {plugin_name}\n' - f'\tstate: id={component.state.id} label={component.state.label}\n' - f'\tcommand interfaces') + print( + f"\tplugin name: {plugin_name}\n" + f"\tstate: id={component.state.id} label={component.state.label}\n" + f"\tcommand interfaces" + ) for cmd_interface in component.command_interfaces: if cmd_interface.is_available: - available_str = f'{bcolors.OKBLUE}[available]{bcolors.ENDC}' + available_str = f"{bcolors.OKBLUE}[available]{bcolors.ENDC}" else: - available_str = f'{bcolors.WARNING}[unavailable]{bcolors.ENDC}' + available_str = f"{bcolors.WARNING}[unavailable]{bcolors.ENDC}" if cmd_interface.is_claimed: - claimed_str = f'{bcolors.OKBLUE}[claimed]{bcolors.ENDC}' + claimed_str = f"{bcolors.OKBLUE}[claimed]{bcolors.ENDC}" else: - claimed_str = '[unclaimed]' + claimed_str = "[unclaimed]" - print(f'\t\t{cmd_interface.name} {available_str} {claimed_str}') + print(f"\t\t{cmd_interface.name} {available_str} {claimed_str}") if args.verbose: print("\tstate interfaces") for state_interface in component.state_interfaces: if state_interface.is_available: - available_str = f'{bcolors.OKBLUE}[available]{bcolors.ENDC}' + available_str = f"{bcolors.OKBLUE}[available]{bcolors.ENDC}" else: - available_str = f'{bcolors.WARNING}[unavailable]{bcolors.ENDC}' + available_str = f"{bcolors.WARNING}[unavailable]{bcolors.ENDC}" print(f"\t\t{state_interface.name} {available_str}") diff --git a/ros2controlcli/ros2controlcli/verb/list_hardware_interfaces.py b/ros2controlcli/ros2controlcli/verb/list_hardware_interfaces.py index 305df18e10..7aa850f3bc 100644 --- a/ros2controlcli/ros2controlcli/verb/list_hardware_interfaces.py +++ b/ros2controlcli/ros2controlcli/verb/list_hardware_interfaces.py @@ -37,21 +37,27 @@ def main(self, *, args): state_interfaces = sorted( hardware_interfaces.state_interfaces, key=lambda hwi: hwi.name ) - print('command interfaces') + print("command interfaces") for command_interface in command_interfaces: if command_interface.is_available: if command_interface.is_claimed: - print(f'\t{bcolors.OKBLUE}{command_interface.name} ' - f'[available] [claimed]{bcolors.ENDC}') + print( + f"\t{bcolors.OKBLUE}{command_interface.name} " + f"[available] [claimed]{bcolors.ENDC}" + ) else: - print(f'\t{bcolors.OKCYAN}{command_interface.name} ' - f'[available] [unclaimed]{bcolors.ENDC}') + print( + f"\t{bcolors.OKCYAN}{command_interface.name} " + f"[available] [unclaimed]{bcolors.ENDC}" + ) else: - print(f'\t{bcolors.WARNING}{command_interface.name} ' - f'[unavailable] [unclaimed]{bcolors.ENDC}') + print( + f"\t{bcolors.WARNING}{command_interface.name} " + f"[unavailable] [unclaimed]{bcolors.ENDC}" + ) - print('state interfaces') + print("state interfaces") for state_interface in state_interfaces: - print(f'\t{state_interface.name}') + print(f"\t{state_interface.name}") return 0 diff --git a/ros2controlcli/ros2controlcli/verb/load_controller.py b/ros2controlcli/ros2controlcli/verb/load_controller.py index 357be8f9fd..d52ecf864f 100644 --- a/ros2controlcli/ros2controlcli/verb/load_controller.py +++ b/ros2controlcli/ros2controlcli/verb/load_controller.py @@ -26,12 +26,12 @@ class LoadControllerVerb(VerbExtension): def add_arguments(self, parser, cli_name): add_arguments(parser) - arg = parser.add_argument('controller_name', help='Name of the controller') + arg = parser.add_argument("controller_name", help="Name of the controller") arg.completer = ControllerNameCompleter() arg = parser.add_argument( - '--set-state', - choices=['configured', 'inactive', 'active'], - help='Set the state of the loaded controller', + "--set-state", + choices=["configured", "inactive", "active"], + help="Set the state of the loaded controller", ) add_controller_mgr_parsers(parser) @@ -39,29 +39,31 @@ def main(self, *, args): with NodeStrategy(args) as node: response = load_controller(node, args.controller_manager, args.controller_name) if not response.ok: - return 'Error loading controller, check controller_manager logs' + return "Error loading controller, check controller_manager logs" if not args.set_state: - print(f'Successfully loaded controller {args.controller_name}') + print(f"Successfully loaded controller {args.controller_name}") return 0 # we in any case configure the controller response = configure_controller(node, args.controller_manager, args.controller_name) if not response.ok: - return 'Error configuring controller' + return "Error configuring controller" # TODO(destogl): remove in humble+ - if args.set_state == 'start': + if args.set_state == "start": print('Setting state "start" is deprecated "activate" instead!') - args.set_state == 'activate' + args.set_state == "activate" - if args.set_state == 'active': + if args.set_state == "active": response = switch_controllers( node, args.controller_manager, [], [args.controller_name], True, True, 5.0 ) if not response.ok: - return 'Error activating controller, check controller_manager logs' + return "Error activating controller, check controller_manager logs" - print(f'Sucessfully loaded controller {args.controller_name} into ' - f'state { "active" if args.set_state == "active" else "inactive" }') + print( + f"Successfully loaded controller {args.controller_name} into " + f'state { "active" if args.set_state == "active" else "inactive" }' + ) return 0 diff --git a/ros2controlcli/ros2controlcli/verb/reload_controller_libraries.py b/ros2controlcli/ros2controlcli/verb/reload_controller_libraries.py index 9d56c6d17e..82bc2e480f 100644 --- a/ros2controlcli/ros2controlcli/verb/reload_controller_libraries.py +++ b/ros2controlcli/ros2controlcli/verb/reload_controller_libraries.py @@ -27,7 +27,7 @@ class ReloadControllerLibrariesVerb(VerbExtension): def add_arguments(self, parser, cli_name): add_arguments(parser) parser.add_argument( - '--force-kill', action='store_true', help='Force stop of loaded controllers' + "--force-kill", action="store_true", help="Force stop of loaded controllers" ) add_controller_mgr_parsers(parser) @@ -37,7 +37,7 @@ def main(self, *, args): node, args.controller_manager, force_kill=args.force_kill ) if not response.ok: - return 'Error reloading libraries, check controller_manager logs' + return "Error reloading libraries, check controller_manager logs" - print('Reload successful') + print("Reload successful") return 0 diff --git a/ros2controlcli/ros2controlcli/verb/set_controller_state.py b/ros2controlcli/ros2controlcli/verb/set_controller_state.py index e2a5734680..26b928aaeb 100644 --- a/ros2controlcli/ros2controlcli/verb/set_controller_state.py +++ b/ros2controlcli/ros2controlcli/verb/set_controller_state.py @@ -26,13 +26,13 @@ class SetControllerStateVerb(VerbExtension): def add_arguments(self, parser, cli_name): add_arguments(parser) - arg = parser.add_argument('controller_name', help='Name of the controller to be changed') + arg = parser.add_argument("controller_name", help="Name of the controller to be changed") arg.completer = LoadedControllerNameCompleter() arg = parser.add_argument( - 'state', + "state", # choices=['unconfigured', 'inactive', 'active'], TODO(destogl): when cleanup is impl - choices=['inactive', 'active'], - help='State in which the controller should be changed to', + choices=["inactive", "active"], + help="State in which the controller should be changed to", ) add_controller_mgr_parsers(parser) @@ -43,7 +43,7 @@ def main(self, *, args): try: matched_controller = [c for c in controllers if c.name == args.controller_name][0] except IndexError: - return f'controller {args.controller_name} does not seem to be loaded' + return f"controller {args.controller_name} does not seem to be loaded" # TODO(destogl): This has to be implemented in CLI and controller manager # if args.state == 'unconfigured': @@ -58,52 +58,56 @@ def main(self, *, args): # # print(f'successfully cleaned up {args.controller_name}') # return 0 - if args.state == 'configure': - args.state = 'inactive' + if args.state == "configure": + args.state = "inactive" print('Setting state "configure" is deprecated, use "inactive" instead!') - if args.state == 'stop': - args.state = 'inactive' + if args.state == "stop": + args.state = "inactive" print('Setting state "stop" is deprecated, use "inactive" instead!') - if args.state == 'inactive': - if matched_controller.state == 'unconfigured': + if args.state == "inactive": + if matched_controller.state == "unconfigured": response = configure_controller( node, args.controller_manager, args.controller_name ) if not response.ok: - return 'Error configuring controller, check controller_manager logs' + return "Error configuring controller, check controller_manager logs" - print(f'Successfully configured {args.controller_name}') + print(f"Successfully configured {args.controller_name}") return 0 - elif matched_controller.state == 'active': + elif matched_controller.state == "active": response = switch_controllers( node, args.controller_manager, [args.controller_name], [], True, True, 5.0 ) if not response.ok: - return 'Error stopping controller, check controller_manager logs' + return "Error stopping controller, check controller_manager logs" - print(f'Successfully deactivated {args.controller_name}') + print(f"Successfully deactivated {args.controller_name}") return 0 else: - return f'cannot put {matched_controller.name} in "inactive" state' \ - f'from its current state {matched_controller.state}' + return ( + f'cannot put {matched_controller.name} in "inactive" state' + f"from its current state {matched_controller.state}" + ) - if args.state == 'start': - args.state = 'active' + if args.state == "start": + args.state = "active" print('Setting state "start" is deprecated, use "active" instead!') - if args.state == 'active': - if matched_controller.state != 'inactive': - return f'cannot activate {matched_controller.name} ' \ - f'from its current state {matched_controller.state}' + if args.state == "active": + if matched_controller.state != "inactive": + return ( + f"cannot activate {matched_controller.name} " + f"from its current state {matched_controller.state}" + ) response = switch_controllers( node, args.controller_manager, [], [args.controller_name], True, True, 5.0 ) if not response.ok: - return 'Error activating controller, check controller_manager logs' + return "Error activating controller, check controller_manager logs" - print(f'Successfully activated {args.controller_name}') + print(f"Successfully activated {args.controller_name}") return 0 diff --git a/ros2controlcli/ros2controlcli/verb/switch_controllers.py b/ros2controlcli/ros2controlcli/verb/switch_controllers.py index c6436635bc..141fefd593 100644 --- a/ros2controlcli/ros2controlcli/verb/switch_controllers.py +++ b/ros2controlcli/ros2controlcli/verb/switch_controllers.py @@ -27,53 +27,53 @@ class SwitchControllersVerb(VerbExtension): def add_arguments(self, parser, cli_name): add_arguments(parser) arg = parser.add_argument( - '--stop', - nargs='*', + "--stop", + nargs="*", default=[], - help='Name of the controllers to be deactivated', + help="Name of the controllers to be deactivated", ) - arg.completer = LoadedControllerNameCompleter(['active']) + arg.completer = LoadedControllerNameCompleter(["active"]) arg = parser.add_argument( - '--deactivate', - nargs='*', + "--deactivate", + nargs="*", default=[], - help='Name of the controllers to be deactivated', + help="Name of the controllers to be deactivated", ) - arg.completer = LoadedControllerNameCompleter(['active']) + arg.completer = LoadedControllerNameCompleter(["active"]) arg = parser.add_argument( - '--start', - nargs='*', + "--start", + nargs="*", default=[], - help='Name of the controllers to be activated', + help="Name of the controllers to be activated", ) - arg.completer = LoadedControllerNameCompleter(['inactive']) + arg.completer = LoadedControllerNameCompleter(["inactive"]) arg = parser.add_argument( - '--activate', - nargs='*', + "--activate", + nargs="*", default=[], - help='Name of the controllers to be activated', + help="Name of the controllers to be activated", ) - arg.completer = LoadedControllerNameCompleter(['inactive']) - parser.add_argument('--strict', action='store_true', help='Strict switch') - parser.add_argument('--start-asap', action='store_true', help='Start asap controllers') - parser.add_argument('--activate-asap', action='store_true', help='Start asap controllers') + arg.completer = LoadedControllerNameCompleter(["inactive"]) + parser.add_argument("--strict", action="store_true", help="Strict switch") + parser.add_argument("--start-asap", action="store_true", help="Start asap controllers") + parser.add_argument("--activate-asap", action="store_true", help="Start asap controllers") parser.add_argument( - '--switch-timeout', + "--switch-timeout", default=5.0, required=False, - help='Timeout for switching controllers', + help="Timeout for switching controllers", ) - arg.completer = LoadedControllerNameCompleter(['inactive']) + arg.completer = LoadedControllerNameCompleter(["inactive"]) add_controller_mgr_parsers(parser) def main(self, *, args): - if (args.stop): + if args.stop: print('"--stop" flag is deprecated, use "--deactivate" instead!') args.deactivate = args.stop - if (args.start): + if args.start: print('"--start" flag is deprecated, use "--activate" instead!') args.activate = args.start - if (args.start_asap): + if args.start_asap: print('"--start-asap" flag is deprecated, use "--activate-asap" instead!') args.activate_asap = args.start_asap @@ -88,7 +88,7 @@ def main(self, *, args): args.switch_timeout, ) if not response.ok: - return 'Error switching controllers, check controller_manager logs' + return "Error switching controllers, check controller_manager logs" - print('Successfully switched controllers') + print("Successfully switched controllers") return 0 diff --git a/ros2controlcli/ros2controlcli/verb/unload_controller.py b/ros2controlcli/ros2controlcli/verb/unload_controller.py index cf24d4847b..81612eb3ad 100644 --- a/ros2controlcli/ros2controlcli/verb/unload_controller.py +++ b/ros2controlcli/ros2controlcli/verb/unload_controller.py @@ -26,7 +26,7 @@ class UnloadControllerVerb(VerbExtension): def add_arguments(self, parser, cli_name): add_arguments(parser) - arg = parser.add_argument('controller_name', help='Name of the controller') + arg = parser.add_argument("controller_name", help="Name of the controller") arg.completer = LoadedControllerNameCompleter() add_controller_mgr_parsers(parser) @@ -34,7 +34,7 @@ def main(self, *, args): with NodeStrategy(args) as node: response = unload_controller(node, args.controller_manager, args.controller_name) if not response.ok: - return 'Error unloading controllers, check controller_manager logs' + return "Error unloading controllers, check controller_manager logs" - print(f'Successfully unloaded controller {args.controller_name}') + print(f"Successfully unloaded controller {args.controller_name}") return 0 diff --git a/ros2controlcli/ros2controlcli/verb/view_controller_chains.py b/ros2controlcli/ros2controlcli/verb/view_controller_chains.py index e175cf33fe..f838a96e1c 100644 --- a/ros2controlcli/ros2controlcli/verb/view_controller_chains.py +++ b/ros2controlcli/ros2controlcli/verb/view_controller_chains.py @@ -24,70 +24,98 @@ import pygraphviz as pgz -def make_controller_node(s, controller_name, state_interfaces, command_interfaces, input_controllers, - output_controllers, port_map): +def make_controller_node( + s, + controller_name, + state_interfaces, + command_interfaces, + input_controllers, + output_controllers, + port_map, +): state_interfaces = sorted(list(state_interfaces)) command_interfaces = sorted(list(command_interfaces)) input_controllers = sorted(list(input_controllers)) output_controllers = sorted(list(output_controllers)) - inputs_str = '' + inputs_str = "" for ind, state_interface in enumerate(state_interfaces): - deliminator = '|' + deliminator = "|" if ind == len(state_interface) - 1: - deliminator = '' - inputs_str += '<{}> {} {} '.format("state_end_" + state_interface, state_interface, deliminator) + deliminator = "" + inputs_str += "<{}> {} {} ".format( + "state_end_" + state_interface, state_interface, deliminator + ) for ind, input_controller in enumerate(input_controllers): - deliminator = '|' + deliminator = "|" if ind == len(input_controller) - 1: - deliminator = '' - inputs_str += '<{}> {} {} '.format("controller_end_" + input_controller, input_controller, deliminator) + deliminator = "" + inputs_str += "<{}> {} {} ".format( + "controller_end_" + input_controller, input_controller, deliminator + ) port_map["controller_end_" + input_controller] = controller_name - outputs_str = '' + outputs_str = "" for ind, command_interface in enumerate(command_interfaces): - deliminator = '|' + deliminator = "|" if ind == len(command_interface) - 1: - deliminator = '' - outputs_str += '<{}> {} {} '.format("command_start_" + command_interface, command_interface, deliminator) + deliminator = "" + outputs_str += "<{}> {} {} ".format( + "command_start_" + command_interface, command_interface, deliminator + ) for ind, output_controller in enumerate(output_controllers): - deliminator = '|' + deliminator = "|" if ind == len(output_controller) - 1: - deliminator = '' - outputs_str += '<{}> {} {} '.format("controller_start_" + output_controller, output_controller, deliminator) + deliminator = "" + outputs_str += "<{}> {} {} ".format( + "controller_start_" + output_controller, output_controller, deliminator + ) - s.add_node(controller_name, label=f'{controller_name}|{{{{{inputs_str}}}|{{{outputs_str}}}}}') + s.add_node(controller_name, label=f"{controller_name}|{{{{{inputs_str}}}|{{{outputs_str}}}}}") def make_command_node(s, command_interfaces): command_interfaces = sorted(list(command_interfaces)) - outputs_str = '' + outputs_str = "" for ind, command_interface in enumerate(command_interfaces): - deliminator = '|' + deliminator = "|" if ind == len(command_interfaces) - 1: - deliminator = '' - outputs_str += '<{}> {} {} '.format("command_end_" + command_interface, command_interface, deliminator) + deliminator = "" + outputs_str += "<{}> {} {} ".format( + "command_end_" + command_interface, command_interface, deliminator + ) - s.add_node("command_interfaces", label='{}|{{{{{}}}}}'.format("command_interfaces", outputs_str)) + s.add_node( + "command_interfaces", label="{}|{{{{{}}}}}".format("command_interfaces", outputs_str) + ) def make_state_node(s, state_interfaces): state_interfaces = sorted(list(state_interfaces)) - inputs_str = '' + inputs_str = "" for ind, state_interface in enumerate(state_interfaces): - deliminator = '|' + deliminator = "|" if ind == len(state_interfaces) - 1: - deliminator = '' - inputs_str += '<{}> {} {} '.format("state_start_" + state_interface, state_interface, deliminator) - - s.add_node("state_interfaces", label='{}|{{{{{}}}}}'.format("state_interfaces", inputs_str)) - - -def show_graph(input_chain_connections, output_chain_connections, command_connections, state_connections, - command_interfaces, state_interfaces, visualize): - s = pgz.AGraph(name='g', strict=False, directed=True, rankdir='LR') + deliminator = "" + inputs_str += "<{}> {} {} ".format( + "state_start_" + state_interface, state_interface, deliminator + ) + + s.add_node("state_interfaces", label="{}|{{{{{}}}}}".format("state_interfaces", inputs_str)) + + +def show_graph( + input_chain_connections, + output_chain_connections, + command_connections, + state_connections, + command_interfaces, + state_interfaces, + visualize, +): + s = pgz.AGraph(name="g", strict=False, directed=True, rankdir="LR") s.node_attr["shape"] = "record" s.node_attr["style"] = "rounded" port_map = dict() @@ -99,29 +127,42 @@ def show_graph(input_chain_connections, output_chain_connections, command_connec controller_names = controller_names.union({name for name in state_connections}) # create node for each controller for controller_name in controller_names: - make_controller_node(s, controller_name, state_connections[controller_name], - command_connections[controller_name], - input_chain_connections[controller_name], - output_chain_connections[controller_name], port_map) + make_controller_node( + s, + controller_name, + state_connections[controller_name], + command_connections[controller_name], + input_chain_connections[controller_name], + output_chain_connections[controller_name], + port_map, + ) make_state_node(s, state_interfaces) make_command_node(s, command_interfaces) for controller_name in controller_names: for connection in output_chain_connections[controller_name]: - s.add_edge('{}:{}'.format(controller_name, "controller_start_" + connection), - '{}:{}'.format(port_map['controller_end_' + connection], 'controller_end_' + connection)) + s.add_edge( + "{}:{}".format(controller_name, "controller_start_" + connection), + "{}:{}".format( + port_map["controller_end_" + connection], "controller_end_" + connection + ), + ) for state_connection in state_connections[controller_name]: - s.add_edge('{}:{}'.format("state_interfaces", "state_start_" + state_connection), - '{}:{}'.format(controller_name, 'state_end_' + state_connection)) + s.add_edge( + "{}:{}".format("state_interfaces", "state_start_" + state_connection), + "{}:{}".format(controller_name, "state_end_" + state_connection), + ) for command_connection in command_connections[controller_name]: - s.add_edge('{}:{}'.format(controller_name, "command_start_" + command_connection), - '{}:{}'.format("command_interfaces", 'command_end_' + command_connection)) + s.add_edge( + "{}:{}".format(controller_name, "command_start_" + command_connection), + "{}:{}".format("command_interfaces", "command_end_" + command_connection), + ) - s.graph_attr.update(ranksep='2') - s.layout(prog='dot') + s.graph_attr.update(ranksep="2") + s.layout(prog="dot") if visualize: - s.draw('/tmp/controller_diagram.gv.pdf', format='pdf') + s.draw("/tmp/controller_diagram.gv.pdf", format="pdf") def parse_response(list_controllers_response, list_hardware_response, visualize=True): @@ -142,8 +183,15 @@ def parse_response(list_controllers_response, list_hardware_response, visualize= command_connections[controller.name] = set(controller.required_command_interfaces) state_connections[controller.name] = set(controller.required_state_interfaces) - show_graph(input_chain_connections, output_chain_connections, command_connections, state_connections, - command_interfaces, state_interfaces, visualize) + show_graph( + input_chain_connections, + output_chain_connections, + command_connections, + state_connections, + command_interfaces, + state_interfaces, + visualize, + ) class ViewControllerChainsVerb(VerbExtension): diff --git a/ros2controlcli/setup.py b/ros2controlcli/setup.py index c8c34db980..006d581890 100644 --- a/ros2controlcli/setup.py +++ b/ros2controlcli/setup.py @@ -15,55 +15,55 @@ from setuptools import find_packages from setuptools import setup -package_name = 'ros2controlcli' +package_name = "ros2controlcli" setup( name=package_name, - version='2.39.1', - packages=find_packages(exclude=['test']), + version="2.39.1", + packages=find_packages(exclude=["test"]), data_files=[ - ('share/' + package_name, ['package.xml']), - ('share/ament_index/resource_index/packages', ['resource/' + package_name]), + ("share/" + package_name, ["package.xml"]), + ("share/ament_index/resource_index/packages", ["resource/" + package_name]), ], - install_requires=['ros2cli'], + install_requires=["ros2cli"], zip_safe=True, - author='Victor Lopez', - author_email='victor.lopez@pal-robotics.com', - maintainer='Victor Lopez', - maintainer_email='victor.lopez@pal-robotics.com', - url='https://github.com/ros-controls/ros2_control', + author="Victor Lopez", + author_email="victor.lopez@pal-robotics.com", + maintainer="Victor Lopez", + maintainer_email="victor.lopez@pal-robotics.com", + url="https://github.com/ros-controls/ros2_control", keywords=[], classifiers=[ - 'Environment :: Console', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python', + "Environment :: Console", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", ], - description='ROS2 Control command interface.', + description="ROS2 Control command interface.", long_description="""\ ROS2 Control command interface.""", - license='Apache License, Version 2.0', - tests_require=['pytest'], + license="Apache License, Version 2.0", + tests_require=["pytest"], entry_points={ - 'ros2cli.command': [ - 'control = ros2controlcli.command.control:ControlCommand', + "ros2cli.command": [ + "control = ros2controlcli.command.control:ControlCommand", ], - 'ros2controlcli.verb': [ - 'list_controllers = ros2controlcli.verb.list_controllers:ListControllersVerb', - 'view_controller_chains = ros2controlcli.verb.view_controller_chains:ViewControllerChainsVerb', - 'list_hardware_components = \ - ros2controlcli.verb.list_hardware_components:ListHardwareComponentsVerb', - 'list_hardware_interfaces = \ - ros2controlcli.verb.list_hardware_interfaces:ListHardwareInterfacesVerb', - 'list_controller_types = \ - ros2controlcli.verb.list_controller_types:ListControllerTypesVerb', - 'load_controller = ros2controlcli.verb.load_controller:LoadControllerVerb', - 'reload_controller_libraries = \ - ros2controlcli.verb.reload_controller_libraries:ReloadControllerLibrariesVerb', - 'set_controller_state = \ - ros2controlcli.verb.set_controller_state:SetControllerStateVerb', - 'switch_controllers = ros2controlcli.verb.switch_controllers:SwitchControllersVerb', - 'unload_controller = ros2controlcli.verb.unload_controller:UnloadControllerVerb', + "ros2controlcli.verb": [ + "list_controllers = ros2controlcli.verb.list_controllers:ListControllersVerb", + "view_controller_chains = ros2controlcli.verb.view_controller_chains:ViewControllerChainsVerb", + "list_hardware_components = \ + ros2controlcli.verb.list_hardware_components:ListHardwareComponentsVerb", + "list_hardware_interfaces = \ + ros2controlcli.verb.list_hardware_interfaces:ListHardwareInterfacesVerb", + "list_controller_types = \ + ros2controlcli.verb.list_controller_types:ListControllerTypesVerb", + "load_controller = ros2controlcli.verb.load_controller:LoadControllerVerb", + "reload_controller_libraries = \ + ros2controlcli.verb.reload_controller_libraries:ReloadControllerLibrariesVerb", + "set_controller_state = \ + ros2controlcli.verb.set_controller_state:SetControllerStateVerb", + "switch_controllers = ros2controlcli.verb.switch_controllers:SwitchControllersVerb", + "unload_controller = ros2controlcli.verb.unload_controller:UnloadControllerVerb", ], }, ) diff --git a/ros2controlcli/test/test_view_controller_chains.py b/ros2controlcli/test/test_view_controller_chains.py index 3ac37198a5..62e1bf5ac7 100644 --- a/ros2controlcli/test/test_view_controller_chains.py +++ b/ros2controlcli/test/test_view_controller_chains.py @@ -62,9 +62,13 @@ def test_expected(self): chain_connection.reference_interfaces.append(f"joint{i}/position") chain_connection.reference_interfaces.append(f"joint{i}/velocity") - chained_to_controller.required_command_interfaces = chained_to_controller.claimed_interfaces + chained_to_controller.required_command_interfaces = ( + chained_to_controller.claimed_interfaces + ) - chained_from_controller.required_command_interfaces = chained_from_controller.claimed_interfaces + chained_from_controller.required_command_interfaces = ( + chained_from_controller.claimed_interfaces + ) chained_from_controller.chain_connections.append(chain_connection) controller_list = [chained_from_controller, chained_to_controller] diff --git a/rqt_controller_manager/rqt_controller_manager/controller_manager.py b/rqt_controller_manager/rqt_controller_manager/controller_manager.py index 54a3dc5c77..d142ea847b 100644 --- a/rqt_controller_manager/rqt_controller_manager/controller_manager.py +++ b/rqt_controller_manager/rqt_controller_manager/controller_manager.py @@ -17,8 +17,13 @@ import os from ament_index_python.packages import get_package_share_directory -from controller_manager.controller_manager_services import configure_controller, \ - list_controllers, load_controller, switch_controllers, unload_controller +from controller_manager.controller_manager_services import ( + configure_controller, + list_controllers, + load_controller, + switch_controllers, + unload_controller, +) from controller_manager_msgs.msg import ControllerState from controller_manager_msgs.srv import SwitchController from python_qt_binding import loadUi @@ -39,7 +44,7 @@ class ControllerManager(Plugin): def __init__(self, context): super().__init__(context) - self.setObjectName('ControllerManager') + self.setObjectName("ControllerManager") # Create QWidget and extend it with all the attributes and children # from the UI file @@ -47,18 +52,18 @@ def __init__(self, context): ui_file = os.path.join( get_package_share_directory("rqt_controller_manager"), "resource", - "controller_manager.ui") + "controller_manager.ui", + ) loadUi(ui_file, self._widget) - self._widget.setObjectName('ControllerManagerUi') + self._widget.setObjectName("ControllerManagerUi") # Pop-up that displays controller information self._popup_widget = QWidget() ui_file = os.path.join( - get_package_share_directory("rqt_controller_manager"), - 'resource', - 'controller_info.ui') + get_package_share_directory("rqt_controller_manager"), "resource", "controller_info.ui" + ) loadUi(ui_file, self._popup_widget) - self._popup_widget.setObjectName('ControllerInfoUi') + self._popup_widget.setObjectName("ControllerInfoUi") # Show _widget.windowTitle on left-top of each plugin (when # it's set in _widget). This is useful when you open multiple @@ -66,12 +71,12 @@ def __init__(self, context): # plugin at once, these lines add number to make it easy to # tell from pane to pane. if context.serial_number() > 1: - self._widget.setWindowTitle(f'{self._widget.windowTitle()} {context.serial_number()}') + self._widget.setWindowTitle(f"{self._widget.windowTitle()} {context.serial_number()}") # Add widget to the user interface context.add_widget(self._widget) # Initialize members - self._cm_name = '' # Name of the selected controller manager's node + self._cm_name = "" # Name of the selected controller manager's node self._controllers = [] # State of each controller self._table_model = None @@ -81,10 +86,10 @@ def __init__(self, context): # Controller state icons path = get_package_share_directory("rqt_controller_manager") self._icons = { - 'active': QIcon(f'{path}/resource/led_green.png'), - 'finalized': QIcon(f'{path}/resource/led_off.png'), - 'inactive': QIcon(f'{path}/resource/led_red.png'), - 'unconfigured': QIcon(f'{path}/resource/led_off.png'), + "active": QIcon(f"{path}/resource/led_green.png"), + "finalized": QIcon(f"{path}/resource/led_off.png"), + "inactive": QIcon(f"{path}/resource/led_red.png"), + "unconfigured": QIcon(f"{path}/resource/led_off.png"), } # Controllers display @@ -121,18 +126,18 @@ def shutdown_plugin(self): self._popup_widget.hide() def save_settings(self, plugin_settings, instance_settings): - instance_settings.set_value('cm_name', self._cm_name) + instance_settings.set_value("cm_name", self._cm_name) def restore_settings(self, plugin_settings, instance_settings): # Restore last session's controller_manager, if present self._update_cm_list() - cm_name = instance_settings.value('cm_name') + cm_name = instance_settings.value("cm_name") cm_combo = self._widget.cm_combo cm_list = [cm_combo.itemText(i) for i in range(cm_combo.count())] try: idx = cm_list.index(cm_name) cm_combo.setCurrentIndex(idx) - except (ValueError): + except ValueError: pass def _update_cm_list(self): @@ -200,39 +205,36 @@ def _on_ctrl_menu(self, pos): # Show context menu menu = QMenu(self._widget.table_view) - if ctrl.state == 'active': - action_deactivate = menu.addAction(self._icons['inactive'], 'Deactivate') - action_kill = menu.addAction(self._icons['finalized'], - 'Deactivate and Unload') - elif ctrl.state == 'inactive': - action_activate = menu.addAction(self._icons['active'], 'Activate') - action_unload = menu.addAction(self._icons['unconfigured'], - 'Unload') - elif ctrl.state == 'unconfigured': - action_configure = menu.addAction(self._icons['inactive'], 'Configure') - action_spawn = menu.addAction(self._icons['active'], - 'Configure and Activate') + if ctrl.state == "active": + action_deactivate = menu.addAction(self._icons["inactive"], "Deactivate") + action_kill = menu.addAction(self._icons["finalized"], "Deactivate and Unload") + elif ctrl.state == "inactive": + action_activate = menu.addAction(self._icons["active"], "Activate") + action_unload = menu.addAction(self._icons["unconfigured"], "Unload") + elif ctrl.state == "unconfigured": + action_configure = menu.addAction(self._icons["inactive"], "Configure") + action_spawn = menu.addAction(self._icons["active"], "Configure and Activate") else: # Controller isn't loaded - action_load = menu.addAction(self._icons['unconfigured'], 'Load') - action_configure = menu.addAction(self._icons['inactive'], 'Load and Configure') - action_activate = menu.addAction(self._icons['active'], 'Load, Configure and Activate') + action_load = menu.addAction(self._icons["unconfigured"], "Load") + action_configure = menu.addAction(self._icons["inactive"], "Load and Configure") + action_activate = menu.addAction(self._icons["active"], "Load, Configure and Activate") action = menu.exec_(self._widget.table_view.mapToGlobal(pos)) # Evaluate user action - if ctrl.state == 'active': + if ctrl.state == "active": if action is action_deactivate: self._deactivate_controller(ctrl.name) elif action is action_kill: self._deactivate_controller(ctrl.name) unload_controller(self._node, self._cm_name, ctrl.name) - elif ctrl.state in ('finalized', 'inactive'): + elif ctrl.state in ("finalized", "inactive"): if action is action_activate: self._activate_controller(ctrl.name) elif action is action_unload: unload_controller(self._node, self._cm_name, ctrl.name) - elif ctrl.state == 'unconfigured': + elif ctrl.state == "unconfigured": if action is action_configure: configure_controller(self._node, self._cm_name, ctrl.name) elif action is action_spawn: @@ -258,7 +260,7 @@ def _on_ctrl_info(self, index): popup.ctrl_type.setText(ctrl.type) res_model = QStandardItemModel() - model_root = QStandardItem('Claimed Interfaces') + model_root = QStandardItem("Claimed Interfaces") res_model.appendRow(model_root) for claimed_interface in ctrl.claimed_interfaces: hw_iface_item = QStandardItem(claimed_interface) @@ -275,7 +277,7 @@ def _on_header_menu(self, pos): # Show context menu menu = QMenu(self._widget.table_view) - action_toggle_auto_resize = menu.addAction('Toggle Auto-Resize') + action_toggle_auto_resize = menu.addAction("Toggle Auto-Resize") action = menu.exec_(header.mapToGlobal(pos)) # Evaluate user action @@ -293,7 +295,7 @@ def _activate_controller(self, name): activate_controllers=[name], strict=SwitchController.Request.STRICT, activate_asap=False, - timeout=0.3 + timeout=0.3, ) def _deactivate_controller(self, name): @@ -304,7 +306,7 @@ def _deactivate_controller(self, name): activate_controllers=[], strict=SwitchController.Request.STRICT, activate_asap=False, - timeout=0.3 + timeout=0.3, ) @@ -316,7 +318,7 @@ class ControllerTable(QAbstractTableModel): name and state. """ - def __init__(self, controller_info, icons, parent=None): + def __init__(self, controller_info, icons, parent=None): QAbstractTableModel.__init__(self, parent) self._data = controller_info self._icons = icons @@ -331,9 +333,9 @@ def headerData(self, col, orientation, role): if orientation != Qt.Horizontal or role != Qt.DisplayRole: return None if col == 0: - return 'controller' + return "controller" elif col == 1: - return 'state' + return "state" def data(self, index, role): if not index.isValid(): @@ -345,7 +347,7 @@ def data(self, index, role): if index.column() == 0: return ctrl.name elif index.column() == 1: - return ctrl.state or 'not loaded' + return ctrl.state or "not loaded" if role == Qt.DecorationRole and index.column() == 0: return self._icons.get(ctrl.state) @@ -390,7 +392,7 @@ def _get_controller_type(node, node_name, ctrl_name): @rtype str """ response = call_get_parameters(node=node, node_name=node_name, parameter_names=[ctrl_name]) - return response.values[0].string_value if response.values else '' + return response.values[0].string_value if response.values else "" def _list_controller_managers(node): @@ -403,14 +405,14 @@ def _list_controller_managers(node): @rtype list of str """ return [ - name.rstrip('list_controllers').rstrip('/') + name.rstrip("list_controllers").rstrip("/") for name, _ in get_service_names_and_types(node=node) - if name.endswith('list_controllers') + if name.endswith("list_controllers") ] def _get_parameter_controller_names(node, node_name): """Get list of ROS parameter names that potentially represent a controller configuration.""" parameter_names = call_list_parameters(node=node, node_name=node_name) - suffix = '.type' + suffix = ".type" return [n[: -len(suffix)] for n in parameter_names if n.endswith(suffix)] diff --git a/rqt_controller_manager/rqt_controller_manager/main.py b/rqt_controller_manager/rqt_controller_manager/main.py index 4dd77d350b..14de0aea54 100644 --- a/rqt_controller_manager/rqt_controller_manager/main.py +++ b/rqt_controller_manager/rqt_controller_manager/main.py @@ -20,8 +20,8 @@ def main(): main = Main() - sys.exit(main.main(sys.argv, standalone='rqt_controller_manager')) + sys.exit(main.main(sys.argv, standalone="rqt_controller_manager")) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/rqt_controller_manager/rqt_controller_manager/update_combo.py b/rqt_controller_manager/rqt_controller_manager/update_combo.py index a838f28112..4a61736aa3 100644 --- a/rqt_controller_manager/rqt_controller_manager/update_combo.py +++ b/rqt_controller_manager/rqt_controller_manager/update_combo.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. + def update_combo(combo, new_vals): """ Update the contents of a combo box with a set of new values. @@ -33,7 +34,7 @@ def update_combo(combo, new_vals): selected_id = -1 try: selected_id = new_vals.index(selected_val) - except (ValueError): + except ValueError: combo.setCurrentIndex(-1) # Re-populate items diff --git a/rqt_controller_manager/setup.py b/rqt_controller_manager/setup.py index 60fd362aca..7481d9bea4 100644 --- a/rqt_controller_manager/setup.py +++ b/rqt_controller_manager/setup.py @@ -1,28 +1,42 @@ +# Copyright 2024 Apache License, Version 2.0 +# +# 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 glob import glob from setuptools import setup -package_name = 'rqt_controller_manager' +package_name = "rqt_controller_manager" setup( name=package_name, - version='2.39.1', + version="2.39.1", packages=[package_name], data_files=[ - ('share/ament_index/resource_index/packages', ['resource/' + package_name]), - ('share/' + package_name, ['package.xml']), + ("share/ament_index/resource_index/packages", ["resource/" + package_name]), + ("share/" + package_name, ["package.xml"]), ("share/" + package_name + "/resource", glob("resource/*.*")), ("share/" + package_name, ["plugin.xml"]), ], - install_requires=['setuptools'], + install_requires=["setuptools"], zip_safe=True, - maintainer='Bence Magyar', - maintainer_email='bence.magyar.robotics@gmail.com', - description='Graphical frontend for interacting with the controller manager.', - license='Apache License, Version 2.0', - tests_require=['pytest'], + maintainer="Bence Magyar", + maintainer_email="bence.magyar.robotics@gmail.com", + description="Graphical frontend for interacting with the controller manager.", + license="Apache License, Version 2.0", + tests_require=["pytest"], entry_points={ - 'console_scripts': [ + "console_scripts": [ "rqt_controller_manager = \ rqt_controller_manager.main:main", ],