diff --git a/.github/workflows/README.md b/.github/workflows/README.md index ed64bcd94c..62007ffc2d 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -2,5 +2,6 @@ ROS2 Distro | Branch | Build status | Documentation | Released packages :---------: | :----: | :----------: | :-----------: | :---------------: **Rolling** | [`master`](https://github.com/ros-controls/ros2_control/tree/master) | [![Rolling Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-binary-build.yml?branch=master)
[![Rolling Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-semi-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-semi-binary-build.yml?branch=master)
[![Rolling Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-source-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-source-build.yml?branch=master)
[![Debian Rolling Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-debian-build.yml/badge.svg)](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-debian-build.yml)
[![RHEL Rolling Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-rhel-binary-build.yml/badge.svg)](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-rhel-binary-build.yml) | [Documentation](https://control.ros.org/master/index.html)
[API Reference](https://control.ros.org/master/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#rolling) +**Jazzy** | [`master`](https://github.com/ros-controls/ros2_control/tree/master) | [![Jazzy Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-binary-build.yml?branch=master)
[![Jazzy Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-semi-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-semi-binary-build.yml?branch=master)
[![Jazzy Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-source-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-source-build.yml?branch=master)
[![Debian Jazzy Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-debian-build.yml/badge.svg)](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-debian-build.yml)
[![RHEL Jazzy Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-rhel-binary-build.yml/badge.svg)](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-rhel-binary-build.yml) | [Documentation](https://control.ros.org/jazzy/index.html)
[API Reference](https://control.ros.org/jazzy/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#jazzy) **Iron** | [`iron`](https://github.com/ros-controls/ros2_control/tree/master) | [![Iron Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/iron-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/iron-binary-build.yml?branch=master)
[![Iron Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/iron-semi-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/iron-semi-binary-build.yml?branch=master)
[![Iron Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/iron-source-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/iron-source-build.yml?branch=master)
[![Debian Iron Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/iron-debian-build.yml/badge.svg)](https://github.com/ros-controls/ros2_control/actions/workflows/iron-debian-build.yml)
[![RHEL Iron Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/iron-rhel-binary-build.yml/badge.svg)](https://github.com/ros-controls/ros2_control/actions/workflows/iron-rhel-binary-build.yml) | [Documentation](https://control.ros.org/iron/index.html)
[API Reference](https://control.ros.org/iron/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#iron) **Humble** | [`humble`](https://github.com/ros-controls/ros2_control/tree/humble) | [![Humble Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/humble-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/humble-binary-build.yml?branch=master)
[![Humble Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/humble-semi-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/humble-semi-binary-build.yml?branch=master)
[![Humble Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/humble-source-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/humble-source-build.yml?branch=master)
[![Debian Humble Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/humble-debian-build.yml/badge.svg)](https://github.com/ros-controls/ros2_control/actions/workflows/humble-debian-build.yml)
[![RHEL Humble Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/humble-rhel-binary-build.yml/badge.svg)](https://github.com/ros-controls/ros2_control/actions/workflows/humble-rhel-binary-build.yml) | [Documentation](https://control.ros.org/humble/index.html)
[API Reference](https://control.ros.org/humble/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#humble) diff --git a/.github/workflows/humble-abi-compatibility.yml b/.github/workflows/humble-abi-compatibility.yml index 5c288fabfb..8b1950babc 100644 --- a/.github/workflows/humble-abi-compatibility.yml +++ b/.github/workflows/humble-abi-compatibility.yml @@ -4,6 +4,14 @@ on: pull_request: branches: - humble + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/humble-abi-compatibility.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control-not-released.humble.repos' jobs: abi_check: diff --git a/.github/workflows/humble-binary-build.yml b/.github/workflows/humble-binary-build.yml index 2cf14105f5..18dfae9666 100644 --- a/.github/workflows/humble-binary-build.yml +++ b/.github/workflows/humble-binary-build.yml @@ -7,9 +7,25 @@ on: pull_request: branches: - humble + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/humble-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control-not-released.humble.repos' push: branches: - humble + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/humble-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control-not-released.humble.repos' schedule: # Run every morning to detect flakiness and broken dependencies - cron: '03 1 * * *' diff --git a/.github/workflows/humble-check-docs.yml b/.github/workflows/humble-check-docs.yml new file mode 100644 index 0000000000..f3c31703cd --- /dev/null +++ b/.github/workflows/humble-check-docs.yml @@ -0,0 +1,18 @@ +name: Humble Check Docs + +on: + workflow_dispatch: + pull_request: + branches: + - humble + paths: + - '**.rst' + - '**.md' + - '**.yaml' + +jobs: + check-docs: + name: Check Docs + uses: ros-controls/control.ros.org/.github/workflows/reusable-sphinx-check-single-version.yml@humble + with: + ROS2_CONTROL_PR: ${{ github.ref }} diff --git a/.github/workflows/humble-coverage-build.yml b/.github/workflows/humble-coverage-build.yml index 0910572227..d699e99278 100644 --- a/.github/workflows/humble-coverage-build.yml +++ b/.github/workflows/humble-coverage-build.yml @@ -4,9 +4,27 @@ on: push: branches: - humble + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/humble-coverage-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.humble.repos' + - 'codecov.yml' pull_request: branches: - humble + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/humble-coverage-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.humble.repos' + - 'codecov.yml' jobs: coverage_humble: @@ -14,4 +32,3 @@ jobs: secrets: inherit with: ros_distro: humble - os_name: ubuntu-22.04 diff --git a/.github/workflows/humble-debian-build.yml b/.github/workflows/humble-debian-build.yml index 3b8a1c6287..5f0c33fa5e 100644 --- a/.github/workflows/humble-debian-build.yml +++ b/.github/workflows/humble-debian-build.yml @@ -4,6 +4,14 @@ on: pull_request: branches: - humble + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/humble-debian-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.humble.repos' schedule: # Run every day to detect flakiness and broken dependencies - cron: '03 1 * * *' diff --git a/.github/workflows/humble-pre-commit.yml b/.github/workflows/humble-pre-commit.yml index be8c84b05b..5bb2408578 100644 --- a/.github/workflows/humble-pre-commit.yml +++ b/.github/workflows/humble-pre-commit.yml @@ -11,4 +11,3 @@ jobs: 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/humble-rhel-binary-build.yml b/.github/workflows/humble-rhel-binary-build.yml index 4c00d2f2ad..dacaf8fee5 100644 --- a/.github/workflows/humble-rhel-binary-build.yml +++ b/.github/workflows/humble-rhel-binary-build.yml @@ -4,6 +4,14 @@ on: pull_request: branches: - humble + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/humble-rhel-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.humble.repos' schedule: # Run every day to detect flakiness and broken dependencies - cron: '03 1 * * *' diff --git a/.github/workflows/humble-semi-binary-build.yml b/.github/workflows/humble-semi-binary-build.yml index 19637c4897..ec994cf7ff 100644 --- a/.github/workflows/humble-semi-binary-build.yml +++ b/.github/workflows/humble-semi-binary-build.yml @@ -7,9 +7,25 @@ on: pull_request: branches: - humble + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/humble-semi-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.humble.repos' push: branches: - humble + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/humble-semi-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.humble.repos' schedule: # Run every morning to detect flakiness and broken dependencies - cron: '03 1 * * *' diff --git a/.github/workflows/humble-source-build.yml b/.github/workflows/humble-source-build.yml index 6325fd50d8..5f437f8b0d 100644 --- a/.github/workflows/humble-source-build.yml +++ b/.github/workflows/humble-source-build.yml @@ -4,6 +4,14 @@ on: push: branches: - humble + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/humble-source-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.humble.repos' schedule: # Run every day to detect flakiness and broken dependencies - cron: '03 3 * * *' diff --git a/.github/workflows/iron-abi-compatibility.yml b/.github/workflows/iron-abi-compatibility.yml index ab6642625f..d9476298a2 100644 --- a/.github/workflows/iron-abi-compatibility.yml +++ b/.github/workflows/iron-abi-compatibility.yml @@ -4,6 +4,14 @@ on: pull_request: branches: - iron + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/iron-abi-compatibility.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control-not-released.iron.repos' jobs: abi_check: diff --git a/.github/workflows/iron-binary-build.yml b/.github/workflows/iron-binary-build.yml index 911ccafae5..0fb0adcb1b 100644 --- a/.github/workflows/iron-binary-build.yml +++ b/.github/workflows/iron-binary-build.yml @@ -7,9 +7,25 @@ on: pull_request: branches: - iron + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/iron-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control-not-released.iron.repos' push: branches: - iron + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/iron-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control-not-released.iron.repos' schedule: # Run every morning to detect flakiness and broken dependencies - cron: '03 1 * * *' diff --git a/.github/workflows/iron-check-docs.yml b/.github/workflows/iron-check-docs.yml new file mode 100644 index 0000000000..e9295dad44 --- /dev/null +++ b/.github/workflows/iron-check-docs.yml @@ -0,0 +1,18 @@ +name: Iron Check Docs + +on: + workflow_dispatch: + pull_request: + branches: + - iron + paths: + - '**.rst' + - '**.md' + - '**.yaml' + +jobs: + check-docs: + name: Check Docs + uses: ros-controls/control.ros.org/.github/workflows/reusable-sphinx-check-single-version.yml@iron + with: + ROS2_CONTROL_PR: ${{ github.ref }} diff --git a/.github/workflows/iron-coverage-build.yml b/.github/workflows/iron-coverage-build.yml index d82c52bf51..c74d66c3fd 100644 --- a/.github/workflows/iron-coverage-build.yml +++ b/.github/workflows/iron-coverage-build.yml @@ -4,9 +4,27 @@ on: push: branches: - iron + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/iron-coverage-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.iron.repos' + - 'codecov.yml' pull_request: branches: - iron + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/iron-coverage-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.iron.repos' + - 'codecov.yml' jobs: coverage_iron: @@ -14,4 +32,3 @@ jobs: secrets: inherit with: ros_distro: iron - os_name: ubuntu-22.04 diff --git a/.github/workflows/iron-debian-build.yml b/.github/workflows/iron-debian-build.yml index ff503d64a9..2e6ba2289c 100644 --- a/.github/workflows/iron-debian-build.yml +++ b/.github/workflows/iron-debian-build.yml @@ -4,6 +4,14 @@ on: pull_request: branches: - iron + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/iron-debian-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.iron.repos' schedule: # Run every day to detect flakiness and broken dependencies - cron: '03 1 * * *' diff --git a/.github/workflows/iron-pre-commit.yml b/.github/workflows/iron-pre-commit.yml index 60ad26d073..a128958031 100644 --- a/.github/workflows/iron-pre-commit.yml +++ b/.github/workflows/iron-pre-commit.yml @@ -11,4 +11,3 @@ jobs: 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/iron-rhel-binary-build.yml b/.github/workflows/iron-rhel-binary-build.yml index 981893524d..a45244b336 100644 --- a/.github/workflows/iron-rhel-binary-build.yml +++ b/.github/workflows/iron-rhel-binary-build.yml @@ -4,6 +4,14 @@ on: pull_request: branches: - iron + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/iron-rhel-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.iron.repos' schedule: # Run every day to detect flakiness and broken dependencies - cron: '03 1 * * *' diff --git a/.github/workflows/iron-semi-binary-build.yml b/.github/workflows/iron-semi-binary-build.yml index 59f8f347dd..8392655c35 100644 --- a/.github/workflows/iron-semi-binary-build.yml +++ b/.github/workflows/iron-semi-binary-build.yml @@ -7,9 +7,25 @@ on: pull_request: branches: - iron + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/iron-semi-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.iron.repos' push: branches: - iron + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/iron-semi-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.iron.repos' schedule: # Run every morning to detect flakiness and broken dependencies - cron: '03 1 * * *' diff --git a/.github/workflows/iron-source-build.yml b/.github/workflows/iron-source-build.yml index 2d12734df0..608702ab72 100644 --- a/.github/workflows/iron-source-build.yml +++ b/.github/workflows/iron-source-build.yml @@ -4,6 +4,14 @@ on: push: branches: - iron + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/iron-source-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.iron.repos' schedule: # Run every day to detect flakiness and broken dependencies - cron: '03 3 * * *' diff --git a/.github/workflows/jazzy-abi-compatibility.yml b/.github/workflows/jazzy-abi-compatibility.yml new file mode 100644 index 0000000000..331234d30f --- /dev/null +++ b/.github/workflows/jazzy-abi-compatibility.yml @@ -0,0 +1,26 @@ +name: Jazzy - ABI Compatibility Check +on: + workflow_dispatch: + pull_request: + branches: + - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/jazzy-abi-compatibility.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control-not-released.jazzy.repos' + +jobs: + abi_check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ros-industrial/industrial_ci@master + env: + ROS_DISTRO: jazzy + ROS_REPO: testing + ABICHECK_URL: github:${{ github.repository }}#${{ github.base_ref }} + NOT_TEST_BUILD: true diff --git a/.github/workflows/jazzy-binary-build.yml b/.github/workflows/jazzy-binary-build.yml new file mode 100644 index 0000000000..ac335c79f4 --- /dev/null +++ b/.github/workflows/jazzy-binary-build.yml @@ -0,0 +1,45 @@ +name: Jazzy Binary Build +# author: Denis Štogl +# description: 'Build & test all dependencies from released (binary) packages.' + +on: + workflow_dispatch: + pull_request: + branches: + - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/jazzy-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control-not-released.jazzy.repos' + push: + branches: + - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/jazzy-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control-not-released.jazzy.repos' + schedule: + # Run every morning to detect flakiness and broken dependencies + - cron: '03 1 * * *' + +jobs: + binary: + uses: ros-controls/ros2_control_ci/.github/workflows/reusable-industrial-ci-with-cache.yml@master + strategy: + fail-fast: false + matrix: + ROS_DISTRO: [jazzy] + ROS_REPO: [main, testing] + with: + ros_distro: ${{ matrix.ROS_DISTRO }} + ros_repo: ${{ matrix.ROS_REPO }} + upstream_workspace: ros2_control-not-released.${{ matrix.ROS_DISTRO }}.repos + ref_for_scheduled_build: master diff --git a/.github/workflows/jazzy-check-docs.yml b/.github/workflows/jazzy-check-docs.yml new file mode 100644 index 0000000000..cbdf6c30bd --- /dev/null +++ b/.github/workflows/jazzy-check-docs.yml @@ -0,0 +1,18 @@ +name: Jazzy Check Docs + +on: + workflow_dispatch: + pull_request: + branches: + - master + paths: + - '**.rst' + - '**.md' + - '**.yaml' + +jobs: + check-docs: + name: Check Docs + uses: ros-controls/control.ros.org/.github/workflows/reusable-sphinx-check-single-version.yml@jazzy + with: + ROS2_CONTROL_PR: ${{ github.ref }} diff --git a/.github/workflows/jazzy-coverage-build.yml b/.github/workflows/jazzy-coverage-build.yml new file mode 100644 index 0000000000..aa345d1e80 --- /dev/null +++ b/.github/workflows/jazzy-coverage-build.yml @@ -0,0 +1,35 @@ +name: Coverage Build - Jazzy +on: + workflow_dispatch: + # TODO(anyone) activate when branched for Jazzy + # push: + # branches: + # - master + # paths: + # - '**.hpp' + # - '**.h' + # - '**.cpp' + # - '.github/workflows/jazzy-coverage-build.yml' + # - '**/package.xml' + # - '**/CMakeLists.txt' + # - 'ros2_control.jazzy.repos' + # - 'codecov.yml' + # pull_request: + # branches: + # - master + # paths: + # - '**.hpp' + # - '**.h' + # - '**.cpp' + # - '.github/workflows/jazzy-coverage-build.yml' + # - '**/package.xml' + # - '**/CMakeLists.txt' + # - 'ros2_control.jazzy.repos' + # - 'codecov.yml' + +jobs: + coverage_jazzy: + uses: ros-controls/ros2_control_ci/.github/workflows/reusable-build-coverage.yml@master + secrets: inherit + with: + ros_distro: jazzy diff --git a/.github/workflows/jazzy-debian-build.yml b/.github/workflows/jazzy-debian-build.yml new file mode 100644 index 0000000000..b61f101ae5 --- /dev/null +++ b/.github/workflows/jazzy-debian-build.yml @@ -0,0 +1,31 @@ +name: Debian Jazzy Source Build +on: + workflow_dispatch: + pull_request: + branches: + - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/jazzy-debian-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.jazzy.repos' + schedule: + # Run every day to detect flakiness and broken dependencies + - cron: '03 1 * * *' + + +jobs: + debian_source_build: + uses: ros-controls/ros2_control_ci/.github/workflows/reusable-debian-build.yml@master + strategy: + fail-fast: false + matrix: + ROS_DISTRO: [jazzy] + with: + ros_distro: ${{ matrix.ROS_DISTRO }} + upstream_workspace: ros2_control.${{ matrix.ROS_DISTRO }}.repos + ref_for_scheduled_build: master + skip_packages: rqt_controller_manager diff --git a/.github/workflows/jazzy-pre-commit.yml b/.github/workflows/jazzy-pre-commit.yml new file mode 100644 index 0000000000..d9ec610bbc --- /dev/null +++ b/.github/workflows/jazzy-pre-commit.yml @@ -0,0 +1,13 @@ +name: Pre-Commit - Jazzy + +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: jazzy diff --git a/.github/workflows/jazzy-rhel-binary-build.yml b/.github/workflows/jazzy-rhel-binary-build.yml new file mode 100644 index 0000000000..1320624345 --- /dev/null +++ b/.github/workflows/jazzy-rhel-binary-build.yml @@ -0,0 +1,30 @@ +name: RHEL Jazzy Semi-Binary Build +on: + workflow_dispatch: + pull_request: + branches: + - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/jazzy-rhel-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.jazzy.repos' + schedule: + # Run every day to detect flakiness and broken dependencies + - cron: '03 1 * * *' + +jobs: + rhel_semi_binary_build: + uses: ros-controls/ros2_control_ci/.github/workflows/reusable-rhel-binary-build.yml@master + strategy: + fail-fast: false + matrix: + ROS_DISTRO: [jazzy] + with: + ros_distro: ${{ matrix.ROS_DISTRO }} + upstream_workspace: ros2_control.${{ matrix.ROS_DISTRO }}.repos + ref_for_scheduled_build: master + skip_packages: rqt_controller_manager diff --git a/.github/workflows/jazzy-semi-binary-build.yml b/.github/workflows/jazzy-semi-binary-build.yml new file mode 100644 index 0000000000..b6c669245a --- /dev/null +++ b/.github/workflows/jazzy-semi-binary-build.yml @@ -0,0 +1,45 @@ +name: Jazzy Semi-Binary Build +# author: Denis Štogl +# description: 'Build & test all dependencies from released (binary) packages.' + +on: + workflow_dispatch: + pull_request: + branches: + - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/jazzy-semi-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.jazzy.repos' + push: + branches: + - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/jazzy-semi-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.jazzy.repos' + schedule: + # Run every morning to detect flakiness and broken dependencies + - cron: '03 1 * * *' + +jobs: + binary: + uses: ros-controls/ros2_control_ci/.github/workflows/reusable-industrial-ci-with-cache.yml@master + strategy: + fail-fast: false + matrix: + ROS_DISTRO: [jazzy] + ROS_REPO: [main, testing] + with: + ros_distro: ${{ matrix.ROS_DISTRO }} + ros_repo: ${{ matrix.ROS_REPO }} + upstream_workspace: ros2_control.${{ matrix.ROS_DISTRO }}.repos + ref_for_scheduled_build: master diff --git a/.github/workflows/jazzy-source-build.yml b/.github/workflows/jazzy-source-build.yml new file mode 100644 index 0000000000..1acadc4a12 --- /dev/null +++ b/.github/workflows/jazzy-source-build.yml @@ -0,0 +1,26 @@ +name: Jazzy Source Build +on: + workflow_dispatch: + push: + branches: + - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/jazzy-source-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.jazzy.repos' + schedule: + # Run every day to detect flakiness and broken dependencies + - cron: '03 3 * * *' + +jobs: + source: + uses: ros-controls/ros2_control_ci/.github/workflows/reusable-ros-tooling-source-build.yml@master + with: + ros_distro: jazzy + ref: master + ros2_repo_branch: master + container: ubuntu:24.04 diff --git a/.github/workflows/rolling-abi-compatibility.yml b/.github/workflows/rolling-abi-compatibility.yml index 73055ef3e5..52b3ae8c57 100644 --- a/.github/workflows/rolling-abi-compatibility.yml +++ b/.github/workflows/rolling-abi-compatibility.yml @@ -4,6 +4,14 @@ on: pull_request: branches: - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/rolling-abi-compatibility.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control-not-released.rolling.repos' jobs: abi_check: diff --git a/.github/workflows/rolling-binary-build.yml b/.github/workflows/rolling-binary-build.yml index b083cc46fc..f556962d3a 100644 --- a/.github/workflows/rolling-binary-build.yml +++ b/.github/workflows/rolling-binary-build.yml @@ -7,9 +7,25 @@ on: pull_request: branches: - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/rolling-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control-not-released.rolling.repos' push: branches: - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/rolling-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control-not-released.rolling.repos' schedule: # Run every morning to detect flakiness and broken dependencies - cron: '03 1 * * *' diff --git a/.github/workflows/ci-check-docs.yml b/.github/workflows/rolling-check-docs.yml similarity index 67% rename from .github/workflows/ci-check-docs.yml rename to .github/workflows/rolling-check-docs.yml index 90a822aa72..80e8287abd 100644 --- a/.github/workflows/ci-check-docs.yml +++ b/.github/workflows/rolling-check-docs.yml @@ -1,8 +1,14 @@ -name: Check Docs +name: Rolling Check Docs on: workflow_dispatch: pull_request: + branches: + - master + paths: + - '**.rst' + - '**.md' + - '**.yaml' jobs: check-docs: diff --git a/.github/workflows/rolling-coverage-build.yml b/.github/workflows/rolling-coverage-build.yml index 8374afe8dc..4c61b47cbc 100644 --- a/.github/workflows/rolling-coverage-build.yml +++ b/.github/workflows/rolling-coverage-build.yml @@ -4,9 +4,27 @@ on: push: branches: - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/rolling-coverage-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.rolling.repos' + - 'codecov.yml' pull_request: branches: - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/rolling-coverage-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.rolling.repos' + - 'codecov.yml' jobs: coverage_rolling: @@ -14,4 +32,3 @@ jobs: secrets: inherit with: ros_distro: rolling - container: ubuntu:24.04 diff --git a/.github/workflows/rolling-debian-build.yml b/.github/workflows/rolling-debian-build.yml index e792963cc6..38ee0d213a 100644 --- a/.github/workflows/rolling-debian-build.yml +++ b/.github/workflows/rolling-debian-build.yml @@ -4,6 +4,14 @@ on: pull_request: branches: - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/rolling-debian-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.rolling.repos' schedule: # Run every day to detect flakiness and broken dependencies - cron: '03 1 * * *' diff --git a/.github/workflows/rolling-pre-commit.yml b/.github/workflows/rolling-pre-commit.yml index 4d91d19100..7bc07ba802 100644 --- a/.github/workflows/rolling-pre-commit.yml +++ b/.github/workflows/rolling-pre-commit.yml @@ -11,4 +11,3 @@ jobs: uses: ros-controls/ros2_control_ci/.github/workflows/reusable-pre-commit.yml@master with: ros_distro: rolling - container: ubuntu:24.04 diff --git a/.github/workflows/rolling-rhel-binary-build.yml b/.github/workflows/rolling-rhel-binary-build.yml index 38f375d6b6..e7e0e93fd1 100644 --- a/.github/workflows/rolling-rhel-binary-build.yml +++ b/.github/workflows/rolling-rhel-binary-build.yml @@ -4,6 +4,14 @@ on: pull_request: branches: - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/rolling-rhel-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.jazzy.repos' schedule: # Run every day to detect flakiness and broken dependencies - cron: '03 1 * * *' diff --git a/.github/workflows/rolling-semi-binary-build.yml b/.github/workflows/rolling-semi-binary-build.yml index f5c7885139..07cbcfc2f0 100644 --- a/.github/workflows/rolling-semi-binary-build.yml +++ b/.github/workflows/rolling-semi-binary-build.yml @@ -7,9 +7,25 @@ on: pull_request: branches: - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/rolling-semi-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.rolling.repos' push: branches: - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/rolling-semi-binary-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.rolling.repos' schedule: # Run every morning to detect flakiness and broken dependencies - cron: '03 1 * * *' diff --git a/.github/workflows/rolling-source-build.yml b/.github/workflows/rolling-source-build.yml index d9f1519ba3..ae6b40a4b4 100644 --- a/.github/workflows/rolling-source-build.yml +++ b/.github/workflows/rolling-source-build.yml @@ -4,6 +4,14 @@ on: push: branches: - master + paths: + - '**.hpp' + - '**.h' + - '**.cpp' + - '.github/workflows/rolling-source-build.yml' + - '**/package.xml' + - '**/CMakeLists.txt' + - 'ros2_control.rolling.repos' schedule: # Run every day to detect flakiness and broken dependencies - cron: '03 3 * * *' diff --git a/README.md b/README.md index e88ca5b7bd..40d2a3c189 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ For more, please check the [documentation](https://control.ros.org/). ROS2 Distro | Branch | Build status | Documentation | Released packages :---------: | :----: | :----------: | :-----------: | :---------------: **Rolling** | [`master`](https://github.com/ros-controls/ros2_control/tree/master) | [![Rolling Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-binary-build.yml?branch=master)
[![Rolling Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-semi-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-semi-binary-build.yml?branch=master) | [Documentation](https://control.ros.org/master/index.html)
[API Reference](https://control.ros.org/master/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#rolling) +**Jazzy** | [`master`](https://github.com/ros-controls/ros2_control/tree/master) | [![Rolling Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-binary-build.yml?branch=master)
[![Rolling Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-semi-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-semi-binary-build.yml?branch=master) | [Documentation](https://control.ros.org/jazzy/index.html)
[API Reference](https://control.ros.org/jazzy/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#jazzy) **Iron** | [`iron`](https://github.com/ros-controls/ros2_control/tree/iron) | [![Iron Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/iron-binary-build.yml/badge.svg?branch=iron)](https://github.com/ros-controls/ros2_control/actions/workflows/iron-binary-build.yml?branch=iron)
[![Iron Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/iron-semi-binary-build.yml/badge.svg?branch=iron)](https://github.com/ros-controls/ros2_control/actions/workflows/iron-semi-binary-build.yml?branch=iron) | [Documentation](https://control.ros.org/iron/index.html)
[API Reference](https://control.ros.org/iron/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#iron) **Humble** | [`humble`](https://github.com/ros-controls/ros2_control/tree/humble) | [![Humble Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/humble-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/humble-binary-build.yml?branch=master)
[![Humble Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/humble-semi-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/humble-semi-binary-build.yml?branch=master) | [Documentation](https://control.ros.org/humble/index.html)
[API Reference](https://control.ros.org/humble/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#humble) diff --git a/controller_interface/CHANGELOG.rst b/controller_interface/CHANGELOG.rst index 1688434cfc..b8301e0cba 100644 --- a/controller_interface/CHANGELOG.rst +++ b/controller_interface/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package controller_interface ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +4.10.0 (2024-05-08) +------------------- +* Working async controllers and components [not synchronized] (`#1041 `_) +* Contributors: Márk Szitanics + 4.9.0 (2024-04-30) ------------------ * return the proper const object of the pointer in the const method (`#1494 `_) diff --git a/controller_interface/CMakeLists.txt b/controller_interface/CMakeLists.txt index 2b7ccb9203..3dc3bc4d0a 100644 --- a/controller_interface/CMakeLists.txt +++ b/controller_interface/CMakeLists.txt @@ -12,6 +12,7 @@ set(THIS_PACKAGE_INCLUDE_DEPENDS ) find_package(ament_cmake REQUIRED) +find_package(ament_cmake_gen_version_h REQUIRED) foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS}) find_package(${Dependency} REQUIRED) endforeach() diff --git a/controller_interface/include/controller_interface/async_controller.hpp b/controller_interface/include/controller_interface/async_controller.hpp new file mode 100644 index 0000000000..5601ff4703 --- /dev/null +++ b/controller_interface/include/controller_interface/async_controller.hpp @@ -0,0 +1,111 @@ +// Copyright 2024 ros2_control development team +// +// 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. + +#ifndef CONTROLLER_INTERFACE__ASYNC_CONTROLLER_HPP_ +#define CONTROLLER_INTERFACE__ASYNC_CONTROLLER_HPP_ + +#include +#include +#include + +#include "controller_interface_base.hpp" +#include "lifecycle_msgs/msg/state.hpp" + +namespace controller_interface +{ + +class AsyncControllerThread +{ +public: + /// Constructor for the AsyncControllerThread object. + /** + * + * \param[in] controller shared pointer to a controller. + * \param[in] cm_update_rate the controller manager's update rate. + */ + AsyncControllerThread( + std::shared_ptr & controller, int cm_update_rate) + : terminated_(false), controller_(controller), thread_{}, cm_update_rate_(cm_update_rate) + { + } + + AsyncControllerThread(const AsyncControllerThread & t) = delete; + AsyncControllerThread(AsyncControllerThread && t) = delete; + + // Destructor, called when the component is erased from its map. + ~AsyncControllerThread() + { + terminated_.store(true, std::memory_order_seq_cst); + if (thread_.joinable()) + { + thread_.join(); + } + } + + /// Creates the controller's thread. + /** + * Called when the controller is activated. + * + */ + void activate() + { + thread_ = std::thread(&AsyncControllerThread::controller_update_callback, this); + } + + /// Periodically execute the controller's update method. + /** + * Callback of the async controller's thread. + * **Not synchronized with the controller manager's write and read currently** + * + */ + void controller_update_callback() + { + using TimePoint = std::chrono::system_clock::time_point; + unsigned int used_update_rate = + controller_->get_update_rate() == 0 ? cm_update_rate_ : controller_->get_update_rate(); + + auto previous_time = controller_->get_node()->now(); + while (!terminated_.load(std::memory_order_relaxed)) + { + auto const period = std::chrono::nanoseconds(1'000'000'000 / used_update_rate); + TimePoint next_iteration_time = + TimePoint(std::chrono::nanoseconds(controller_->get_node()->now().nanoseconds())); + + if (controller_->get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) + { + auto const current_time = controller_->get_node()->now(); + auto const measured_period = current_time - previous_time; + previous_time = current_time; + controller_->update( + controller_->get_node()->now(), + (controller_->get_update_rate() != cm_update_rate_ && controller_->get_update_rate() != 0) + ? rclcpp::Duration::from_seconds(1.0 / controller_->get_update_rate()) + : measured_period); + } + + next_iteration_time += period; + std::this_thread::sleep_until(next_iteration_time); + } + } + +private: + std::atomic terminated_; + std::shared_ptr controller_; + std::thread thread_; + unsigned int cm_update_rate_; +}; + +} // namespace controller_interface + +#endif // CONTROLLER_INTERFACE__ASYNC_CONTROLLER_HPP_ diff --git a/controller_interface/package.xml b/controller_interface/package.xml index 9e8010eeb9..a4b89265e3 100644 --- a/controller_interface/package.xml +++ b/controller_interface/package.xml @@ -2,7 +2,7 @@ controller_interface - 4.9.0 + 4.10.0 Description of controller_interface Bence Magyar Denis Štogl diff --git a/controller_interface/test/test_controller_interface.cpp b/controller_interface/test/test_controller_interface.cpp index 3976b2a81e..03838b1a2e 100644 --- a/controller_interface/test/test_controller_interface.cpp +++ b/controller_interface/test/test_controller_interface.cpp @@ -88,7 +88,8 @@ TEST(TestableControllerInterface, setting_update_rate_in_configure) ASSERT_EQ(controller.get_update_rate(), 2812u); // Test updating of update_rate parameter - EXPECT_EQ(std::system("ros2 param set /testable_controller_interface update_rate 623"), 0); + auto res = controller.get_node()->set_parameter(rclcpp::Parameter("update_rate", 623)); + EXPECT_EQ(res.successful, true); // Keep the same update rate until transition from 'UNCONFIGURED' TO 'INACTIVE' does not happen controller.configure(); // No transition so the update rate should stay intact ASSERT_NE(controller.get_update_rate(), 623u); diff --git a/controller_manager/CHANGELOG.rst b/controller_manager/CHANGELOG.rst index 74657cd12b..4c5e9579ef 100644 --- a/controller_manager/CHANGELOG.rst +++ b/controller_manager/CHANGELOG.rst @@ -2,6 +2,15 @@ Changelog for package controller_manager ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +4.10.0 (2024-05-08) +------------------- +* allow extra spawner arguments to not declare every argument in launch utils (`#1505 `_) +* Working async controllers and components [not synchronized] (`#1041 `_) +* Add fallback controllers list to the ControllerInfo (`#1503 `_) +* Add a functionality to look for the controller type in the params file when not parsed (`#1502 `_) +* Add controller exception handling in controller manager (`#1507 `_) +* Contributors: Márk Szitanics, Sai Kishor Kothakota + 4.9.0 (2024-04-30) ------------------ * Deactivate the controllers when they return error similar to hardware (`#1499 `_) diff --git a/controller_manager/CMakeLists.txt b/controller_manager/CMakeLists.txt index d99a7f7330..c3c0de7739 100644 --- a/controller_manager/CMakeLists.txt +++ b/controller_manager/CMakeLists.txt @@ -21,6 +21,7 @@ set(THIS_PACKAGE_INCLUDE_DEPENDS find_package(ament_cmake REQUIRED) find_package(ament_cmake_python REQUIRED) find_package(ament_cmake_core REQUIRED) +find_package(ament_cmake_gen_version_h REQUIRED) find_package(backward_ros REQUIRED) foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS}) find_package(${Dependency} REQUIRED) diff --git a/controller_manager/controller_manager/launch_utils.py b/controller_manager/controller_manager/launch_utils.py index c7c5974a94..01528552d0 100644 --- a/controller_manager/controller_manager/launch_utils.py +++ b/controller_manager/controller_manager/launch_utils.py @@ -21,7 +21,7 @@ def generate_load_controller_launch_description( - controller_name, controller_type=None, controller_params_file=None + controller_name, controller_type=None, controller_params_file=None, extra_spawner_args=[] ): """ Generate launch description for loading a controller using spawner. @@ -40,7 +40,8 @@ def generate_load_controller_launch_description( 'joint_state_broadcaster', controller_type='joint_state_broadcaster/JointStateBroadcaster', controller_params_file=os.path.join(get_package_share_directory('my_pkg'), - 'config', 'controller_params.yaml') + 'config', 'controller_params.yaml'), + extra_spawner_args=[--load-only] ) """ @@ -86,6 +87,9 @@ def generate_load_controller_launch_description( ) ] + if extra_spawner_args: + spawner_arguments += extra_spawner_args + spawner = Node( package="controller_manager", executable="spawner", diff --git a/controller_manager/include/controller_manager/controller_manager.hpp b/controller_manager/include/controller_manager/controller_manager.hpp index 9a709c5f8f..845fa2dee0 100644 --- a/controller_manager/include/controller_manager/controller_manager.hpp +++ b/controller_manager/include/controller_manager/controller_manager.hpp @@ -23,6 +23,7 @@ #include #include +#include "controller_interface/async_controller.hpp" #include "controller_interface/chainable_controller_interface.hpp" #include "controller_interface/controller_interface.hpp" #include "controller_interface/controller_interface_base.hpp" @@ -196,6 +197,15 @@ class ControllerManager : public rclcpp::Node CONTROLLER_MANAGER_PUBLIC unsigned int get_update_rate() const; + /// Deletes all async controllers and components. + /** + * Needed to join the threads immediately after the control loop is ended + * to avoid unnecessary iterations. Otherwise + * the threads will be joined only when the controller manager gets destroyed. + */ + CONTROLLER_MANAGER_PUBLIC + void shutdown_async_controllers_and_components(); + protected: CONTROLLER_MANAGER_PUBLIC void init_services(); @@ -563,65 +573,7 @@ class ControllerManager : public rclcpp::Node SwitchParams switch_params_; - class ControllerThreadWrapper - { - public: - ControllerThreadWrapper( - std::shared_ptr & controller, - int cm_update_rate) - : terminated_(false), controller_(controller), thread_{}, cm_update_rate_(cm_update_rate) - { - } - - ControllerThreadWrapper(const ControllerThreadWrapper & t) = delete; - ControllerThreadWrapper(ControllerThreadWrapper && t) = default; - ~ControllerThreadWrapper() - { - terminated_.store(true, std::memory_order_seq_cst); - if (thread_.joinable()) - { - thread_.join(); - } - } - - void activate() - { - thread_ = std::thread(&ControllerThreadWrapper::call_controller_update, this); - } - - void call_controller_update() - { - using TimePoint = std::chrono::system_clock::time_point; - unsigned int used_update_rate = - controller_->get_update_rate() == 0 - ? cm_update_rate_ - : controller_ - ->get_update_rate(); // determines if the controller's or CM's update rate is used - - while (!terminated_.load(std::memory_order_relaxed)) - { - auto const period = std::chrono::nanoseconds(1'000'000'000 / used_update_rate); - TimePoint next_iteration_time = - TimePoint(std::chrono::nanoseconds(controller_->get_node()->now().nanoseconds())); - - if (controller_->get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) - { - // critical section, not implemented yet - } - - next_iteration_time += period; - std::this_thread::sleep_until(next_iteration_time); - } - } - - private: - std::atomic terminated_; - std::shared_ptr controller_; - std::thread thread_; - unsigned int cm_update_rate_; - }; - - std::unordered_map> + std::unordered_map> async_controller_threads_; }; diff --git a/controller_manager/package.xml b/controller_manager/package.xml index 884ffa9384..ec5b0e4f0e 100644 --- a/controller_manager/package.xml +++ b/controller_manager/package.xml @@ -2,7 +2,7 @@ controller_manager - 4.9.0 + 4.10.0 Description of controller_manager Bence Magyar Denis Štogl diff --git a/controller_manager/src/controller_manager.cpp b/controller_manager/src/controller_manager.cpp index 6cb7b025b1..41fe1ad95d 100644 --- a/controller_manager/src/controller_manager.cpp +++ b/controller_manager/src/controller_manager.cpp @@ -707,7 +707,8 @@ controller_interface::return_type ControllerManager::configure_controller( if (controller->is_async()) { async_controller_threads_.emplace( - controller_name, std::make_unique(controller, update_rate_)); + controller_name, + std::make_unique(controller, update_rate_)); } const auto controller_update_rate = controller->get_update_rate(); @@ -2317,6 +2318,13 @@ std::pair ControllerManager::split_command_interface( unsigned int ControllerManager::get_update_rate() const { return update_rate_; } +void ControllerManager::shutdown_async_controllers_and_components() +{ + async_controller_threads_.erase( + async_controller_threads_.begin(), async_controller_threads_.end()); + resource_manager_->shutdown_async_components(); +} + void ControllerManager::propagate_deactivation_of_chained_mode( const std::vector & controllers) { diff --git a/controller_manager/src/ros2_control_node.cpp b/controller_manager/src/ros2_control_node.cpp index 2747e79a1b..6dd7d72fb2 100644 --- a/controller_manager/src/ros2_control_node.cpp +++ b/controller_manager/src/ros2_control_node.cpp @@ -83,6 +83,8 @@ int main(int argc, char ** argv) next_iteration_time += period; std::this_thread::sleep_until(next_iteration_time); } + + cm->shutdown_async_controllers_and_components(); }); executor->add_node(cm); diff --git a/controller_manager_msgs/CHANGELOG.rst b/controller_manager_msgs/CHANGELOG.rst index 6491f5e292..cf752dde2a 100644 --- a/controller_manager_msgs/CHANGELOG.rst +++ b/controller_manager_msgs/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package controller_manager_msgs ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +4.10.0 (2024-05-08) +------------------- + 4.9.0 (2024-04-30) ------------------ diff --git a/controller_manager_msgs/package.xml b/controller_manager_msgs/package.xml index 32264bd09e..1f078be2cf 100644 --- a/controller_manager_msgs/package.xml +++ b/controller_manager_msgs/package.xml @@ -2,7 +2,7 @@ controller_manager_msgs - 4.9.0 + 4.10.0 Messages and services for the controller manager. Bence Magyar Denis Štogl diff --git a/doc/release_notes/Jazzy.rst b/doc/release_notes/Jazzy.rst index 52fc54a071..4074cbca63 100644 --- a/doc/release_notes/Jazzy.rst +++ b/doc/release_notes/Jazzy.rst @@ -70,6 +70,30 @@ hardware_interface ****************** * A portable version for string-to-double conversion was added: ``hardware_interface::stod`` (`#1257 `_) * ``test_components`` was moved to its own package (`#1325 `_) +* The ``ros2_control`` tag now supports parsing of the limits from the URDF into the ``HardwareInfo`` structure. More conservative limits can be defined using the ``min`` and ``max`` attributes per interface (`#1472 `_) + + .. code:: xml + + + + ros2_control_demo_hardware/RRBotSystemPositionOnlyHardware + 2.0 + 3.0 + 2.0 + + + + -1 + 1 + + + + + + + + +* Soft limits are also parsed from the URDF into the ``HardwareInfo`` structure for the defined joints (`#1488 `_) joint_limits ************ diff --git a/hardware_interface/CHANGELOG.rst b/hardware_interface/CHANGELOG.rst index 6e1528bc15..a27d097d6e 100644 --- a/hardware_interface/CHANGELOG.rst +++ b/hardware_interface/CHANGELOG.rst @@ -2,6 +2,15 @@ Changelog for package hardware_interface ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +4.10.0 (2024-05-08) +------------------- +* Add hardware components exception handling in resource manager (`#1508 `_) +* Working async controllers and components [not synchronized] (`#1041 `_) +* Parse URDF joint hard limits into the HardwareInfo structure (`#1472 `_) +* Add fallback controllers list to the ControllerInfo (`#1503 `_) +* Add more common hardware interface type constants (`#1500 `_) +* Contributors: Márk Szitanics, Sai Kishor Kothakota + 4.9.0 (2024-04-30) ------------------ * Add missing calculate_dynamics (`#1498 `_) diff --git a/hardware_interface/CMakeLists.txt b/hardware_interface/CMakeLists.txt index a5debef3b7..c557a4970f 100644 --- a/hardware_interface/CMakeLists.txt +++ b/hardware_interface/CMakeLists.txt @@ -20,6 +20,7 @@ set(THIS_PACKAGE_INCLUDE_DEPENDS ) find_package(ament_cmake REQUIRED) +find_package(ament_cmake_gen_version_h REQUIRED) foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS}) find_package(${Dependency} REQUIRED) endforeach() diff --git a/hardware_interface/include/hardware_interface/async_components.hpp b/hardware_interface/include/hardware_interface/async_components.hpp index 2fa6fd7f85..73495c92f8 100644 --- a/hardware_interface/include/hardware_interface/async_components.hpp +++ b/hardware_interface/include/hardware_interface/async_components.hpp @@ -17,6 +17,7 @@ #include #include +#include #include #include "hardware_interface/actuator.hpp" @@ -34,29 +35,23 @@ class AsyncComponentThread { public: explicit AsyncComponentThread( - Actuator * component, unsigned int update_rate, + unsigned int update_rate, rclcpp::node_interfaces::NodeClockInterface::SharedPtr clock_interface) - : hardware_component_(component), cm_update_rate_(update_rate), clock_interface_(clock_interface) + : cm_update_rate_(update_rate), clock_interface_(clock_interface) { } - explicit AsyncComponentThread( - System * component, unsigned int update_rate, - rclcpp::node_interfaces::NodeClockInterface::SharedPtr clock_interface) - : hardware_component_(component), cm_update_rate_(update_rate), clock_interface_(clock_interface) - { - } - - explicit AsyncComponentThread( - Sensor * component, unsigned int update_rate, - rclcpp::node_interfaces::NodeClockInterface::SharedPtr clock_interface) - : hardware_component_(component), cm_update_rate_(update_rate), clock_interface_(clock_interface) + // Fills the internal variant with the desired component. + template + void register_component(T * component) { + hardware_component_ = component; } AsyncComponentThread(const AsyncComponentThread & t) = delete; - AsyncComponentThread(AsyncComponentThread && t) = default; + AsyncComponentThread(AsyncComponentThread && t) = delete; + // Destructor, called when the component is erased from its map. ~AsyncComponentThread() { terminated_.store(true, std::memory_order_seq_cst); @@ -65,9 +60,19 @@ class AsyncComponentThread write_and_read_.join(); } } - + /// Creates the component's thread. + /** + * Called when the component is activated. + * + */ void activate() { write_and_read_ = std::thread(&AsyncComponentThread::write_and_read, this); } + /// Periodically execute the component's write and read methods. + /** + * Callback of the async component's thread. + * **Not synchronized with the controller manager's update currently** + * + */ void write_and_read() { using TimePoint = std::chrono::system_clock::time_point; @@ -88,8 +93,12 @@ class AsyncComponentThread auto measured_period = current_time - previous_time; previous_time = current_time; - // write - // read + if (!first_iteration) + { + component->write(clock_interface_->get_clock()->now(), measured_period); + } + component->read(clock_interface_->get_clock()->now(), measured_period); + first_iteration = false; } next_iteration_time += period; std::this_thread::sleep_until(next_iteration_time); @@ -104,6 +113,7 @@ class AsyncComponentThread std::thread write_and_read_{}; unsigned int cm_update_rate_; + bool first_iteration = true; rclcpp::node_interfaces::NodeClockInterface::SharedPtr clock_interface_; }; diff --git a/hardware_interface/include/hardware_interface/hardware_info.hpp b/hardware_interface/include/hardware_interface/hardware_info.hpp index cb548b9bf4..d94573bf5e 100644 --- a/hardware_interface/include/hardware_interface/hardware_info.hpp +++ b/hardware_interface/include/hardware_interface/hardware_info.hpp @@ -171,6 +171,13 @@ struct HardwareInfo * The URDF parsed limits of the hardware components joint command interfaces */ std::unordered_map limits; + + /** + * Map of software joint limits used for clamping the command where the key is the joint name. + * Optional If not specified or less restrictive than the JointLimits uses the previous + * JointLimits. + */ + std::unordered_map soft_limits; }; } // namespace hardware_interface diff --git a/hardware_interface/include/hardware_interface/resource_manager.hpp b/hardware_interface/include/hardware_interface/resource_manager.hpp index 743e548a4c..91a98d1e96 100644 --- a/hardware_interface/include/hardware_interface/resource_manager.hpp +++ b/hardware_interface/include/hardware_interface/resource_manager.hpp @@ -376,6 +376,12 @@ class HARDWARE_INTERFACE_PUBLIC ResourceManager return_type set_component_state( const std::string & component_name, rclcpp_lifecycle::State & target_state); + /// Deletes all async components from the resource storage + /** + * Needed to join the threads immediately after the control loop is ended. + */ + void shutdown_async_components(); + /// Reads all loaded hardware components. /** * Reads from all active hardware components. diff --git a/hardware_interface/package.xml b/hardware_interface/package.xml index b79d280a11..33dea58c73 100644 --- a/hardware_interface/package.xml +++ b/hardware_interface/package.xml @@ -1,7 +1,7 @@ hardware_interface - 4.9.0 + 4.10.0 ros2_control hardware interface Bence Magyar Denis Štogl diff --git a/hardware_interface/src/component_parser.cpp b/hardware_interface/src/component_parser.cpp index aad1d827ec..14e016df21 100644 --- a/hardware_interface/src/component_parser.cpp +++ b/hardware_interface/src/component_parser.cpp @@ -927,6 +927,16 @@ std::vector parse_control_resources_from_urdf(const std::string & // Take the most restricted one. Also valid for continuous-joint type only detail::update_interface_limits(joint.command_interfaces, limits); hw_info.limits[joint.name] = limits; + joint_limits::SoftJointLimits soft_limits; + if (getSoftJointLimits(urdf_joint, soft_limits)) + { + if (limits.has_position_limits) + { + soft_limits.min_position = std::max(soft_limits.min_position, limits.min_position); + soft_limits.max_position = std::min(soft_limits.max_position, limits.max_position); + } + hw_info.soft_limits[joint.name] = soft_limits; + } } } diff --git a/hardware_interface/src/resource_manager.cpp b/hardware_interface/src/resource_manager.cpp index 2e8ccc7b1f..92b6d01519 100644 --- a/hardware_interface/src/resource_manager.cpp +++ b/hardware_interface/src/resource_manager.cpp @@ -67,6 +67,26 @@ auto trigger_and_print_hardware_state_transition = return result; }; +std::string interfaces_to_string( + const std::vector & start_interfaces, + const std::vector & stop_interfaces) +{ + std::stringstream ss; + ss << "Start interfaces: " << std::endl << "[" << std::endl; + for (const auto & start_if : start_interfaces) + { + ss << " " << start_if << std::endl; + } + ss << "]" << std::endl; + ss << "Stop interfaces: " << std::endl << "[" << std::endl; + for (const auto & stop_if : stop_interfaces) + { + ss << " " << stop_if << std::endl; + } + ss << "]" << std::endl; + return ss.str(); +}; + class ResourceStorage { static constexpr const char * pkg_name = "hardware_interface"; @@ -90,27 +110,62 @@ class ResourceStorage } template - void load_hardware( + [[nodiscard]] bool load_hardware( const HardwareInfo & hardware_info, pluginlib::ClassLoader & loader, std::vector & container) { - RCUTILS_LOG_INFO_NAMED( - "resource_manager", "Loading hardware '%s' ", hardware_info.name.c_str()); - // hardware_plugin_name has to match class name in plugin xml description - auto interface = std::unique_ptr( - loader.createUnmanagedInstance(hardware_info.hardware_plugin_name)); - HardwareT hardware(std::move(interface)); - container.emplace_back(std::move(hardware)); - // initialize static data about hardware component to reduce later calls - HardwareComponentInfo component_info; - component_info.name = hardware_info.name; - component_info.type = hardware_info.type; - component_info.plugin_name = hardware_info.hardware_plugin_name; - component_info.is_async = hardware_info.is_async; - - hardware_info_map_.insert(std::make_pair(component_info.name, component_info)); - hardware_used_by_controllers_.insert( - std::make_pair(component_info.name, std::vector())); + bool is_loaded = false; + try + { + RCUTILS_LOG_INFO_NAMED( + "resource_manager", "Loading hardware '%s' ", hardware_info.name.c_str()); + // hardware_plugin_name has to match class name in plugin xml description + auto interface = std::unique_ptr( + loader.createUnmanagedInstance(hardware_info.hardware_plugin_name)); + if (interface) + { + RCUTILS_LOG_INFO_NAMED( + "resource_manager", "Loaded hardware '%s' from plugin '%s'", hardware_info.name.c_str(), + hardware_info.hardware_plugin_name.c_str()); + HardwareT hardware(std::move(interface)); + container.emplace_back(std::move(hardware)); + // initialize static data about hardware component to reduce later calls + HardwareComponentInfo component_info; + component_info.name = hardware_info.name; + component_info.type = hardware_info.type; + component_info.plugin_name = hardware_info.hardware_plugin_name; + component_info.is_async = hardware_info.is_async; + + hardware_info_map_.insert(std::make_pair(component_info.name, component_info)); + hardware_used_by_controllers_.insert( + std::make_pair(component_info.name, std::vector())); + is_loaded = true; + } + else + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Failed to load hardware '%s' from plugin '%s'", + hardware_info.name.c_str(), hardware_info.hardware_plugin_name.c_str()); + } + } + catch (const pluginlib::PluginlibException & ex) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Exception while loading hardware: %s", ex.what()); + } + catch (const std::exception & ex) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Exception occurred while loading hardware '%s': %s", + hardware_info.name.c_str(), ex.what()); + } + catch (...) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Unknown exception occurred while loading hardware '%s'", + hardware_info.name.c_str()); + } + return is_loaded; } template @@ -119,30 +174,62 @@ class ResourceStorage RCUTILS_LOG_INFO_NAMED( "resource_manager", "Initialize hardware '%s' ", hardware_info.name.c_str()); - const rclcpp_lifecycle::State new_state = hardware.initialize(hardware_info); - - bool result = new_state.id() == lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED; + bool result = false; + try + { + const rclcpp_lifecycle::State new_state = hardware.initialize(hardware_info); + result = new_state.id() == lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED; - if (result) + if (result) + { + RCUTILS_LOG_INFO_NAMED( + "resource_manager", "Successful initialization of hardware '%s'", + hardware_info.name.c_str()); + } + else + { + RCUTILS_LOG_INFO_NAMED( + "resource_manager", "Failed to initialize hardware '%s'", hardware_info.name.c_str()); + } + } + catch (const std::exception & ex) { - RCUTILS_LOG_INFO_NAMED( - "resource_manager", "Successful initialization of hardware '%s'", - hardware_info.name.c_str()); + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Exception occurred while initializing hardware '%s': %s", + hardware_info.name.c_str(), ex.what()); } - else + catch (...) { - RCUTILS_LOG_INFO_NAMED( - "resource_manager", "Failed to initialize hardware '%s'", hardware_info.name.c_str()); + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Unknown exception occurred while initializing hardware '%s'", + hardware_info.name.c_str()); } + return result; } template bool configure_hardware(HardwareT & hardware) { - bool result = trigger_and_print_hardware_state_transition( - std::bind(&HardwareT::configure, &hardware), "configure", hardware.get_name(), - lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE); + bool result = false; + try + { + result = trigger_and_print_hardware_state_transition( + std::bind(&HardwareT::configure, &hardware), "configure", hardware.get_name(), + lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE); + } + catch (const std::exception & ex) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Exception occurred while configuring hardware '%s': %s", + hardware.get_name().c_str(), ex.what()); + } + catch (...) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Unknown exception occurred while configuring hardware '%s'", + hardware.get_name().c_str()); + } if (result) { @@ -206,7 +293,9 @@ class ResourceStorage { async_component_threads_.emplace( std::piecewise_construct, std::forward_as_tuple(hardware.get_name()), - std::forward_as_tuple(&hardware, cm_update_rate_, clock_interface_)); + std::forward_as_tuple(cm_update_rate_, clock_interface_)); + + async_component_threads_.at(hardware.get_name()).register_component(&hardware); } } return result; @@ -267,9 +356,25 @@ class ResourceStorage template bool cleanup_hardware(HardwareT & hardware) { - bool result = trigger_and_print_hardware_state_transition( - std::bind(&HardwareT::cleanup, &hardware), "cleanup", hardware.get_name(), - lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED); + bool result = false; + try + { + result = trigger_and_print_hardware_state_transition( + std::bind(&HardwareT::cleanup, &hardware), "cleanup", hardware.get_name(), + lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED); + } + catch (const std::exception & ex) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Exception occurred while cleaning up hardware '%s': %s", + hardware.get_name().c_str(), ex.what()); + } + catch (...) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Unknown exception occurred while cleaning up hardware '%s'", + hardware.get_name().c_str()); + } if (result) { @@ -281,9 +386,25 @@ class ResourceStorage template bool shutdown_hardware(HardwareT & hardware) { - bool result = trigger_and_print_hardware_state_transition( - std::bind(&HardwareT::shutdown, &hardware), "shutdown", hardware.get_name(), - lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED); + bool result = false; + try + { + result = trigger_and_print_hardware_state_transition( + std::bind(&HardwareT::shutdown, &hardware), "shutdown", hardware.get_name(), + lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED); + } + catch (const std::exception & ex) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Exception occurred while shutting down hardware '%s': %s", + hardware.get_name().c_str(), ex.what()); + } + catch (...) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Unknown exception occurred while shutting down hardware '%s'", + hardware.get_name().c_str()); + } if (result) { @@ -300,9 +421,25 @@ class ResourceStorage template bool activate_hardware(HardwareT & hardware) { - bool result = trigger_and_print_hardware_state_transition( - std::bind(&HardwareT::activate, &hardware), "activate", hardware.get_name(), - lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE); + bool result = false; + try + { + result = trigger_and_print_hardware_state_transition( + std::bind(&HardwareT::activate, &hardware), "activate", hardware.get_name(), + lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE); + } + catch (const std::exception & ex) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Exception occurred while activating hardware '%s': %s", + hardware.get_name().c_str(), ex.what()); + } + catch (...) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Unknown exception occurred while activating hardware '%s'", + hardware.get_name().c_str()); + } if (result) { @@ -319,9 +456,25 @@ class ResourceStorage template bool deactivate_hardware(HardwareT & hardware) { - bool result = trigger_and_print_hardware_state_transition( - std::bind(&HardwareT::deactivate, &hardware), "deactivate", hardware.get_name(), - lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE); + bool result = false; + try + { + result = trigger_and_print_hardware_state_transition( + std::bind(&HardwareT::deactivate, &hardware), "deactivate", hardware.get_name(), + lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE); + } + catch (const std::exception & ex) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Exception occurred while deactivating hardware '%s': %s", + hardware.get_name().c_str(), ex.what()); + } + catch (...) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Unknown exception occurred while deactivating hardware '%s'", + hardware.get_name().c_str()); + } if (result) { @@ -433,25 +586,60 @@ class ResourceStorage template void import_state_interfaces(HardwareT & hardware) { - auto interfaces = hardware.export_state_interfaces(); - std::vector interface_names; - interface_names.reserve(interfaces.size()); - for (auto & interface : interfaces) + try { - auto key = interface.get_name(); - state_interface_map_.emplace(std::make_pair(key, std::move(interface))); - interface_names.push_back(key); + auto interfaces = hardware.export_state_interfaces(); + std::vector interface_names; + interface_names.reserve(interfaces.size()); + for (auto & interface : interfaces) + { + auto key = interface.get_name(); + state_interface_map_.emplace(std::make_pair(key, std::move(interface))); + interface_names.push_back(key); + } + hardware_info_map_[hardware.get_name()].state_interfaces = interface_names; + available_state_interfaces_.reserve( + available_state_interfaces_.capacity() + interface_names.size()); + } + catch (const std::exception & e) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", + "Exception occurred while importing state interfaces for the hardware '%s' : %s", + hardware.get_name().c_str(), e.what()); + } + catch (...) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", + "Unknown exception occurred while importing state interfaces for the hardware '%s'", + hardware.get_name().c_str()); } - hardware_info_map_[hardware.get_name()].state_interfaces = interface_names; - available_state_interfaces_.reserve( - available_state_interfaces_.capacity() + interface_names.size()); } template void import_command_interfaces(HardwareT & hardware) { - auto interfaces = hardware.export_command_interfaces(); - hardware_info_map_[hardware.get_name()].command_interfaces = add_command_interfaces(interfaces); + try + { + auto interfaces = hardware.export_command_interfaces(); + hardware_info_map_[hardware.get_name()].command_interfaces = + add_command_interfaces(interfaces); + } + catch (const std::exception & ex) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", + "Exception occurred while importing command interfaces for the hardware '%s' : %s", + hardware.get_name().c_str(), ex.what()); + } + catch (...) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", + "Unknown exception occurred while importing command interfaces for the hardware '%s'", + hardware.get_name().c_str()); + } } /// Adds exported command interfaces into internal storage. @@ -514,7 +702,10 @@ class ResourceStorage auto load_and_init_actuators = [&](auto & container) { check_for_duplicates(hardware_info); - load_hardware(hardware_info, actuator_loader_, container); + if (!load_hardware(hardware_info, actuator_loader_, container)) + { + return; + } if (initialize_hardware(hardware_info, container.back())) { import_state_interfaces(container.back()); @@ -544,7 +735,10 @@ class ResourceStorage auto load_and_init_sensors = [&](auto & container) { check_for_duplicates(hardware_info); - load_hardware(hardware_info, sensor_loader_, container); + if (!load_hardware(hardware_info, sensor_loader_, container)) + { + return; + } if (initialize_hardware(hardware_info, container.back())) { import_state_interfaces(container.back()); @@ -573,7 +767,10 @@ class ResourceStorage auto load_and_init_systems = [&](auto & container) { check_for_duplicates(hardware_info); - load_hardware(hardware_info, system_loader_, container); + if (!load_hardware(hardware_info, system_loader_, container)) + { + return; + } if (initialize_hardware(hardware_info, container.back())) { import_state_interfaces(container.back()); @@ -1083,24 +1280,6 @@ bool ResourceManager::prepare_command_mode_switch( return true; } - auto interfaces_to_string = [&]() - { - std::stringstream ss; - ss << "Start interfaces: " << std::endl << "[" << std::endl; - for (const auto & start_if : start_interfaces) - { - ss << " " << start_if << std::endl; - } - ss << "]" << std::endl; - ss << "Stop interfaces: " << std::endl << "[" << std::endl; - for (const auto & stop_if : stop_interfaces) - { - ss << " " << stop_if << std::endl; - } - ss << "]" << std::endl; - return ss.str(); - }; - // Check if interface exists std::stringstream ss_not_existing; ss_not_existing << "Not existing: " << std::endl << "[" << std::endl; @@ -1122,7 +1301,8 @@ bool ResourceManager::prepare_command_mode_switch( ss_not_existing << "]" << std::endl; RCUTILS_LOG_ERROR_NAMED( "resource_manager", "Not acceptable command interfaces combination: \n%s%s", - interfaces_to_string().c_str(), ss_not_existing.str().c_str()); + interfaces_to_string(start_interfaces, stop_interfaces).c_str(), + ss_not_existing.str().c_str()); return false; } @@ -1147,12 +1327,12 @@ bool ResourceManager::prepare_command_mode_switch( ss_not_available << "]" << std::endl; RCUTILS_LOG_ERROR_NAMED( "resource_manager", "Not acceptable command interfaces combination: \n%s%s", - interfaces_to_string().c_str(), ss_not_available.str().c_str()); + interfaces_to_string(start_interfaces, stop_interfaces).c_str(), + ss_not_available.str().c_str()); return false; } - auto call_prepare_mode_switch = - [&start_interfaces, &stop_interfaces, &interfaces_to_string](auto & components) + auto call_prepare_mode_switch = [&start_interfaces, &stop_interfaces](auto & components) { bool ret = true; for (auto & component : components) @@ -1161,14 +1341,38 @@ bool ResourceManager::prepare_command_mode_switch( component.get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE || component.get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) { - if ( - return_type::OK != - component.prepare_command_mode_switch(start_interfaces, stop_interfaces)) + try + { + if ( + return_type::OK != + component.prepare_command_mode_switch(start_interfaces, stop_interfaces)) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", + "Component '%s' did not accept command interfaces combination: \n%s", + component.get_name().c_str(), + interfaces_to_string(start_interfaces, stop_interfaces).c_str()); + ret = false; + } + } + catch (const std::exception & e) { RCUTILS_LOG_ERROR_NAMED( "resource_manager", - "Component '%s' did not accept command interfaces combination: \n%s", - component.get_name().c_str(), interfaces_to_string().c_str()); + "Exception occurred while preparing command mode switch for component '%s' for the " + "interfaces: \n %s : %s", + component.get_name().c_str(), + interfaces_to_string(start_interfaces, stop_interfaces).c_str(), e.what()); + ret = false; + } + catch (...) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", + "Unknown exception occurred while preparing command mode switch for component '%s' for " + "the interfaces: \n %s", + component.get_name().c_str(), + interfaces_to_string(start_interfaces, stop_interfaces).c_str()); ret = false; } } @@ -1202,13 +1406,37 @@ bool ResourceManager::perform_command_mode_switch( component.get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE || component.get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) { - if ( - return_type::OK != - component.perform_command_mode_switch(start_interfaces, stop_interfaces)) + try + { + if ( + return_type::OK != + component.perform_command_mode_switch(start_interfaces, stop_interfaces)) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Component '%s' could not perform switch", + component.get_name().c_str()); + ret = false; + } + } + catch (const std::exception & e) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", + "Exception occurred while performing command mode switch for component '%s' for the " + "interfaces: \n %s : %s", + component.get_name().c_str(), + interfaces_to_string(start_interfaces, stop_interfaces).c_str(), e.what()); + ret = false; + } + catch (...) { RCUTILS_LOG_ERROR_NAMED( - "resource_manager", "Component '%s' could not perform switch", - component.get_name().c_str()); + "resource_manager", + "Unknown exception occurred while performing command mode switch for component '%s' " + "for " + "the interfaces: \n %s", + component.get_name().c_str(), + interfaces_to_string(start_interfaces, stop_interfaces).c_str()); ret = false; } } @@ -1324,6 +1552,13 @@ return_type ResourceManager::set_component_state( return result; } +void ResourceManager::shutdown_async_components() +{ + resource_storage_->async_component_threads_.erase( + resource_storage_->async_component_threads_.begin(), + resource_storage_->async_component_threads_.end()); +} + // CM API: Called in "update"-thread HardwareReadWriteStatus ResourceManager::read( const rclcpp::Time & time, const rclcpp::Duration & period) @@ -1336,7 +1571,25 @@ HardwareReadWriteStatus ResourceManager::read( { for (auto & component : components) { - auto ret_val = component.read(time, period); + auto ret_val = return_type::OK; + try + { + ret_val = component.read(time, period); + } + catch (const std::exception & e) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Exception thrown durind read of the component '%s': %s", + component.get_name().c_str(), e.what()); + ret_val = return_type::ERROR; + } + catch (...) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Unknown exception thrown during read of the component '%s'", + component.get_name().c_str()); + ret_val = return_type::ERROR; + } if (ret_val == return_type::ERROR) { read_write_status.ok = false; @@ -1376,7 +1629,25 @@ HardwareReadWriteStatus ResourceManager::write( { for (auto & component : components) { - auto ret_val = component.write(time, period); + auto ret_val = return_type::OK; + try + { + ret_val = component.write(time, period); + } + catch (const std::exception & e) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Exception thrown during write of the component '%s': %s", + component.get_name().c_str(), e.what()); + ret_val = return_type::ERROR; + } + catch (...) + { + RCUTILS_LOG_ERROR_NAMED( + "resource_manager", "Unknown exception thrown during write of the component '%s'", + component.get_name().c_str()); + ret_val = return_type::ERROR; + } if (ret_val == return_type::ERROR) { read_write_status.ok = false; diff --git a/hardware_interface/test/test_component_parser.cpp b/hardware_interface/test/test_component_parser.cpp index 946fda7c15..6a0c11cf72 100644 --- a/hardware_interface/test/test_component_parser.cpp +++ b/hardware_interface/test/test_component_parser.cpp @@ -141,6 +141,8 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_one_interface) // Verify limits parsed from the URDF ASSERT_THAT(hardware_info.limits, SizeIs(2)); + ASSERT_THAT(hardware_info.soft_limits, SizeIs(1)); + char joint2_name[] = "joint2"; for (const auto & joint : {"joint1", "joint2"}) { EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits); @@ -152,6 +154,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_one_interface) EXPECT_TRUE(hardware_info.limits.at(joint).has_effort_limits); EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5)); EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5)); + if (strcmp(joint, joint2_name) == 0) + { + EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.0, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5)); + } } } @@ -193,6 +202,8 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_multi_interface // Verify limits parsed from the URDF ASSERT_THAT(hardware_info.limits, SizeIs(2)); + ASSERT_THAT(hardware_info.soft_limits, SizeIs(1)); + char joint2_name[] = "joint2"; for (const auto & joint : {"joint1", "joint2"}) { EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits); @@ -205,6 +216,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_multi_interface // effort and velocity limits won't change as they are above the main URDF hard limits EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5)); EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5)); + if (strcmp(joint, joint2_name) == 0) + { + EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.0, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5)); + } } } @@ -253,6 +271,8 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_robot_with_sens // Verify limits parsed from the URDF ASSERT_THAT(hardware_info.limits, SizeIs(2)); + ASSERT_THAT(hardware_info.soft_limits, SizeIs(1)); + char joint2_name[] = "joint2"; for (const auto & joint : {"joint1", "joint2"}) { EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits); @@ -264,6 +284,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_robot_with_sens EXPECT_TRUE(hardware_info.limits.at(joint).has_effort_limits); EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5)); EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5)); + if (strcmp(joint, joint2_name) == 0) + { + EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.0, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5)); + } } } @@ -297,6 +324,8 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_robot_with_exte // Verify limits parsed from the URDF ASSERT_THAT(hardware_info.limits, SizeIs(2)); + ASSERT_THAT(hardware_info.soft_limits, SizeIs(1)); + char joint2_name[] = "joint2"; for (const auto & joint : {"joint1", "joint2"}) { EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits); @@ -308,6 +337,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_robot_with_exte EXPECT_TRUE(hardware_info.limits.at(joint).has_effort_limits); EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5)); EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5)); + if (strcmp(joint, joint2_name) == 0) + { + EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.0, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5)); + } } hardware_info = control_hardware.at(1); @@ -322,6 +358,7 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_robot_with_exte EXPECT_EQ(hardware_info.sensors[0].type, "sensor"); EXPECT_EQ(hardware_info.sensors[0].parameters.at("frame_id"), "kuka_tcp"); ASSERT_THAT(hardware_info.limits, SizeIs(0)); + ASSERT_THAT(hardware_info.soft_limits, SizeIs(0)); } TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot) @@ -346,6 +383,7 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot EXPECT_EQ(hardware_info.joints[0].type, "joint"); // Verify limits parsed from the URDF ASSERT_THAT(hardware_info.limits, SizeIs(1)); + ASSERT_THAT(hardware_info.soft_limits, SizeIs(0)); for (const auto & joint : {"joint1"}) { EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits); @@ -373,6 +411,8 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot EXPECT_EQ(hardware_info.joints[0].type, "joint"); // Verify limits parsed from the URDF ASSERT_THAT(hardware_info.limits, SizeIs(1)); + ASSERT_THAT(hardware_info.soft_limits, SizeIs(1)); + char joint2_name[] = "joint2"; for (const auto & joint : {"joint2"}) { EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits); @@ -384,6 +424,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot EXPECT_TRUE(hardware_info.limits.at(joint).has_effort_limits); EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5)); EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5)); + if (strcmp(joint, joint2_name) == 0) + { + EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.0, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5)); + } } } @@ -421,6 +468,7 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot EXPECT_THAT(hardware_info.transmissions[0].actuators[0].name, "actuator1"); // Verify limits parsed from the URDF ASSERT_THAT(hardware_info.limits, SizeIs(1)); + ASSERT_THAT(hardware_info.soft_limits, SizeIs(0)); for (const auto & joint : {"joint1"}) { EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits); @@ -451,6 +499,8 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot EXPECT_EQ(hardware_info.joints[0].command_interfaces[0].max, "1"); // Verify limits parsed from the URDF ASSERT_THAT(hardware_info.limits, SizeIs(1)); + ASSERT_THAT(hardware_info.soft_limits, SizeIs(1)); + char joint2_name[] = "joint2"; for (const auto & joint : {"joint2"}) { EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits); @@ -461,6 +511,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot // effort and velocity limits won't change as they are above the main URDF hard limits EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5)); EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5)); + if (strcmp(joint, joint2_name) == 0) + { + EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.5, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5)); + } } hardware_info = control_hardware.at(2); @@ -481,6 +538,7 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot EXPECT_EQ(hardware_info.joints[0].state_interfaces[0].name, HW_IF_POSITION); // Verify limits parsed from the URDF ASSERT_THAT(hardware_info.limits, SizeIs(1)); + ASSERT_THAT(hardware_info.soft_limits, SizeIs(0)); for (const auto & joint : {"joint1"}) { EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits); @@ -511,6 +569,7 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot EXPECT_EQ(hardware_info.joints[0].state_interfaces[0].name, HW_IF_POSITION); // Verify limits parsed from the URDF ASSERT_THAT(hardware_info.limits, SizeIs(1)); + ASSERT_THAT(hardware_info.soft_limits, SizeIs(1)); for (const auto & joint : {"joint2"}) { EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits); @@ -521,6 +580,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot // effort and velocity limits won't change as they are above the main URDF hard limits EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5)); EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5)); + if (strcmp(joint, joint2_name) == 0) + { + EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.5, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5)); + } } } @@ -596,6 +662,7 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_sensor_only) EXPECT_EQ(hardware_info.sensors[1].state_interfaces[0].name, "image"); // There will be no limits as the ros2_control tag has only sensor info ASSERT_THAT(hardware_info.limits, SizeIs(0)); + ASSERT_THAT(hardware_info.soft_limits, SizeIs(0)); } TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_only) @@ -656,6 +723,7 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_only) // effort and velocity limits won't change as they are above the main URDF hard limits EXPECT_THAT(hardware_info.limits.at("joint1").max_velocity, DoubleNear(0.2, 1e-5)); EXPECT_THAT(hardware_info.limits.at("joint1").max_effort, DoubleNear(0.1, 1e-5)); + ASSERT_THAT(hardware_info.soft_limits, SizeIs(0)); } TEST_F(TestComponentParser, successfully_parse_locale_independent_double) @@ -727,6 +795,8 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_robot_with_gpio // Verify limits parsed from the URDF ASSERT_THAT(hardware_info.limits, SizeIs(2)); + ASSERT_THAT(hardware_info.soft_limits, SizeIs(1)); + char joint2_name[] = "joint2"; for (const auto & joint : {"joint1", "joint2"}) { // Position limits are limited in the ros2_control tag @@ -737,6 +807,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_robot_with_gpio EXPECT_TRUE(hardware_info.limits.at(joint).has_effort_limits); EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5)); EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5)); + if (strcmp(joint, joint2_name) == 0) + { + EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.0, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5)); + } } } @@ -905,6 +982,10 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_and_disabled_in EXPECT_FALSE(hardware_info.limits.at("joint2").has_jerk_limits); EXPECT_THAT(hardware_info.limits.at("joint2").max_velocity, DoubleNear(0.2, 1e-5)); EXPECT_THAT(hardware_info.limits.at("joint2").max_effort, DoubleNear(0.1, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at("joint2").max_position, DoubleNear(0.5, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at("joint2").min_position, DoubleNear(-1.5, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at("joint2").k_position, DoubleNear(10.0, 1e-5)); + EXPECT_THAT(hardware_info.soft_limits.at("joint2").k_velocity, DoubleNear(20.0, 1e-5)); EXPECT_TRUE(hardware_info.limits.at("joint3").has_position_limits); EXPECT_THAT(hardware_info.limits.at("joint3").min_position, DoubleNear(-M_PI, 1e-5)); @@ -1169,6 +1250,7 @@ TEST_F(TestComponentParser, successfully_parse_parameter_empty) // Verify limits parsed from the URDF ASSERT_THAT(hardware_info.limits, SizeIs(1)); + ASSERT_THAT(hardware_info.soft_limits, SizeIs(0)); for (const auto & joint : {"joint1"}) { EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits); diff --git a/hardware_interface_testing/CHANGELOG.rst b/hardware_interface_testing/CHANGELOG.rst index 13161cdd4c..cecc4e220c 100644 --- a/hardware_interface_testing/CHANGELOG.rst +++ b/hardware_interface_testing/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package hardware_interface_testing ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +4.10.0 (2024-05-08) +------------------- + 4.9.0 (2024-04-30) ------------------ * Component parser: Get mimic information from URDF (`#1256 `_) diff --git a/hardware_interface_testing/package.xml b/hardware_interface_testing/package.xml index df6a035c6b..737d228df3 100644 --- a/hardware_interface_testing/package.xml +++ b/hardware_interface_testing/package.xml @@ -1,7 +1,7 @@ hardware_interface_testing - 4.9.0 + 4.10.0 ros2_control hardware interface testing Bence Magyar Denis Štogl diff --git a/joint_limits/CHANGELOG.rst b/joint_limits/CHANGELOG.rst index 2bf49a13ec..dba0758112 100644 --- a/joint_limits/CHANGELOG.rst +++ b/joint_limits/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package joint_limits ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +4.10.0 (2024-05-08) +------------------- + 4.9.0 (2024-04-30) ------------------ diff --git a/joint_limits/CMakeLists.txt b/joint_limits/CMakeLists.txt index f8a45f8864..ef0aab6ee5 100644 --- a/joint_limits/CMakeLists.txt +++ b/joint_limits/CMakeLists.txt @@ -16,6 +16,7 @@ set(THIS_PACKAGE_INCLUDE_DEPENDS ) find_package(ament_cmake REQUIRED) +find_package(ament_cmake_gen_version_h REQUIRED) find_package(backward_ros REQUIRED) foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS}) find_package(${Dependency} REQUIRED) @@ -145,4 +146,5 @@ install(TARGETS ament_export_targets(export_joint_limits HAS_LIBRARY_TARGET) ament_export_dependencies(${THIS_PACKAGE_INCLUDE_DEPENDS}) ament_package() -ament_generate_version_header(${PROJECT_NAME}) +# TODO(anyone) uncomment if https://github.com/ament/ament_cmake/pull/526 is merged +# ament_generate_version_header(${PROJECT_NAME}) diff --git a/joint_limits/package.xml b/joint_limits/package.xml index 3a7fd9f17f..86b1498766 100644 --- a/joint_limits/package.xml +++ b/joint_limits/package.xml @@ -1,6 +1,6 @@ joint_limits - 4.9.0 + 4.10.0 Interfaces for handling of joint limits for controllers or hardware. Bence Magyar @@ -13,7 +13,8 @@ https://github.com/ros-controls/ros2_control ament_cmake - ament_cmake_gen_version_h + + backward_ros pluginlib @@ -23,6 +24,7 @@ urdf trajectory_msgs + launch_ros ament_cmake_gmock ament_cmake_gtest generate_parameter_library diff --git a/ros2_control-not-released.jazzy.repos b/ros2_control-not-released.jazzy.repos new file mode 100644 index 0000000000..56f46b6f79 --- /dev/null +++ b/ros2_control-not-released.jazzy.repos @@ -0,0 +1 @@ +repositories: diff --git a/ros2_control.jazzy.repos b/ros2_control.jazzy.repos new file mode 100644 index 0000000000..c93d8f4ef6 --- /dev/null +++ b/ros2_control.jazzy.repos @@ -0,0 +1,9 @@ +repositories: + ros-controls/realtime_tools: + type: git + url: https://github.com/ros-controls/realtime_tools.git + version: master + ros-controls/control_msgs: + type: git + url: https://github.com/ros-controls/control_msgs.git + version: master diff --git a/ros2_control/CHANGELOG.rst b/ros2_control/CHANGELOG.rst index aa93e8cc1b..a6df0c09e3 100644 --- a/ros2_control/CHANGELOG.rst +++ b/ros2_control/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package ros2_control ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +4.10.0 (2024-05-08) +------------------- + 4.9.0 (2024-04-30) ------------------ diff --git a/ros2_control/package.xml b/ros2_control/package.xml index acb69b2069..1fa309643f 100644 --- a/ros2_control/package.xml +++ b/ros2_control/package.xml @@ -1,7 +1,7 @@ ros2_control - 4.9.0 + 4.10.0 Metapackage for ROS2 control related packages Bence Magyar Denis Štogl diff --git a/ros2_control_test_assets/CHANGELOG.rst b/ros2_control_test_assets/CHANGELOG.rst index b3ab824fea..4057ceaa9e 100644 --- a/ros2_control_test_assets/CHANGELOG.rst +++ b/ros2_control_test_assets/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package ros2_control_test_assets ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +4.10.0 (2024-05-08) +------------------- +* Parse URDF joint hard limits into the HardwareInfo structure (`#1472 `_) +* Contributors: Sai Kishor Kothakota + 4.9.0 (2024-04-30) ------------------ * Component parser: Get mimic information from URDF (`#1256 `_) diff --git a/ros2_control_test_assets/include/ros2_control_test_assets/descriptions.hpp b/ros2_control_test_assets/include/ros2_control_test_assets/descriptions.hpp index f5f668fd12..31e630059b 100644 --- a/ros2_control_test_assets/include/ros2_control_test_assets/descriptions.hpp +++ b/ros2_control_test_assets/include/ros2_control_test_assets/descriptions.hpp @@ -86,6 +86,7 @@ const auto urdf_head = + diff --git a/ros2_control_test_assets/package.xml b/ros2_control_test_assets/package.xml index fe862846c4..a76e1109a8 100644 --- a/ros2_control_test_assets/package.xml +++ b/ros2_control_test_assets/package.xml @@ -2,7 +2,7 @@ ros2_control_test_assets - 4.9.0 + 4.10.0 The package provides shared test resources for ros2_control stack Bence Magyar diff --git a/ros2controlcli/CHANGELOG.rst b/ros2controlcli/CHANGELOG.rst index 16946acdce..84a64c1fd8 100644 --- a/ros2controlcli/CHANGELOG.rst +++ b/ros2controlcli/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package ros2controlcli ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +4.10.0 (2024-05-08) +------------------- + 4.9.0 (2024-04-30) ------------------ * [CI] Specify runner/container images and codecov for joint_limits (`#1504 `_) diff --git a/ros2controlcli/package.xml b/ros2controlcli/package.xml index fddcd2a0b4..8e2b02b266 100644 --- a/ros2controlcli/package.xml +++ b/ros2controlcli/package.xml @@ -2,7 +2,7 @@ ros2controlcli - 4.9.0 + 4.10.0 The ROS 2 command line tools for ROS2 Control. diff --git a/ros2controlcli/setup.py b/ros2controlcli/setup.py index 19fb09df99..d3ce54204e 100644 --- a/ros2controlcli/setup.py +++ b/ros2controlcli/setup.py @@ -19,7 +19,7 @@ setup( name=package_name, - version="4.9.0", + version="4.10.0", packages=find_packages(exclude=["test"]), data_files=[ ("share/" + package_name, ["package.xml"]), diff --git a/rqt_controller_manager/CHANGELOG.rst b/rqt_controller_manager/CHANGELOG.rst index 1691c2c8d5..33660cc16d 100644 --- a/rqt_controller_manager/CHANGELOG.rst +++ b/rqt_controller_manager/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rqt_controller_manager ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +4.10.0 (2024-05-08) +------------------- + 4.9.0 (2024-04-30) ------------------ diff --git a/rqt_controller_manager/package.xml b/rqt_controller_manager/package.xml index 3c0e5ce652..c722043890 100644 --- a/rqt_controller_manager/package.xml +++ b/rqt_controller_manager/package.xml @@ -2,7 +2,7 @@ rqt_controller_manager - 4.9.0 + 4.10.0 Graphical frontend for interacting with the controller manager. Bence Magyar Denis Štogl diff --git a/rqt_controller_manager/setup.py b/rqt_controller_manager/setup.py index 107981322a..ae79268335 100644 --- a/rqt_controller_manager/setup.py +++ b/rqt_controller_manager/setup.py @@ -20,7 +20,7 @@ setup( name=package_name, - version="4.9.0", + version="4.10.0", packages=[package_name], data_files=[ ("share/ament_index/resource_index/packages", ["resource/" + package_name]), diff --git a/transmission_interface/CHANGELOG.rst b/transmission_interface/CHANGELOG.rst index 94e13cd793..8ac4224337 100644 --- a/transmission_interface/CHANGELOG.rst +++ b/transmission_interface/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package transmission_interface ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +4.10.0 (2024-05-08) +------------------- + 4.9.0 (2024-04-30) ------------------ * rosdoc2 for transmission_interface (`#1496 `_) diff --git a/transmission_interface/CMakeLists.txt b/transmission_interface/CMakeLists.txt index 98dcdcd192..185fb32d3a 100644 --- a/transmission_interface/CMakeLists.txt +++ b/transmission_interface/CMakeLists.txt @@ -13,6 +13,7 @@ set(THIS_PACKAGE_INCLUDE_DEPENDS ) find_package(ament_cmake REQUIRED) +find_package(ament_cmake_gen_version_h REQUIRED) foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS}) find_package(${Dependency} REQUIRED) endforeach() diff --git a/transmission_interface/package.xml b/transmission_interface/package.xml index 36bbbd2a38..83da85a2ef 100644 --- a/transmission_interface/package.xml +++ b/transmission_interface/package.xml @@ -2,7 +2,7 @@ transmission_interface - 4.9.0 + 4.10.0 transmission_interface contains data structures for representing mechanical transmissions, methods for propagating values between actuator and joint spaces and tooling to support this. Bence Magyar Denis Štogl