├── .clang-format ├── .codespellignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question.md ├── dependabot.yaml └── workflows │ ├── ci-format.yml │ ├── ci-ros-lint.yml.backup │ ├── docker.yml │ ├── humble.yml │ ├── humble_documentation.yml │ ├── iron.yml │ ├── iron_documentation.yml │ ├── prerelease-rolling.yml │ ├── rolling.yml │ └── rolling_documentation.yml ├── .gitignore ├── .gitlab-ci.yml ├── .pre-commit-config.yaml ├── Dockerfile ├── README.md ├── canopen ├── CHANGELOG.rst ├── CMakeLists.txt ├── LICENSE ├── doxygen │ └── Doxyfile ├── package.xml └── sphinx │ ├── Makefile │ ├── _static │ └── css │ │ └── custom.css │ ├── application │ ├── prbt_robot.rst │ └── trinamic.rst │ ├── conf.py │ ├── developers-guide │ ├── architecture.rst │ ├── design-objectives.rst │ ├── new-device-manager.rst │ ├── new-driver.rst │ ├── new-master.rst │ └── overview.rst │ ├── images │ ├── configuration-flow.png │ ├── device-manager-usage.png │ ├── device-manager.png │ ├── prbt_rviz2.png │ ├── ros2-canopen-logo.png │ └── system-interface.png │ ├── index.rst │ ├── make.bat │ ├── quickstart │ ├── examples.rst │ ├── installation.rst │ └── operation.rst │ ├── software-tests │ ├── proxy-driver-test.rst │ └── ros2_control_system-test.rst │ └── user-guide │ ├── cia402-driver.rst │ ├── configuration.rst │ ├── how-to-create-a-cia301-system.rst │ ├── how-to-create-a-configuration.rst │ ├── how-to-create-a-robot-system.rst │ ├── master.rst │ ├── operation.rst │ ├── operation │ ├── managed-service-interface.rst │ ├── ros2-control-interface.rst │ └── service-interface.rst │ └── proxy-driver.rst ├── canopen_402_driver ├── CHANGELOG.rst ├── CMakeLists.txt ├── LICENSE ├── include │ └── canopen_402_driver │ │ ├── base.hpp │ │ ├── cia402_driver.hpp │ │ ├── command.hpp │ │ ├── default_homing_mode.hpp │ │ ├── homing_mode.hpp │ │ ├── lifecycle_cia402_driver.hpp │ │ ├── mode.hpp │ │ ├── mode_forward_helper.hpp │ │ ├── mode_target_helper.hpp │ │ ├── motor.hpp │ │ ├── node_interfaces │ │ ├── node_canopen_402_driver.hpp │ │ └── node_canopen_402_driver_impl.hpp │ │ ├── profiled_position_mode.hpp │ │ ├── state.hpp │ │ ├── visibility_control.h │ │ └── word_accessor.hpp ├── package.xml ├── src │ ├── cia402_driver.cpp │ ├── command.cpp │ ├── default_homing_mode.cpp │ ├── lifecycle_cia402_driver.cpp │ ├── motor.cpp │ ├── node_interfaces │ │ └── node_canopen_402_driver.cpp │ └── state.cpp └── test │ ├── CMakeLists.txt │ ├── master.dcf │ └── test_driver_component.cpp ├── canopen_base_driver ├── CHANGELOG.rst ├── CMakeLists.txt ├── LICENSE ├── include │ └── canopen_base_driver │ │ ├── base_driver.hpp │ │ ├── diagnostic_collector.hpp │ │ ├── lely_driver_bridge.hpp │ │ ├── lifecycle_base_driver.hpp │ │ ├── node_interfaces │ │ ├── node_canopen_base_driver.hpp │ │ └── node_canopen_base_driver_impl.hpp │ │ └── visibility_control.h ├── package.xml ├── src │ ├── base_driver.cpp │ ├── lely_driver_bridge.cpp │ ├── lifecycle_base_driver.cpp │ └── node_interfaces │ │ └── node_canopen_base_driver.cpp └── test │ ├── CMakeLists.txt │ ├── master.dcf │ ├── test_base_driver_component.cpp │ └── test_node_canopen_base_driver_ros.cpp ├── canopen_core ├── CHANGELOG.rst ├── CMakeLists.txt ├── ConfigExtras.cmake ├── LICENSE ├── include │ └── canopen_core │ │ ├── configuration_manager.hpp │ │ ├── device_container.hpp │ │ ├── device_container_error.hpp │ │ ├── driver_error.hpp │ │ ├── driver_node.hpp │ │ ├── exchange.hpp │ │ ├── lifecycle_manager.hpp │ │ ├── master_error.hpp │ │ ├── master_node.hpp │ │ ├── node_interfaces │ │ ├── node_canopen_driver.hpp │ │ ├── node_canopen_driver_interface.hpp │ │ ├── node_canopen_master.hpp │ │ └── node_canopen_master_interface.hpp │ │ └── visibility_control.h ├── launch │ └── canopen.launch.py ├── package.xml ├── readme.md ├── scripts │ └── setup_vcan.sh ├── src │ ├── configuration_manager.cpp │ ├── device_container.cpp │ ├── device_container_error.cpp │ ├── device_container_node.cpp │ ├── driver_error.cpp │ ├── driver_node.cpp │ ├── lifecycle_manager.cpp │ ├── master_error.cpp │ ├── master_node.cpp │ └── node_interfaces │ │ ├── node_canopen_driver.cpp │ │ └── node_canopen_master.cpp └── test │ ├── CMakeLists.txt │ ├── bus_configs │ ├── bad_driver_duplicate.yml │ ├── bad_driver_no_driver.yml │ ├── bad_driver_no_id.yml │ ├── bad_driver_no_package.yml │ ├── bad_master_no_driver.yml │ ├── bad_master_no_id.yml │ ├── bad_master_no_package.yml │ ├── bad_no_master.yml │ ├── good_driver.yml │ ├── good_master.yml │ └── good_master_and_two_driver.yml │ ├── master.dcf │ ├── simple.eds │ ├── test_canopen_driver.cpp │ ├── test_canopen_master.cpp │ ├── test_device_container.cpp │ ├── test_errors.cpp │ ├── test_lifecycle_canopen_driver.cpp │ ├── test_lifecycle_canopen_master.cpp │ ├── test_lifecycle_manager.cpp │ ├── test_node_canopen_driver.cpp │ └── test_node_canopen_master.cpp ├── canopen_fake_slaves ├── CHANGELOG.rst ├── CMakeLists.txt ├── LICENSE ├── Readme.md ├── config │ ├── cia402_slave.eds │ └── simple_slave.eds ├── include │ └── canopen_fake_slaves │ │ ├── base_slave.hpp │ │ ├── basic_slave.hpp │ │ ├── cia402_slave.hpp │ │ └── motion_generator.hpp ├── launch │ ├── basic_slave.launch.py │ └── cia402_slave.launch.py ├── package.xml └── src │ ├── basic_slave.cpp │ ├── cia402_slave.cpp │ └── motion_generator.cpp ├── canopen_interfaces ├── CHANGELOG.rst ├── CMakeLists.txt ├── LICENSE ├── msg │ └── COData.msg ├── package.xml └── srv │ ├── COHeartbeatID.srv │ ├── CONmtID.srv │ ├── CONode.srv │ ├── CORead.srv │ ├── COReadID.srv │ ├── COTargetDouble.srv │ ├── COWrite.srv │ └── COWriteID.srv ├── canopen_master_driver ├── CHANGELOG.rst ├── CMakeLists.txt ├── LICENSE ├── include │ └── canopen_master_driver │ │ ├── lely_master_bridge.hpp │ │ ├── lifecycle_master_driver.hpp │ │ ├── master_driver.hpp │ │ ├── node_interfaces │ │ ├── node_canopen_basic_master.hpp │ │ └── node_canopen_basic_master_impl.hpp │ │ └── visibility_control.h ├── package.xml ├── src │ ├── lely_master_bridge.cpp │ ├── lifecycle_master_driver.cpp │ ├── master_driver.cpp │ └── node_interfaces │ │ └── node_canopen_basic_master.cpp └── test │ ├── CMakeLists.txt │ ├── master.dcf │ ├── test_master_driver_component.cpp │ ├── test_node_canopen_basic_master.cpp │ └── test_node_canopen_basic_master_ros.cpp ├── canopen_proxy_driver ├── CHANGELOG.rst ├── CMakeLists.txt ├── LICENSE ├── config │ ├── concurrency_test │ │ └── master.dcf │ ├── nmt_test │ │ └── master.dcf │ ├── pdo_test │ │ └── master.dcf │ └── sdo_test │ │ └── master.dcf ├── include │ └── canopen_proxy_driver │ │ ├── lifecycle_proxy_driver.hpp │ │ ├── node_interfaces │ │ ├── node_canopen_proxy_driver.hpp │ │ └── node_canopen_proxy_driver_impl.hpp │ │ ├── proxy_driver.hpp │ │ └── visibility_control.h ├── package.xml ├── readme.md ├── src │ ├── lifecycle_proxy_driver.cpp │ ├── node_interfaces │ │ └── node_canopen_proxy_driver.cpp │ └── proxy_driver.cpp └── test │ ├── CMakeLists.txt │ ├── master.dcf │ ├── test_driver_component.cpp │ └── test_node_interface.cpp ├── canopen_ros2_control ├── CHANGELOG.rst ├── CMakeLists.txt ├── LICENSE ├── canopen_ros2_control.xml ├── include │ └── canopen_ros2_control │ │ ├── canopen_system.hpp │ │ ├── cia402_data.hpp │ │ ├── cia402_system.hpp │ │ ├── helpers.hpp │ │ ├── robot_system.hpp │ │ └── visibility_control.h ├── package.xml ├── src │ ├── canopen_system.cpp │ ├── cia402_system.cpp │ └── robot_system.cpp └── test │ └── test_canopen_system.cpp ├── canopen_ros2_controllers ├── CHANGELOG.rst ├── CMakeLists.txt ├── LICENSE ├── canopen_ros2_controllers.xml ├── include │ └── canopen_ros2_controllers │ │ ├── canopen_proxy_controller.hpp │ │ ├── cia402_device_controller.hpp │ │ └── visibility_control.h ├── package.xml ├── src │ ├── canopen_proxy_controller.cpp │ └── cia402_device_controller.cpp └── test │ ├── test_canopen_proxy_controller.cpp │ ├── test_canopen_proxy_controller.hpp │ ├── test_load_canopen_proxy_controller.cpp │ ├── test_load_cia402_device_controller.cpp │ └── test_load_cia402_robot_controller.cpp ├── canopen_tests ├── CHANGELOG.rst ├── CMakeLists.txt ├── LICENSE ├── README.md ├── ROS_NAMESPACES.md ├── config │ ├── canopen_system │ │ ├── bus.yml │ │ ├── ros2_controllers.yaml │ │ └── simple.eds │ ├── cia402 │ │ ├── bus.yml │ │ └── cia402_slave.eds │ ├── cia402_diagnostics │ │ ├── bus.yml │ │ └── cia402_slave.eds │ ├── cia402_lifecycle │ │ ├── bus.yml │ │ └── cia402_slave.eds │ ├── cia402_namespaced_system │ │ ├── bus.yml │ │ ├── cia402_slave.eds │ │ └── ros2_controllers.yaml │ ├── cia402_system │ │ ├── bus.yml │ │ ├── cia402_slave.eds │ │ └── ros2_controllers.yaml │ ├── robot_control │ │ ├── bus.yml │ │ ├── cia402_slave.eds │ │ ├── prbt_0_1.dcf │ │ └── ros2_controllers.yaml │ ├── simple │ │ ├── bus.yml │ │ └── simple.eds │ ├── simple_diagnostics │ │ ├── bus.yml │ │ └── simple.eds │ └── simple_lifecycle │ │ ├── bus.yml │ │ └── simple.eds ├── launch │ ├── analyzers │ │ ├── cia402_diagnostic_analyzer.yaml │ │ └── proxy_diagnostic_analyzer.yaml │ ├── canopen_system.launch.py │ ├── cia402_diagnostics_setup.launch.py │ ├── cia402_lifecycle_setup.launch.py │ ├── cia402_namespaced_system.launch.py │ ├── cia402_setup.launch.py │ ├── cia402_system.launch.py │ ├── proxy_diagnostics_setup.launch.py │ ├── proxy_lifecycle_setup.launch.py │ ├── proxy_setup.launch.py │ ├── proxy_setup_namespaced.launch.py │ ├── robot_control_setup.launch.py │ └── view_urdf.launch.py ├── launch_tests │ ├── test_cia402_driver.py │ ├── test_proxy_driver.py │ ├── test_proxy_driver_namespaced.py │ ├── test_proxy_lifecycle_driver.py │ └── test_robot_control.py ├── package.xml ├── rviz │ └── robot_controller.rviz └── urdf │ ├── canopen_system │ ├── canopen_system.ros2_control.xacro │ └── canopen_system.urdf.xacro │ ├── cia402_system │ ├── cia402_system.ros2_control.xacro │ └── cia402_system.urdf.xacro │ └── robot_controller │ ├── robot_controller.macro.xacro │ ├── robot_controller.ros2_control.xacro │ └── robot_controller.urdf.xacro ├── canopen_utils ├── CHANGELOG.rst ├── LICENSE ├── canopen_utils │ ├── __init__.py │ ├── cyclic_tester.py │ ├── launch_test_node.py │ ├── simple_rpdo_tpdo_tester.py │ └── test_node.py ├── no_tests │ ├── _test_copyright.py │ ├── _test_flake8.py │ └── _test_pep257.py ├── package.xml ├── resource │ └── canopen_utils ├── setup.cfg └── setup.py └── lely_core_libraries ├── CHANGELOG.rst ├── CMakeLists.txt ├── LICENSE ├── cmake └── lely_core_libraries-extras.cmake ├── cogen ├── __init__.py └── cogen.py ├── package.xml ├── patches └── 0001-Fix-dcf-tools.patch └── setup.cfg /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | 5 | AccessModifierOffset: -2 6 | AlignAfterOpenBracket: AlwaysBreak 7 | BreakBeforeBraces: Allman 8 | ColumnLimit: 100 9 | ConstructorInitializerIndentWidth: 0 10 | ContinuationIndentWidth: 2 11 | IndentWidth: 2 12 | TabWidth: 2 13 | DerivePointerAlignment: false 14 | PointerAlignment: Middle 15 | ReflowComments: true 16 | IncludeBlocks: Preserve 17 | ... 18 | -------------------------------------------------------------------------------- /.codespellignore: -------------------------------------------------------------------------------- 1 | ons 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Logs** 20 | 21 | **Setup:** 22 | - Device: 23 | - OS: 24 | - ROS-Distro: 25 | - Branch/Commit: 26 | 27 | **Additional context** 28 | Add any other context about the problem here. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Logs** 14 | Add build or execution log for context. 15 | 16 | **Setup:** 17 | - Device: 18 | - OS: 19 | - ROS-Distro: 20 | - Branch/Commit: 21 | 22 | **Additional context** 23 | Add any other context about the problem here. 24 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | # Set update schedule for GitHub Actions 2 | 3 | version: 2 4 | updates: 5 | 6 | - package-ecosystem: "github-actions" 7 | directory: "/" 8 | schedule: 9 | # Check for updates to GitHub Actions every week 10 | interval: "weekly" 11 | -------------------------------------------------------------------------------- /.github/workflows/ci-format.yml: -------------------------------------------------------------------------------- 1 | # This is a format job. Pre-commit has a first-party GitHub action, so we use 2 | # that: https://github.com/pre-commit/action 3 | 4 | name: Pre-commit Format 5 | 6 | on: 7 | workflow_dispatch: 8 | pull_request: 9 | 10 | jobs: 11 | pre-commit: 12 | name: Format 13 | runs-on: ubuntu-22.04 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: actions/setup-python@v5 17 | with: 18 | python-version: 3.10.6 19 | - name: Install system hooks 20 | run: sudo apt update && sudo apt install -qq clang-format-14 cppcheck 21 | - uses: pre-commit/action@v3.0.1 22 | with: 23 | extra_args: --all-files --hook-stage manual 24 | -------------------------------------------------------------------------------- /.github/workflows/ci-ros-lint.yml.backup: -------------------------------------------------------------------------------- 1 | name: ROS Lint 2 | on: 3 | pull_request: 4 | 5 | jobs: 6 | ament_lint: 7 | name: ament_${{ matrix.linter }} 8 | runs-on: ubuntu-20.04 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | linter: [cppcheck, copyright, lint_cmake] 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: ros-tooling/setup-ros@v0.2 16 | - uses: ros-tooling/action-ros-lint@v0.1 17 | with: 18 | distribution: rolling 19 | linter: ${{ matrix.linter }} 20 | package-name: 21 | $PKG_NAME$ 22 | 23 | ament_lint_100: 24 | name: ament_${{ matrix.linter }} 25 | runs-on: ubuntu-20.04 26 | strategy: 27 | fail-fast: false 28 | matrix: 29 | linter: [cpplint] 30 | steps: 31 | - uses: actions/checkout@v3 32 | - uses: ros-tooling/setup-ros@v0.2 33 | - uses: ros-tooling/action-ros-lint@v0.1 34 | with: 35 | distribution: rolling 36 | linter: cpplint 37 | arguments: "--linelength=100 --filter=-whitespace/newline" 38 | package-name: 39 | ros2_canopen 40 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Create and publish a Docker image 2 | on: 3 | push: 4 | branches: ['master'] 5 | workflow_dispatch: 6 | 7 | env: 8 | REGISTRY: ghcr.io 9 | IMAGE_NAME: ${{ github.repository }} 10 | 11 | jobs: 12 | build-and-push-image: 13 | runs-on: ubuntu-22.04 14 | 15 | permissions: 16 | contents: read 17 | packages: write 18 | 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v4 22 | - name: Log in to the Container registry 23 | uses: docker/login-action@v3 24 | with: 25 | registry: ${{ env.REGISTRY }} 26 | username: ${{ github.actor }} 27 | password: ${{ secrets.GITHUB_TOKEN }} 28 | 29 | - name: Extract metadata (tags, labels) for Docker 30 | id: meta 31 | uses: docker/metadata-action@v5 32 | with: 33 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 34 | 35 | - name: Build and push Docker image 36 | uses: docker/build-push-action@v6 37 | with: 38 | context: . 39 | push: true 40 | tags: ${{ steps.meta.outputs.tags }} 41 | labels: ${{ steps.meta.outputs.labels }} 42 | -------------------------------------------------------------------------------- /.github/workflows/humble.yml: -------------------------------------------------------------------------------- 1 | name: Industrial CI Humble 2 | 3 | on: 4 | push: 5 | branches: [ humble ] 6 | pull_request: 7 | branches: [ humble ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | industrial_ci: 12 | name: ROS ${{ matrix.ROS_DISTRO }} (${{ matrix.ROS_REPO }}) 13 | runs-on: ubuntu-22.04 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | ROS_DISTRO: [humble] 18 | ROS_REPO: [testing] 19 | env: 20 | CCACHE_DIR: "${{ github.workspace }}/.ccache" 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: actions/cache@v4 24 | with: 25 | path: ${{ env.CCACHE_DIR }} 26 | key: ccache-${{ matrix.ROS_DISTRO }}-${{ matrix.ROS_REPO }}-${{github.run_id}} 27 | restore-keys: | 28 | ccache-${{ matrix.ROS_DISTRO }}-${{ matrix.ROS_REPO }}- 29 | - uses: 'ros-industrial/industrial_ci@master' 30 | env: 31 | BEFORE_INSTALL_TARGET_DEPENDENCIES: 'sudo apt-get update' 32 | ROS_DISTRO: ${{ matrix.ROS_DISTRO }} 33 | ROS_REPO: ${{ matrix.ROS_REPO }} 34 | -------------------------------------------------------------------------------- /.github/workflows/humble_documentation.yml: -------------------------------------------------------------------------------- 1 | name: Documentation Humble 2 | 3 | # Controls when the workflow will run 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the master branch 6 | push: 7 | branches: [ humble ] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 13 | jobs: 14 | # This workflow contains a single job called "build" 15 | build_documentation: 16 | # The type of runner that the job will run on 17 | runs-on: ubuntu-22.04 18 | 19 | # Steps represent a sequence of tasks that will be executed as part of the job 20 | steps: 21 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 22 | - uses: actions/checkout@v4 23 | 24 | - name: Install dependencies 25 | run: | 26 | sudo apt-get update -qq 27 | sudo apt-get install -y -qq doxygen graphviz plantuml 28 | pip install sphinx-rtd-theme 29 | pip install sphinxcontrib-plantuml 30 | pip install sphinx-mdinclude 31 | pip install breathe 32 | pip install exhale 33 | 34 | - name: Build doxygen 35 | run: | 36 | cd ./canopen/doxygen/ 37 | doxygen 38 | cd ../.. 39 | 40 | - name: Build documentation 41 | run: | 42 | cd ./canopen/sphinx/ 43 | make html 44 | cd ../.. 45 | 46 | - name: Create commit 47 | run: | 48 | git clone https://github.com/ros-industrial/ros2_canopen.git --branch gh-pages --single-branch gh-pages 49 | mkdir -p gh-pages/manual/humble/ 50 | mkdir -p gh-pages/api/humble/ 51 | cp -r ./canopen/sphinx/_build/html/* gh-pages/manual/humble/ 52 | cp -r ./canopen/doxygen/_build/html/* gh-pages/api/humble/ 53 | cd gh-pages 54 | git config --local user.email "action@github.com" 55 | git config --local user.name "GitHub Action" 56 | git add . 57 | git commit -m "Update documentation" -a || true 58 | 59 | - name: Push changes 60 | uses: ad-m/github-push-action@master 61 | with: 62 | branch: gh-pages 63 | directory: gh-pages 64 | github_token: ${{ secrets.GITHUB_TOKEN }} 65 | -------------------------------------------------------------------------------- /.github/workflows/iron.yml: -------------------------------------------------------------------------------- 1 | name: Industiral CI Iron 2 | 3 | on: 4 | push: 5 | branches: [ iron ] 6 | pull_request: 7 | branches: [ iron ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | industrial_ci: 12 | name: ROS ${{ matrix.ROS_DISTRO }} (${{ matrix.ROS_REPO }}) 13 | runs-on: ubuntu-22.04 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | ROS_DISTRO: [iron] 18 | ROS_REPO: [testing] 19 | env: 20 | CCACHE_DIR: "${{ github.workspace }}/.ccache" 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: actions/cache@v4 24 | with: 25 | path: ${{ env.CCACHE_DIR }} 26 | key: ccache-${{ matrix.ROS_DISTRO }}-${{ matrix.ROS_REPO }}-${{github.run_id}} 27 | restore-keys: | 28 | ccache-${{ matrix.ROS_DISTRO }}-${{ matrix.ROS_REPO }}- 29 | - uses: 'ros-industrial/industrial_ci@master' 30 | env: 31 | BEFORE_INSTALL_TARGET_DEPENDENCIES: 'sudo apt-get update' 32 | ROS_DISTRO: ${{ matrix.ROS_DISTRO }} 33 | ROS_REPO: ${{ matrix.ROS_REPO }} 34 | NOT_TEST_BUILD: 1 35 | -------------------------------------------------------------------------------- /.github/workflows/iron_documentation.yml: -------------------------------------------------------------------------------- 1 | name: Documentation Iron 2 | 3 | # Controls when the workflow will run 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the master branch 6 | push: 7 | branches: [ iron ] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 13 | jobs: 14 | # This workflow contains a single job called "build" 15 | build_documentation: 16 | # The type of runner that the job will run on 17 | runs-on: ubuntu-22.04 18 | 19 | # Steps represent a sequence of tasks that will be executed as part of the job 20 | steps: 21 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 22 | - uses: actions/checkout@v4 23 | 24 | - name: Install dependencies 25 | run: | 26 | sudo apt-get update -qq 27 | sudo apt-get install -y -qq doxygen graphviz plantuml 28 | pip install sphinx-rtd-theme 29 | pip install sphinxcontrib-plantuml 30 | pip install sphinx-mdinclude 31 | pip install breathe 32 | pip install exhale 33 | 34 | - name: Build doxygen 35 | run: | 36 | cd ./canopen/doxygen/ 37 | doxygen 38 | cd ../.. 39 | 40 | - name: Build documentation 41 | run: | 42 | cd ./canopen/sphinx/ 43 | make html 44 | cd ../.. 45 | 46 | - name: Create commit 47 | run: | 48 | git clone https://github.com/ros-industrial/ros2_canopen.git --branch gh-pages --single-branch gh-pages 49 | mkdir -p gh-pages/manual/iron/ 50 | mkdir -p gh-pages/api/iron/ 51 | cp -r ./canopen/sphinx/_build/html/* gh-pages/manual/iron/ 52 | cp -r ./canopen/doxygen/_build/html/* gh-pages/api/iron/ 53 | cd gh-pages 54 | git config --local user.email "action@github.com" 55 | git config --local user.name "GitHub Action" 56 | git add . 57 | git commit -m "Update documentation" -a || true 58 | 59 | - name: Push changes 60 | uses: ad-m/github-push-action@master 61 | with: 62 | branch: gh-pages 63 | directory: gh-pages 64 | github_token: ${{ secrets.GITHUB_TOKEN }} 65 | -------------------------------------------------------------------------------- /.github/workflows/prerelease-rolling.yml: -------------------------------------------------------------------------------- 1 | name: Prerelease-Test Rolling 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - "*" 8 | 9 | jobs: 10 | industrial_ci: 11 | name: ROS ${{ matrix.ROS_DISTRO }} (${{ matrix.ROS_REPO }}) 12 | runs-on: ubuntu-22.04 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | ROS_DISTRO: [rolling] 17 | ROS_REPO: [testing] 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: 'ros-industrial/industrial_ci@master' 21 | env: 22 | BEFORE_INSTALL_TARGET_DEPENDENCIES: 'sudo apt-get update' 23 | AFTER_BUILD_TARGET_WORKSPACE: 'set +u && COLCON_TRACE=0 AMENT_TRACE_SETUP_FILES=0 source /root/target_ws/install/setup.bash && set -u' 24 | ROS_DISTRO: ${{ matrix.ROS_DISTRO }} 25 | ROS_REPO: ${{ matrix.ROS_REPO }} 26 | PRERELEASE: true 27 | -------------------------------------------------------------------------------- /.github/workflows/rolling.yml: -------------------------------------------------------------------------------- 1 | name: Industrial CI Rolling 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | industrial_ci: 12 | name: ROS ${{ matrix.ROS_DISTRO }} (${{ matrix.ROS_REPO }}) 13 | runs-on: ubuntu-22.04 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | ROS_DISTRO: [rolling] 18 | ROS_REPO: [testing] 19 | env: 20 | CCACHE_DIR: "${{ github.workspace }}/.ccache" 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: actions/cache@v4 24 | with: 25 | path: ${{ env.CCACHE_DIR }} 26 | key: ccache-${{ matrix.ROS_DISTRO }}-${{ matrix.ROS_REPO }}-${{github.run_id}} 27 | restore-keys: | 28 | ccache-${{ matrix.ROS_DISTRO }}-${{ matrix.ROS_REPO }}- 29 | - uses: 'ros-industrial/industrial_ci@master' 30 | env: 31 | BEFORE_INSTALL_TARGET_DEPENDENCIES: 'sudo apt-get update' 32 | AFTER_BUILD_TARGET_WORKSPACE: 'set +u && COLCON_TRACE=0 AMENT_TRACE_SETUP_FILES=0 source /root/target_ws/install/setup.bash && set -u' 33 | ROS_DISTRO: ${{ matrix.ROS_DISTRO }} 34 | ROS_REPO: ${{ matrix.ROS_REPO }} 35 | PRERELEASE: false 36 | -------------------------------------------------------------------------------- /.github/workflows/rolling_documentation.yml: -------------------------------------------------------------------------------- 1 | name: Documentation Rolling 2 | 3 | # Controls when the workflow will run 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the master branch 6 | push: 7 | branches: [ master ] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 13 | jobs: 14 | # This workflow contains a single job called "build" 15 | build_documentation: 16 | # The type of runner that the job will run on 17 | runs-on: ubuntu-22.04 18 | 19 | # Steps represent a sequence of tasks that will be executed as part of the job 20 | steps: 21 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 22 | - uses: actions/checkout@v4 23 | 24 | - name: Install dependencies 25 | run: | 26 | sudo apt-get update -qq 27 | sudo apt-get install -y -qq doxygen graphviz plantuml 28 | pip install sphinx-rtd-theme 29 | pip install sphinxcontrib-plantuml 30 | pip install sphinx-mdinclude 31 | pip install breathe 32 | pip install exhale 33 | 34 | - name: Build doxygen 35 | run: | 36 | cd ./canopen/doxygen/ 37 | doxygen 38 | cd ../.. 39 | 40 | - name: Build documentation 41 | run: | 42 | cd ./canopen/sphinx/ 43 | make html 44 | cd ../.. 45 | 46 | - name: Create commit 47 | run: | 48 | git clone https://github.com/ros-industrial/ros2_canopen.git --branch gh-pages --single-branch gh-pages 49 | mkdir -p gh-pages/manual/rolling/ 50 | mkdir -p gh-pages/api/rolling/ 51 | cp -r ./canopen/sphinx/_build/html/* gh-pages/manual/rolling/ 52 | cp -r ./canopen/doxygen/_build/html/* gh-pages/api/rolling/ 53 | cd gh-pages 54 | git config --local user.email "action@github.com" 55 | git config --local user.name "GitHub Action" 56 | git add . 57 | git commit -m "Update documentation" -a || true 58 | 59 | - name: Push changes 60 | uses: ad-m/github-push-action@master 61 | with: 62 | branch: gh-pages 63 | directory: gh-pages 64 | github_token: ${{ secrets.GITHUB_TOKEN }} 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | **/doc_output/ 3 | **/__pycache__/ 4 | **/_build 5 | **/api 6 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - doc 3 | - build 4 | - dockerize 5 | 6 | doxygen: 7 | stage: doc 8 | image: ubuntu:latest 9 | before_script: 10 | - apt-get update -qq 11 | - apt-get install -y -qq doxygen graphviz 12 | script: 13 | - cd canopen/doxygen && doxygen 14 | artifacts: 15 | paths: 16 | - canopen/doxygen/doc_output/ 17 | tags: 18 | - asprunner 19 | 20 | sphinx: 21 | stage: doc 22 | image: sphinxdoc/sphinx:latest 23 | before_script: 24 | - apt-get update -qq 25 | - apt-get install -y -qq doxygen graphviz 26 | - pip install sphinx-rtd-theme 27 | - pip install sphinx-mdinclude 28 | - pip install breathe 29 | - pip install exhale 30 | script: 31 | - cd canopen/sphinx/ 32 | - make html 33 | artifacts: 34 | name: documentation 35 | paths: 36 | - canopen/sphinx/_build 37 | tags: 38 | - asprunner 39 | 40 | 41 | .build: 42 | stage: build 43 | before_script: 44 | - apt-get update -qq 45 | - apt-get install -y -qq git-core 46 | - git clone --quiet --depth 1 https://github.com/ros-industrial/industrial_ci .industrial_ci 47 | script: .industrial_ci/gitlab.sh 48 | services: [] 49 | variables: 50 | ISOLATION: shell 51 | tags: 52 | - asprunner 53 | 54 | galactic: 55 | extends: [.build] 56 | image: ros:galactic 57 | 58 | foxy: 59 | extends: [.build] 60 | image: ros:foxy 61 | 62 | 63 | dockerize-job: 64 | stage: dockerize 65 | image: docker:19.03.12 66 | services: 67 | - docker:19.03.12-dind 68 | variables: 69 | DOCKER_DRIVER: overlay2 70 | DOCKER_HOST: tcp://docker:2375 71 | DOCKER_TLS_CERTDIR: "" 72 | IMAGE_TAG: $CI_REGISTRY_IMAGE/ros2_canopen:$CI_COMMIT_REF_SLUG 73 | IMAGE_TAG_LATEST: $CI_REGISTRY_IMAGE/ros2_canopen:latest 74 | before_script: 75 | - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY 76 | - docker info 77 | script: 78 | - docker pull IMAGE_TAG_LATEST || true 79 | - docker build --cache-from IMAGE_TAG_LATEST --tag $IMAGE_TAG --tag $IMAGE_TAG_LATEST . 80 | - docker push $IMAGE_TAG 81 | - docker push $IMAGE_TAG_LATEST 82 | tags: 83 | - docker-build 84 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ros:rolling-ros-core 2 | 3 | 4 | RUN apt-get update \ 5 | && apt-get install -y \ 6 | python3-rosdep \ 7 | python3-argcomplete \ 8 | python3-colcon-common-extensions \ 9 | build-essential \ 10 | pkg-config \ 11 | python3-wheel 12 | 13 | WORKDIR /home/can_ws/src 14 | COPY . ros2_canopen 15 | 16 | WORKDIR /home/can_ws/ 17 | RUN . /opt/ros/rolling/setup.sh \ 18 | && rosdep init && rosdep update \ 19 | && rosdep install --from-paths src --ignore-src -r -y \ 20 | && colcon build \ 21 | && . install/setup.sh 22 | -------------------------------------------------------------------------------- /canopen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(canopen NONE) 3 | find_package(ament_cmake REQUIRED) 4 | ament_package() 5 | -------------------------------------------------------------------------------- /canopen/doxygen/Doxyfile: -------------------------------------------------------------------------------- 1 | # All settings not listed here will use the Doxygen default values. 2 | 3 | PROJECT_NAME = "ros2_canopen" 4 | PROJECT_NUMBER = master 5 | PROJECT_BRIEF = "C++ ROS CANopen Library" 6 | 7 | # Use these lines to include the generated logging.hpp (update install path if needed) 8 | INPUT = ../../canopen_core/include ../../canopen_base_driver/include ../../canopen_402_driver/include ../../canopen_proxy_driver/include 9 | 10 | RECURSIVE = YES 11 | OUTPUT_DIRECTORY = _build 12 | 13 | EXTRACT_ALL = YES 14 | SORT_MEMBER_DOCS = NO 15 | 16 | GENERATE_LATEX = NO 17 | GENERATE_XML = YES 18 | 19 | ENABLE_PREPROCESSING = YES 20 | 21 | DOT_GRAPH_MAX_NODES = 101 22 | 23 | FILE_PATTERNS = *.cpp *.h *.hpp *.md 24 | -------------------------------------------------------------------------------- /canopen/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | canopen 5 | 0.3.0 6 | Meta-package aggregating the ros2_canopen packages and documentation 7 | Christoph Hellmann Santos 8 | Vishnuprasad Prachandabhanu 9 | Apache-2.0 10 | 11 | ament_cmake 12 | canopen_interfaces 13 | canopen_core 14 | canopen_base_driver 15 | lely_core_libraries 16 | canopen_proxy_driver 17 | canopen_base_driver 18 | canopen_402_driver 19 | 20 | 21 | ament_cmake 22 | 23 | 24 | -------------------------------------------------------------------------------- /canopen/sphinx/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile clean 16 | 17 | clean: 18 | rm -rf "_doxygen/" "api/" 19 | @$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 20 | 21 | # Catch-all target: route all unknown targets to Sphinx using the new 22 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 23 | %: Makefile 24 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 25 | -------------------------------------------------------------------------------- /canopen/sphinx/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | 2 | .wy-table-responsive table td, 3 | .wy-table-responsive table th { 4 | white-space:normal; 5 | } 6 | 7 | tr { 8 | white-space: normal; 9 | } 10 | 11 | thead { 12 | white-space: normal; 13 | } 14 | 15 | table { 16 | white-space: normal; 17 | } 18 | -------------------------------------------------------------------------------- /canopen/sphinx/developers-guide/design-objectives.rst: -------------------------------------------------------------------------------- 1 | Design Objectives 2 | ====================== 3 | 4 | The ros_canopen stack had a number of drawbacks especially when it came 5 | to maintainablity and complexity. In order to address these drawbacks, we 6 | have decided to redesign the ros2_canopen stack completely with the following 7 | development goals. 8 | 9 | 10 | .. csv-table:: Development Objectives 11 | :header-rows: 1 12 | :class: longtable 13 | :delim: ; 14 | :widths: 1 2 15 | 16 | Objective; Description 17 | Understandability and Extendability; One major drawback of ros_canopen was that actually extending it with new drivers required to understand the complex stack with its different layers. 18 | Robust Parallel Requests; When multiple nodes are running on the same bus, it should be possible to make requests to the nodes concurrently from ROS2 and have the canopen master handle the sequencing. 19 | Easy Maintenance; Maintenance effort should be reduced as much as possible. Therefore, a clean and clear code structure and documentation is needed and only funcitonalities that are not already available from other high quality open source libraries should be self implemented. 20 | Enable controlling drives via ros2_control; A driver for CIA402 and a ros2_control interface need to be developed. 21 | Enable controlling drives via ros2 ervice interface; A driver for CIA402 and a service interface need to be developed. 22 | Enable proxy functionalities via ros2 interface; For debugging purposes a proxy driver should be developed, which enables sending and receiving CANopen objects via a ros2 interface. 23 | Good enough documentation; Write documentation for using and understanding the ros2_canopen stack. 24 | -------------------------------------------------------------------------------- /canopen/sphinx/developers-guide/new-device-manager.rst: -------------------------------------------------------------------------------- 1 | Creating your device manager 2 | ============================ 3 | -------------------------------------------------------------------------------- /canopen/sphinx/developers-guide/new-master.rst: -------------------------------------------------------------------------------- 1 | Creating a new master 2 | ===================== 3 | -------------------------------------------------------------------------------- /canopen/sphinx/developers-guide/overview.rst: -------------------------------------------------------------------------------- 1 | Overview 2 | ======== 3 | ros2_canopen provides nodes for interfacing with CANopen devices. The library builds upon the professional and open source 4 | lelycore canopen library as opposed to the previous ros_canopen stack. 5 | In ros2_canopen the bus configuration is simplified through the use of 6 | lelycore's configutaration toolchain. 7 | 8 | ros2_canopen contains a number of packages that serve different serve different purposes. 9 | 10 | * **canopen**: 11 | 12 | Meta-package for easy installation and contains overall documentation 13 | of the ros2_canopen stack. 14 | 15 | * **lely_core_libraries**: 16 | 17 | A colcon package wrapper for the lelycore canopen library, for convenient 18 | installation with rosdep. 19 | 20 | * **canopen_core**: 21 | 22 | Contains the core structures of the ros2_canopen stack such as the device manager 23 | and the master node and the driver node interface. 24 | 25 | * **canopen_base_driver**: 26 | 27 | This package contains the base implementation of a ROS2 CANopen device driver. It can base 28 | used by other drivers for easy extension. 29 | 30 | * **canopen_proxy_driver**: 31 | 32 | Contains an implementation of a proxy driver which simply forwards CANopen functionality 33 | for a specific device via ROS2 services and messages. 34 | 35 | * **canopen_402_driver**: 36 | 37 | Contains an implementation of the CIA402 profile for motion controllers and exposes 38 | the profiles functionalities via ROS2 services and messages. The implementation is 39 | copied from ros_canopen/canopen_402 and this package is licensed accordingly under 40 | GNU Lesser General Public License v3.0! 41 | -------------------------------------------------------------------------------- /canopen/sphinx/images/configuration-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-industrial/ros2_canopen/f144f280f8e3c3f47491ae15e7f44ece1781db1a/canopen/sphinx/images/configuration-flow.png -------------------------------------------------------------------------------- /canopen/sphinx/images/device-manager-usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-industrial/ros2_canopen/f144f280f8e3c3f47491ae15e7f44ece1781db1a/canopen/sphinx/images/device-manager-usage.png -------------------------------------------------------------------------------- /canopen/sphinx/images/device-manager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-industrial/ros2_canopen/f144f280f8e3c3f47491ae15e7f44ece1781db1a/canopen/sphinx/images/device-manager.png -------------------------------------------------------------------------------- /canopen/sphinx/images/prbt_rviz2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-industrial/ros2_canopen/f144f280f8e3c3f47491ae15e7f44ece1781db1a/canopen/sphinx/images/prbt_rviz2.png -------------------------------------------------------------------------------- /canopen/sphinx/images/ros2-canopen-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-industrial/ros2_canopen/f144f280f8e3c3f47491ae15e7f44ece1781db1a/canopen/sphinx/images/ros2-canopen-logo.png -------------------------------------------------------------------------------- /canopen/sphinx/images/system-interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-industrial/ros2_canopen/f144f280f8e3c3f47491ae15e7f44ece1781db1a/canopen/sphinx/images/system-interface.png -------------------------------------------------------------------------------- /canopen/sphinx/index.rst: -------------------------------------------------------------------------------- 1 | ROS2 CANopen Stack 2 | ================== 3 | 4 | This is the documentation of the ROS2 CANopen stack. 5 | 6 | .. toctree:: 7 | :maxdepth: 1 8 | :caption: Quickstart 9 | :glob: 10 | 11 | quickstart/installation 12 | quickstart/operation 13 | quickstart/examples 14 | 15 | .. toctree:: 16 | :maxdepth: 1 17 | :caption: User Guide 18 | :glob: 19 | 20 | user-guide/operation 21 | user-guide/configuration 22 | user-guide/master 23 | user-guide/proxy-driver 24 | user-guide/cia402-driver 25 | user-guide/how-to-create-a-configuration 26 | user-guide/how-to-create-a-cia301-system 27 | user-guide/how-to-create-a-robot-system 28 | 29 | 30 | .. toctree:: 31 | :maxdepth: 1 32 | :caption: Developer Guide 33 | :glob: 34 | 35 | developers-guide/design-objectives 36 | developers-guide/overview 37 | developers-guide/architecture 38 | developers-guide/new-driver 39 | developers-guide/new-master 40 | API Reference 41 | 42 | .. toctree:: 43 | :maxdepth: 1 44 | :caption: Software Tests 45 | :glob: 46 | 47 | software-tests/** 48 | 49 | .. toctree:: 50 | :maxdepth: 1 51 | :caption: Application Demos 52 | :glob: 53 | 54 | application/** 55 | -------------------------------------------------------------------------------- /canopen/sphinx/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.https://www.sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /canopen/sphinx/quickstart/examples.rst: -------------------------------------------------------------------------------- 1 | Running Examples 2 | ================ 3 | 4 | In order to tryout the library a few examples are provided in the ``canopen_tests`` directory. 5 | You can run them if you have :ref:`started the vcan0 interface `. 6 | 7 | Service Interface 8 | --------------------- 9 | 10 | .. code-block:: bash 11 | 12 | ros2 launch canopen_tests cia402_setup.launch.py 13 | 14 | 15 | Managed Service Interface 16 | ------------------------- 17 | 18 | .. code-block:: bash 19 | 20 | ros2 launch canopen_tests cia402_lifecycle_setup.launch.py 21 | 22 | ROS2 Control 23 | ------------ 24 | 25 | Proxy Setup 26 | ,,,,,,,,,,, 27 | 28 | .. code-block:: bash 29 | 30 | ros2 launch canopen_tests canopen_system.launch.py 31 | 32 | 33 | CiA402 Setup 34 | ,,,,,,,,,,,, 35 | 36 | .. code-block:: bash 37 | 38 | ros2 launch canopen_tests cia402_system.launch.py 39 | 40 | 41 | Robot Setup 42 | ,,,,,,,,,,,, 43 | 44 | .. code-block:: bash 45 | 46 | ros2 launch canopen_tests robot_control_setup.launch.py 47 | -------------------------------------------------------------------------------- /canopen/sphinx/quickstart/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | =============================== 3 | Clone ros2_canopen into your ROS2 workspace's source folder, install dependencies and 4 | build with colcon and your done. 5 | 6 | .. code-block:: console 7 | 8 | $ git clone https://github.com/ros-industrial/ros2_canopen.git 9 | $ cd .. 10 | $ rosdep install --from-paths src/ros2_canopen --ignore-src -r -y 11 | $ colcon build 12 | -------------------------------------------------------------------------------- /canopen/sphinx/quickstart/operation.rst: -------------------------------------------------------------------------------- 1 | Setup CAN Controller 2 | ==================== 3 | .. _quick-start-setup-can-controller: 4 | 5 | **Option 1**: Virtual CANController 6 | 7 | .. code-block:: console 8 | 9 | $ sudo modprobe vcan 10 | $ sudo ip link add dev vcan0 type vcan 11 | $ sudo ip link set vcan0 txqueuelen 1000 12 | $ sudo ip link set up vcan0 13 | 14 | **Option 2**: Peak CANController 15 | 16 | .. code-block:: console 17 | 18 | $ sudo modprobe peak_usb 19 | $ sudo ip link set can0 up type can bitrate 1000000 20 | $ sudo ip link set can0 txqueuelen 1000 21 | $ sudo ip link set up can0 22 | 23 | Bitrate depends on your bus and devices capabilities. 24 | 25 | **Option 3**: candleLight USB-CAN Adapter 26 | 27 | .. code-block:: console 28 | 29 | $ sudo modprobe gs_usb 30 | $ sudo ip link set can0 up type can bitrate 500000 31 | $ sudo ip link set can0 txqueuelen 1000 32 | $ sudo ip link set up can0 33 | 34 | Bitrate depends on your bus and devices capabilities. 35 | 36 | **Option 4**: Adapt these steps to other socketcan devices 37 | -------------------------------------------------------------------------------- /canopen/sphinx/software-tests/proxy-driver-test.rst: -------------------------------------------------------------------------------- 1 | Lifecycle Proxy Driver Test 2 | =========================== 3 | 4 | .. csv-table:: Testdetails 5 | :header: "Detail", "Information" 6 | :delim: ; 7 | 8 | Package; canopen_tests 9 | Test file; launch_tests/test_lifecycle_proxy_driver.py 10 | Description; Tests nmt, sdo and lifecycle of lifecycle proxy driver 11 | Prerequisites; vcan0 must be available 12 | 13 | Proxy Driver Test 14 | =========================== 15 | 16 | .. csv-table:: Testdetails 17 | :header: "Detail", "Information" 18 | :delim: ; 19 | 20 | Package; canopen_tests 21 | Test file; launch_tests/test_proxy_driver.py 22 | Description; Tests nmt, sdo and lifecycle of proxy driver 23 | Prerequisites; vcan0 must be available 24 | -------------------------------------------------------------------------------- /canopen/sphinx/user-guide/master.rst: -------------------------------------------------------------------------------- 1 | Master Driver 2 | ============= 3 | 4 | The master driver handles the creation of the necessary can interface and sets up a canopen event loop which drivers can hook onto. 5 | In addition, the node offers services for communicating with nodes via nmt and sdo. 6 | 7 | .. csv-table:: Master Drivers 8 | :header: Type, Package, Name 9 | :widths: 30, 20, 50 10 | 11 | lifecycle, canopen_master_driver, ros2_canopen::LifecycleMasterDriver 12 | simple, canopen_master_driver, ros2_canopen::MasterDriver 13 | 14 | 15 | Services 16 | -------- 17 | 18 | .. list-table:: 19 | :widths: 30 20 50 20 | :header-rows: 1 21 | 22 | * - Services 23 | - Type 24 | - Description 25 | * - ~/read_sdo 26 | - COReadID 27 | - Reads an SDO object specified by Index, Subindex and Datatype of the device with the specified nodeid. 28 | * - ~/write_sdo 29 | - COWriteID 30 | - Writes Data to an SDO object specified by Index, Subindex and Datatype on the device with the specified nodeid. 31 | * - ~/set_nmt 32 | - CONmtID 33 | - Sends the NMT command to the device with the specified nodeid 34 | -------------------------------------------------------------------------------- /canopen/sphinx/user-guide/operation.rst: -------------------------------------------------------------------------------- 1 | Operation 2 | ========= 3 | 4 | The ros2_canopen stack can be used in three different ways: 5 | 6 | * standard nodes container 7 | * managed nodes container 8 | * ros2_control system interface with standard nodes 9 | 10 | 11 | Simple nodes container 12 | """""""""""""""""""""""" 13 | The standard node container mode bundles the master and all slave driver nodes in one specialised 14 | container called device container. All nodes are simple ROS 2 nodes and expose a publish and subscribe 15 | as well as a service interfaces. Once the device container is started, all nodes are brought up 16 | and ready to be used. 17 | 18 | **Purpose**: 19 | The simple nodes container is thought for applications where the user needs a simple and 20 | easy to launch interface and does not need any realtime control capabilities as provided by 21 | ros2_control. 22 | 23 | Managed nodes container 24 | """""""""""""""""""""""""" 25 | The managed nodes container has the same properties as the standard nodes container. 26 | The exception is, that all nodes are lifecycle nodes and there is a special node called 27 | lifecycle manager. The user can use the lifecycle manager to control the lifecycle of 28 | all nodes in the container. 29 | 30 | **Purpose**: 31 | The managed nodes container is thought for applications where the user wants to have 32 | more runtime recovery options than killing and restarting the container. 33 | 34 | 35 | ROS 2 control system interface 36 | """""""""""""""""""""""""""""" 37 | The ros2_control interface is currently build on top of the simple nodes container. In 38 | addition to the standard nodes container the ros2_control system interface provides a 39 | hardware interface that can be used to control the devices on the bus. Currently, three 40 | different system interfaces are provided: 41 | 42 | * canopen_ros2_control/CANopenSystem 43 | * canopen_ros2_control/CIA402System 44 | * canopen_ros2_control/RobotSystem 45 | 46 | **Purpose**: 47 | The ROS 2 control system interfaces are thought for control applications that require 48 | low latencies. 49 | -------------------------------------------------------------------------------- /canopen/sphinx/user-guide/proxy-driver.rst: -------------------------------------------------------------------------------- 1 | Proxy Driver 2 | =================== 3 | A proxy driver which simply forwards CANopen functionality for a specific device via ROS2 services and messages. 4 | 5 | .. csv-table:: Proxy Drivers 6 | :header: Type, Package, Name 7 | :widths: 30, 20, 50 8 | 9 | lifecycle, canopen_proxy_driver, ros2_canopen::LifecycleProxyDriver 10 | simple, canopen_proxy_driver, ros2_canopen::ProxyDriver 11 | 12 | Services 13 | -------- 14 | 15 | .. list-table:: 16 | :widths: 30 20 50 17 | :header-rows: 1 18 | :align: left 19 | 20 | * - Services 21 | - Type 22 | - Description 23 | * - ~/nmt_reset_node 24 | - Trigger 25 | - Resets CANopen Device the Proxy Device Node manages. 26 | * - ~/sdo_read 27 | - CORead 28 | - Reads an SDO object from the specified index, subindex and datatype of the remote device. 29 | * - ~/sdo_write 30 | - COWrite 31 | - Writes data to an SDO object on the specified index, subindex and datatype of the remote device. 32 | 33 | 34 | Publishers 35 | ---------- 36 | 37 | .. list-table:: 38 | :widths: 30 20 50 39 | :header-rows: 1 40 | :align: left 41 | 42 | * - Topic 43 | - Type 44 | - Description 45 | * - ~/nmt_state 46 | - String 47 | - Publishes NMT state on change 48 | * - ~/rpdo 49 | - COData 50 | - Publishes received PDO objects on reception 51 | 52 | Subscribers 53 | ----------- 54 | 55 | .. list-table:: 56 | :widths: 30 20 50 57 | :header-rows: 1 58 | 59 | * - Topic 60 | - Type 61 | - Description 62 | * - ~/tpdo 63 | - COData 64 | - Writes received data to remote device if the specified object is RPDO mapped on remote device. 65 | -------------------------------------------------------------------------------- /canopen_402_driver/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package canopen_402_driver 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.2.9 (2024-04-16) 6 | ------------------ 7 | * Update the lely_core_libraries hash to the latest. 8 | * fix ci build error 9 | * Contributors: Vishnuprasad Prachandabhanu 10 | 11 | 0.3.0 (2024-12-12) 12 | ------------------ 13 | * Reformat using pre-commit 14 | * Implement position offsets 15 | * pre-commit fix 16 | * impl operation mode 17 | * Add cyclic torque mode to cia402 driver and robot system controller (`#293 `_) 18 | * Add base functions for switching to cyclic torque mode 19 | * Add cyclic torque mode as effort interface to robot_system controller 20 | * Add documentation about cyclic torque mode. 21 | --------- 22 | Co-authored-by: Christoph Hellmann Santos 23 | 24 | 0.2.12 (2024-04-22) 25 | ------------------- 26 | * 0.2.9 27 | * forthcoming 28 | * Merge pull request `#267 `_ from clalancette/clalancette/update-lely-core-hash 29 | Update the lely_core_libraries hash to the latest. 30 | * fix ci build error 31 | * Contributors: Vishnuprasad Prachandabhanu, ipa-vsp 32 | 33 | 0.2.8 (2024-01-19) 34 | ------------------ 35 | 36 | 0.2.7 (2023-06-30) 37 | ------------------ 38 | * Add missing license headers and activate ament_copyright 39 | * Fix maintainer naming 40 | * Contributors: Christoph Hellmann Santos 41 | 42 | 0.2.6 (2023-06-24) 43 | ------------------ 44 | 45 | 0.2.5 (2023-06-23) 46 | ------------------ 47 | 48 | 0.2.4 (2023-06-22) 49 | ------------------ 50 | 51 | 0.2.3 (2023-06-22) 52 | ------------------ 53 | 54 | 0.2.2 (2023-06-21) 55 | ------------------ 56 | 57 | 0.2.1 (2023-06-21) 58 | ------------------ 59 | * Fix boost/std placeholders ambiguity in older boost versions 60 | * Contributors: Christoph Hellmann Santos 61 | 62 | 0.2.0 (2023-06-14) 63 | ------------------ 64 | * Created package 65 | * Contributors: Borong Yuan, Błażej Sowa, Christoph Hellmann Santos, Denis Štogl, G.A. vd. Hoorn, Lovro, Vishnuprasad Prachandabhanu, livanov93 66 | -------------------------------------------------------------------------------- /canopen_402_driver/include/canopen_402_driver/default_homing_mode.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Christoph Hellmann Santos 2 | // Copyright 2014-2022 Authors of ros_canopen 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | // 17 | 18 | #ifndef DEFAULT_HOMING_MODE_HPP 19 | #define DEFAULT_HOMING_MODE_HPP 20 | #include 21 | #include "canopen_base_driver/lely_driver_bridge.hpp" 22 | #include "homing_mode.hpp" 23 | 24 | namespace ros2_canopen 25 | { 26 | 27 | class DefaultHomingMode : public HomingMode 28 | { 29 | const uint16_t index = 0x6098; 30 | std::shared_ptr driver; 31 | 32 | std::atomic execute_; 33 | 34 | std::mutex mutex_; 35 | std::condition_variable cond_; 36 | uint16_t status_; 37 | 38 | const int homing_timeout_seconds_; 39 | 40 | enum SW_masks 41 | { 42 | MASK_Reached = (1 << State402::SW_Target_reached), 43 | MASK_Attained = (1 << SW_Attained), 44 | MASK_Error = (1 << SW_Error), 45 | }; 46 | bool error(const std::string & msg) 47 | { 48 | execute_ = false; 49 | std::cout << msg << std::endl; 50 | return false; 51 | } 52 | 53 | public: 54 | DefaultHomingMode(std::shared_ptr driver, int homing_timeout_seconds) 55 | : homing_timeout_seconds_(homing_timeout_seconds) 56 | { 57 | this->driver = driver; 58 | } 59 | virtual bool start(); 60 | virtual bool read(const uint16_t & sw); 61 | virtual bool write(OpModeAccesser & cw); 62 | 63 | virtual bool executeHoming(); 64 | }; 65 | } // namespace ros2_canopen 66 | #endif // DEFAULT_HOMING_MODE_HPP 67 | -------------------------------------------------------------------------------- /canopen_402_driver/include/canopen_402_driver/homing_mode.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Christoph Hellmann Santos 2 | // Copyright 2014-2022 Authors of ros_canopen 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | // 17 | 18 | #ifndef HOMING_MODE_HPP 19 | #define HOMING_MODE_HPP 20 | #include "base.hpp" 21 | #include "mode.hpp" 22 | 23 | namespace ros2_canopen 24 | { 25 | class HomingMode : public Mode 26 | { 27 | protected: 28 | enum SW_bits 29 | { 30 | SW_Attained = State402::SW_Operation_mode_specific0, 31 | SW_Error = State402::SW_Operation_mode_specific1, 32 | }; 33 | enum CW_bits 34 | { 35 | CW_StartHoming = Command402::CW_Operation_mode_specific0, 36 | }; 37 | 38 | public: 39 | HomingMode() : Mode(MotorBase::Homing) {} 40 | virtual bool executeHoming() = 0; 41 | }; 42 | } // namespace ros2_canopen 43 | 44 | #endif // HOMING_MODE_HPP 45 | -------------------------------------------------------------------------------- /canopen_402_driver/include/canopen_402_driver/mode.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Christoph Hellmann Santos 2 | // Copyright 2014-2022 Authors of ros_canopen 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | // 17 | 18 | #ifndef CANOPEN_402_DRIVER_MODE_HPP 19 | #define CANOPEN_402_DRIVER_MODE_HPP 20 | 21 | #include 22 | #include 23 | #include "command.hpp" 24 | #include "state.hpp" 25 | #include "word_accessor.hpp" 26 | 27 | namespace ros2_canopen 28 | { 29 | class Mode 30 | { 31 | public: 32 | const uint16_t mode_id_; 33 | Mode(uint16_t id) : mode_id_(id) {} 34 | typedef WordAccessor< 35 | (1 << Command402::CW_Operation_mode_specific0) | 36 | (1 << Command402::CW_Operation_mode_specific1) | 37 | (1 << Command402::CW_Operation_mode_specific2) | (1 << Command402::CW_Operation_mode_specific3)> 38 | OpModeAccesser; 39 | virtual bool start() = 0; 40 | virtual bool read(const uint16_t & sw) = 0; 41 | virtual bool write(OpModeAccesser & cw) = 0; 42 | virtual bool setTarget(const double & val) { return false; } 43 | virtual ~Mode() {} 44 | }; 45 | typedef std::shared_ptr ModeSharedPtr; 46 | } // namespace ros2_canopen 47 | 48 | #endif // CANOPEN_402_DRIVER_MODE_HPP 49 | -------------------------------------------------------------------------------- /canopen_402_driver/include/canopen_402_driver/mode_forward_helper.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Christoph Hellmann Santos 2 | // Copyright 2014-2022 Authors of ros_canopen 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | // 17 | 18 | #ifndef MODE_FORWARD_HELPER_HPP 19 | #define MODE_FORWARD_HELPER_HPP 20 | 21 | #include 22 | #include 23 | #include "canopen_base_driver/lely_driver_bridge.hpp" 24 | #include "mode_target_helper.hpp" 25 | 26 | namespace ros2_canopen 27 | { 28 | 29 | template 30 | class ModeForwardHelper : public ModeTargetHelper 31 | { 32 | std::shared_ptr driver; 33 | 34 | public: 35 | ModeForwardHelper(std::shared_ptr driver) : ModeTargetHelper(ID) 36 | { 37 | this->driver = driver; 38 | } 39 | virtual bool read(const uint16_t & sw) { return true; } 40 | virtual bool write(Mode::OpModeAccesser & cw) 41 | { 42 | if (this->hasTarget()) 43 | { 44 | cw = cw.get() | CW_MASK; 45 | 46 | driver->universal_set_value(OBJ, SUB, this->getTarget()); 47 | return true; 48 | } 49 | else 50 | { 51 | cw = cw.get() & ~CW_MASK; 52 | return false; 53 | } 54 | } 55 | }; 56 | } // namespace ros2_canopen 57 | 58 | #endif // MODE_FORWARD_HELPER_HPP 59 | -------------------------------------------------------------------------------- /canopen_402_driver/include/canopen_402_driver/state.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Christoph Hellmann Santos 2 | // Copyright 2014-2022 Authors of ros_canopen 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | // 17 | 18 | #ifndef CANOPEN_402_DRIVER_STATE_HPP 19 | #define CANOPEN_402_DRIVER_STATE_HPP 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | namespace ros2_canopen 26 | { 27 | class State402 28 | { 29 | public: 30 | enum StatusWord 31 | { 32 | SW_Ready_To_Switch_On = 0, 33 | SW_Switched_On = 1, 34 | SW_Operation_enabled = 2, 35 | SW_Fault = 3, 36 | SW_Voltage_enabled = 4, 37 | SW_Quick_stop = 5, 38 | SW_Switch_on_disabled = 6, 39 | SW_Warning = 7, 40 | SW_Manufacturer_specific0 = 8, 41 | SW_Remote = 9, 42 | SW_Target_reached = 10, 43 | SW_Internal_limit = 11, 44 | SW_Operation_mode_specific0 = 12, 45 | SW_Operation_mode_specific1 = 13, 46 | SW_Manufacturer_specific1 = 14, 47 | SW_Manufacturer_specific2 = 15 48 | }; 49 | enum InternalState 50 | { 51 | Unknown = 0, 52 | Start = 0, 53 | Not_Ready_To_Switch_On = 1, 54 | Switch_On_Disabled = 2, 55 | Ready_To_Switch_On = 3, 56 | Switched_On = 4, 57 | Operation_Enable = 5, 58 | Quick_Stop_Active = 6, 59 | Fault_Reaction_Active = 7, 60 | Fault = 8, 61 | }; 62 | InternalState getState(); 63 | InternalState read(uint16_t sw); 64 | bool waitForNewState( 65 | const std::chrono::steady_clock::time_point & abstime, InternalState & state); 66 | State402() : state_(Unknown) {} 67 | 68 | private: 69 | std::condition_variable cond_; 70 | std::mutex mutex_; 71 | InternalState state_; 72 | }; 73 | } // namespace ros2_canopen 74 | 75 | #endif // CANOPEN_402_DRIVER_STATE_HPP 76 | -------------------------------------------------------------------------------- /canopen_402_driver/include/canopen_402_driver/visibility_control.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Christoph Hellmann Santos 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | #ifndef CANOPEN_402_DRIVER__VISIBILITY_CONTROL_H_ 17 | #define CANOPEN_402_DRIVER__VISIBILITY_CONTROL_H_ 18 | 19 | // This logic was borrowed (then namespaced) from the examples on the gcc wiki: 20 | // https://gcc.gnu.org/wiki/Visibility 21 | 22 | #if defined _WIN32 || defined __CYGWIN__ 23 | #ifdef __GNUC__ 24 | #define CANOPEN_402_DRIVER_EXPORT __attribute__((dllexport)) 25 | #define CANOPEN_402_DRIVER_IMPORT __attribute__((dllimport)) 26 | #else 27 | #define CANOPEN_402_DRIVER_EXPORT __declspec(dllexport) 28 | #define CANOPEN_402_DRIVER_IMPORT __declspec(dllimport) 29 | #endif 30 | #ifdef CANOPEN_402_DRIVER_BUILDING_LIBRARY 31 | #define CANOPEN_402_DRIVER_PUBLIC CANOPEN_402_DRIVER_EXPORT 32 | #else 33 | #define CANOPEN_402_DRIVER_PUBLIC CANOPEN_402_DRIVER_IMPORT 34 | #endif 35 | #define CANOPEN_402_DRIVER_PUBLIC_TYPE CANOPEN_402_DRIVER_PUBLIC 36 | #define CANOPEN_402_DRIVER_LOCAL 37 | #else 38 | #define CANOPEN_402_DRIVER_EXPORT __attribute__((visibility("default"))) 39 | #define CANOPEN_402_DRIVER_IMPORT 40 | #if __GNUC__ >= 4 41 | #define CANOPEN_402_DRIVER_PUBLIC __attribute__((visibility("default"))) 42 | #define CANOPEN_402_DRIVER_LOCAL __attribute__((visibility("hidden"))) 43 | #else 44 | #define CANOPEN_402_DRIVER_PUBLIC 45 | #define CANOPEN_402_DRIVER_LOCAL 46 | #endif 47 | #define CANOPEN_402_DRIVER_PUBLIC_TYPE 48 | #endif 49 | 50 | #endif // CANOPEN_402_DRIVER__VISIBILITY_CONTROL_H_ 51 | -------------------------------------------------------------------------------- /canopen_402_driver/include/canopen_402_driver/word_accessor.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Christoph Hellmann Santos 2 | // Copyright 2014-2022 Authors of ros_canopen 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | // 17 | 18 | #ifndef WORD_ACCESSOR_HPP 19 | #define WORD_ACCESSOR_HPP 20 | 21 | #include 22 | 23 | namespace ros2_canopen 24 | { 25 | template 26 | class WordAccessor 27 | { 28 | uint16_t & word_; 29 | 30 | public: 31 | WordAccessor(uint16_t & word) : word_(word) {} 32 | bool set(uint8_t bit) 33 | { 34 | uint16_t val = MASK & (1 << bit); 35 | word_ |= val; 36 | return val; 37 | } 38 | bool reset(uint8_t bit) 39 | { 40 | uint16_t val = MASK & (1 << bit); 41 | word_ &= ~val; 42 | return val; 43 | } 44 | bool get(uint8_t bit) const { return word_ & (1 << bit); } 45 | uint16_t get() const { return word_ & MASK; } 46 | WordAccessor & operator=(const uint16_t & val) 47 | { 48 | word_ = (word_ & ~MASK) | (val & MASK); 49 | return *this; 50 | } 51 | }; 52 | } // namespace ros2_canopen 53 | 54 | #endif // WORD_ACCESSOR_HPP 55 | -------------------------------------------------------------------------------- /canopen_402_driver/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | canopen_402_driver 5 | 0.3.0 6 | Driiver for devices implementing CIA402 profile 7 | Christoph Hellmann Santos 8 | LGPL-v3 9 | 10 | ament_cmake_ros 11 | 12 | boost 13 | canopen_base_driver 14 | canopen_core 15 | canopen_interfaces 16 | canopen_proxy_driver 17 | rclcpp 18 | rclcpp_components 19 | rclcpp_lifecycle 20 | sensor_msgs 21 | std_srvs 22 | 23 | ament_lint_auto 24 | 25 | 26 | ament_cmake 27 | 28 | 29 | -------------------------------------------------------------------------------- /canopen_402_driver/src/cia402_driver.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Christoph Hellmann Santos 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | 17 | #include "canopen_402_driver/cia402_driver.hpp" 18 | 19 | using namespace ros2_canopen; 20 | 21 | Cia402Driver::Cia402Driver(rclcpp::NodeOptions node_options) : CanopenDriver(node_options) 22 | { 23 | node_canopen_402_driver_ = 24 | std::make_shared>(this); 25 | node_canopen_driver_ = 26 | std::static_pointer_cast(node_canopen_402_driver_); 27 | } 28 | 29 | #include "rclcpp_components/register_node_macro.hpp" 30 | RCLCPP_COMPONENTS_REGISTER_NODE(ros2_canopen::Cia402Driver) 31 | -------------------------------------------------------------------------------- /canopen_402_driver/src/lifecycle_cia402_driver.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Christoph Hellmann Santos 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | 17 | #include "canopen_402_driver/lifecycle_cia402_driver.hpp" 18 | 19 | using namespace ros2_canopen; 20 | 21 | LifecycleCia402Driver::LifecycleCia402Driver(rclcpp::NodeOptions node_options) 22 | : LifecycleCanopenDriver(node_options) 23 | { 24 | node_canopen_402_driver_ = 25 | std::make_shared>(this); 26 | node_canopen_driver_ = 27 | std::static_pointer_cast(node_canopen_402_driver_); 28 | } 29 | 30 | #include "rclcpp_components/register_node_macro.hpp" 31 | RCLCPP_COMPONENTS_REGISTER_NODE(ros2_canopen::LifecycleCia402Driver) 32 | -------------------------------------------------------------------------------- /canopen_402_driver/src/node_interfaces/node_canopen_402_driver.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Christoph Hellmann Santos 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | 17 | #include "canopen_402_driver/node_interfaces/node_canopen_402_driver.hpp" 18 | #include "canopen_402_driver/node_interfaces/node_canopen_402_driver_impl.hpp" 19 | #include "canopen_core/driver_error.hpp" 20 | 21 | using namespace ros2_canopen::node_interfaces; 22 | 23 | template class ros2_canopen::node_interfaces::NodeCanopen402Driver; 24 | template class ros2_canopen::node_interfaces::NodeCanopen402Driver; 25 | -------------------------------------------------------------------------------- /canopen_402_driver/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ament_add_gtest(test_driver_component 2 | test_driver_component.cpp 3 | ) 4 | ament_target_dependencies(test_driver_component 5 | ${dependencies} 6 | ) 7 | target_include_directories(test_driver_component PUBLIC 8 | ${CMAKE_CURRENT_SOURCE_DIR}/../include/ 9 | ) 10 | -------------------------------------------------------------------------------- /canopen_402_driver/test/test_driver_component.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Christoph Hellmann Santos 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "canopen_base_driver/node_interfaces/node_canopen_base_driver.hpp" 22 | #include "gtest/gtest.h" 23 | using namespace rclcpp_components; 24 | 25 | TEST(ComponentLoad, test_load_component_1) 26 | { 27 | rclcpp::init(0, nullptr); 28 | auto exec = std::make_shared(); 29 | auto manager = std::make_shared(exec); 30 | 31 | std::vector resources = 32 | manager->get_component_resources("canopen_402_driver"); 33 | 34 | EXPECT_EQ(2u, resources.size()); 35 | 36 | auto factory = manager->create_component_factory(resources[0]); 37 | auto instance_wrapper = 38 | factory->create_node_instance(rclcpp::NodeOptions().use_global_arguments(false)); 39 | 40 | rclcpp::shutdown(); 41 | } 42 | 43 | TEST(ComponentLoad, test_load_component_2) 44 | { 45 | rclcpp::init(0, nullptr); 46 | auto exec = std::make_shared(); 47 | auto manager = std::make_shared(exec); 48 | 49 | std::vector resources = 50 | manager->get_component_resources("canopen_402_driver"); 51 | 52 | EXPECT_EQ(2u, resources.size()); 53 | 54 | auto factory = manager->create_component_factory(resources[1]); 55 | auto instance_wrapper = 56 | factory->create_node_instance(rclcpp::NodeOptions().use_global_arguments(false)); 57 | 58 | rclcpp::shutdown(); 59 | } 60 | -------------------------------------------------------------------------------- /canopen_base_driver/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package canopen_base_driver 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.2.9 (2024-04-16) 6 | ------------------ 7 | * Add timeouts 8 | * Contributors: Vishnuprasad Prachandabhanu 9 | 10 | 0.3.0 (2024-12-12) 11 | ------------------ 12 | 13 | 0.2.12 (2024-04-22) 14 | ------------------- 15 | * Merge pull request `#280 `_ from ipa-vsp/fix/yaml-build-error 16 | Fix undefined reference to yaml library 17 | * pre-commit update 18 | * Merge pull request `#261 `_ from gsalinas/configurable-sdo-timeout 19 | Configurable SDO timeout 20 | * 0.2.9 21 | * forthcoming 22 | * Merge pull request `#220 `_ from ipa-vsp/feature/timeout-config 23 | Add timeouts 24 | * Set SDO timeout from node config. 25 | * Replace two more hardcoded timeouts. 26 | * Include timeout in documentation comment for LelyDriverBridge. 27 | * Make 20ms a default argument of the master & driver bridges. 28 | * change error to warn for testing 29 | * remove build warings 30 | * non transmit time from bus.yml 31 | * Contributors: Gerry Salinas, Vishnuprasad Prachandabhanu, ipa-vsp 32 | 33 | 0.2.8 (2024-01-19) 34 | ------------------ 35 | 36 | 0.2.7 (2023-06-30) 37 | ------------------ 38 | * Add missing license headers and activate ament_copyright 39 | * Fix maintainer naming 40 | * Update printed output in lely_driver_bridge.cpp 41 | * Contributors: Christoph Hellmann Santos, yos627 42 | 43 | 0.2.6 (2023-06-24) 44 | ------------------ 45 | 46 | 0.2.5 (2023-06-23) 47 | ------------------ 48 | 49 | 0.2.4 (2023-06-22) 50 | ------------------ 51 | 52 | 0.2.3 (2023-06-22) 53 | ------------------ 54 | 55 | 0.2.2 (2023-06-21) 56 | ------------------ 57 | 58 | 0.2.1 (2023-06-21) 59 | ------------------ 60 | * Fix base driver lifecyle 61 | * Fix node polling mode in base driver 62 | * Contributors: Błażej Sowa, Christoph Hellmann Santos 63 | 64 | 0.2.0 (2023-06-14) 65 | ------------------ 66 | * Created package 67 | * Contributors: Błażej Sowa, Christoph Hellmann Santos, Denis Štogl, Lovro, Vishnuprasad Prachandabhanu 68 | -------------------------------------------------------------------------------- /canopen_base_driver/include/canopen_base_driver/base_driver.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #ifndef CANOPEN_BASE_DRIVER__CANOPEN_BASE_DRIVER_HPP_ 15 | #define CANOPEN_BASE_DRIVER__CANOPEN_BASE_DRIVER_HPP_ 16 | 17 | #include "canopen_base_driver/node_interfaces/node_canopen_base_driver.hpp" 18 | #include "canopen_core/driver_node.hpp" 19 | 20 | namespace ros2_canopen 21 | { 22 | /** 23 | * @brief Abstract Class for a CANopen Device Node 24 | * 25 | * This class provides the base functionality for creating a 26 | * CANopen device node. It provides callbacks for nmt and rpdo. 27 | */ 28 | class BaseDriver : public ros2_canopen::CanopenDriver 29 | { 30 | std::shared_ptr> node_canopen_base_driver_; 31 | 32 | public: 33 | BaseDriver(rclcpp::NodeOptions node_options = rclcpp::NodeOptions()); 34 | 35 | void register_nmt_state_cb(std::function nmt_state_cb) 36 | { 37 | node_canopen_base_driver_->register_nmt_state_cb(nmt_state_cb); 38 | } 39 | 40 | void register_rpdo_cb(std::function rpdo_cb) 41 | { 42 | node_canopen_base_driver_->register_rpdo_cb(rpdo_cb); 43 | } 44 | }; 45 | } // namespace ros2_canopen 46 | 47 | #endif // CANOPEN_BASE_DRIVER__CANOPEN_BASE_DRIVER_HPP_ 48 | -------------------------------------------------------------------------------- /canopen_base_driver/include/canopen_base_driver/lifecycle_base_driver.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #ifndef CANOPEN_BASE_DRIVER__CANOPEN_BASE_DRIVER_HPP_ 15 | #define CANOPEN_BASE_DRIVER__CANOPEN_BASE_DRIVER_HPP_ 16 | 17 | #include "canopen_base_driver/node_interfaces/node_canopen_base_driver.hpp" 18 | #include "canopen_core/driver_node.hpp" 19 | 20 | namespace ros2_canopen 21 | { 22 | /** 23 | * @brief Lifecycle Base Driver 24 | * 25 | * A very basic driver without any functionality. 26 | * 27 | */ 28 | class LifecycleBaseDriver : public ros2_canopen::LifecycleCanopenDriver 29 | { 30 | std::shared_ptr> 31 | node_canopen_base_driver_; 32 | 33 | public: 34 | LifecycleBaseDriver(rclcpp::NodeOptions node_options = rclcpp::NodeOptions()); 35 | 36 | void register_nmt_state_cb(std::function nmt_state_cb) 37 | { 38 | node_canopen_base_driver_->register_nmt_state_cb(nmt_state_cb); 39 | } 40 | 41 | void register_rpdo_cb(std::function rpdo_cb) 42 | { 43 | node_canopen_base_driver_->register_rpdo_cb(rpdo_cb); 44 | } 45 | }; 46 | } // namespace ros2_canopen 47 | 48 | #endif // CANOPEN_BASE_DRIVER__CANOPEN_BASE_DRIVER_HPP_ 49 | -------------------------------------------------------------------------------- /canopen_base_driver/include/canopen_base_driver/visibility_control.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef CANOPEN_BASE_DRIVER__VISIBILITY_CONTROL_H_ 16 | #define CANOPEN_BASE_DRIVER__VISIBILITY_CONTROL_H_ 17 | 18 | // This logic was borrowed (then namespaced) from the examples on the gcc wiki: 19 | // https://gcc.gnu.org/wiki/Visibility 20 | 21 | #if defined _WIN32 || defined __CYGWIN__ 22 | #ifdef __GNUC__ 23 | #define CANOPEN_BASE_DRIVER_EXPORT __attribute__((dllexport)) 24 | #define CANOPEN_BASE_DRIVER_IMPORT __attribute__((dllimport)) 25 | #else 26 | #define CANOPEN_BASE_DRIVER_EXPORT __declspec(dllexport) 27 | #define CANOPEN_BASE_DRIVER_IMPORT __declspec(dllimport) 28 | #endif 29 | #ifdef CANOPEN_BASE_DRIVER_BUILDING_LIBRARY 30 | #define CANOPEN_BASE_DRIVER_PUBLIC CANOPEN_BASE_DRIVER_EXPORT 31 | #else 32 | #define CANOPEN_BASE_DRIVER_PUBLIC CANOPEN_BASE_DRIVER_IMPORT 33 | #endif 34 | #define CANOPEN_BASE_DRIVER_PUBLIC_TYPE CANOPEN_BASE_DRIVER_PUBLIC 35 | #define CANOPEN_BASE_DRIVER_LOCAL 36 | #else 37 | #define CANOPEN_BASE_DRIVER_EXPORT __attribute__((visibility("default"))) 38 | #define CANOPEN_BASE_DRIVER_IMPORT 39 | #if __GNUC__ >= 4 40 | #define CANOPEN_BASE_DRIVER_PUBLIC __attribute__((visibility("default"))) 41 | #define CANOPEN_BASE_DRIVER_LOCAL __attribute__((visibility("hidden"))) 42 | #else 43 | #define CANOPEN_BASE_DRIVER_PUBLIC 44 | #define CANOPEN_BASE_DRIVER_LOCAL 45 | #endif 46 | #define CANOPEN_BASE_DRIVER_PUBLIC_TYPE 47 | #endif 48 | 49 | #endif // CANOPEN_BASE_DRIVER__VISIBILITY_CONTROL_H_ 50 | -------------------------------------------------------------------------------- /canopen_base_driver/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | canopen_base_driver 5 | 0.3.0 6 | Library containing abstract CANopen driver class for ros2_canopen 7 | Christoph Hellmann Santos 8 | Apache-2.0 9 | 10 | ament_cmake_ros 11 | 12 | canopen_core 13 | canopen_interfaces 14 | lely_core_libraries 15 | rclcpp 16 | rclcpp_components 17 | rclcpp_lifecycle 18 | std_msgs 19 | std_srvs 20 | boost 21 | diagnostic_updater 22 | 23 | ament_lint_auto 24 | 25 | 26 | ament_cmake 27 | 28 | 29 | -------------------------------------------------------------------------------- /canopen_base_driver/src/base_driver.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "canopen_base_driver/base_driver.hpp" 16 | 17 | using namespace ros2_canopen; 18 | 19 | BaseDriver::BaseDriver(rclcpp::NodeOptions node_options) : CanopenDriver(node_options) 20 | { 21 | node_canopen_base_driver_ = 22 | std::make_shared>(this); 23 | node_canopen_driver_ = std::static_pointer_cast( 24 | node_canopen_base_driver_); 25 | } 26 | 27 | #include "rclcpp_components/register_node_macro.hpp" 28 | RCLCPP_COMPONENTS_REGISTER_NODE(ros2_canopen::BaseDriver) 29 | -------------------------------------------------------------------------------- /canopen_base_driver/src/lifecycle_base_driver.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include "canopen_base_driver/lifecycle_base_driver.hpp" 15 | 16 | using namespace ros2_canopen; 17 | 18 | LifecycleBaseDriver::LifecycleBaseDriver(rclcpp::NodeOptions node_options) 19 | : LifecycleCanopenDriver(node_options) 20 | { 21 | node_canopen_base_driver_ = 22 | std::make_shared>(this); 23 | node_canopen_driver_ = std::static_pointer_cast( 24 | node_canopen_base_driver_); 25 | } 26 | 27 | #include "rclcpp_components/register_node_macro.hpp" 28 | RCLCPP_COMPONENTS_REGISTER_NODE(ros2_canopen::LifecycleBaseDriver) 29 | -------------------------------------------------------------------------------- /canopen_base_driver/src/node_interfaces/node_canopen_base_driver.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "canopen_base_driver/node_interfaces/node_canopen_base_driver.hpp" 16 | #include "canopen_base_driver/node_interfaces/node_canopen_base_driver_impl.hpp" 17 | #include "canopen_core/driver_error.hpp" 18 | 19 | using namespace ros2_canopen::node_interfaces; 20 | 21 | template class ros2_canopen::node_interfaces::NodeCanopenBaseDriver; 22 | template class ros2_canopen::node_interfaces::NodeCanopenBaseDriver< 23 | rclcpp_lifecycle::LifecycleNode>; 24 | -------------------------------------------------------------------------------- /canopen_base_driver/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ament_add_gtest(test_node_canopen_base_driver_ros 2 | test_node_canopen_base_driver_ros.cpp 3 | ) 4 | ament_target_dependencies(test_node_canopen_base_driver_ros 5 | ${dependencies} 6 | ) 7 | target_include_directories(test_node_canopen_base_driver_ros PUBLIC 8 | ${CMAKE_CURRENT_SOURCE_DIR}/../include/ 9 | ) 10 | target_link_libraries(test_node_canopen_base_driver_ros 11 | node_canopen_base_driver 12 | ) 13 | 14 | 15 | 16 | 17 | ament_add_gtest(test_base_driver_component 18 | test_base_driver_component.cpp 19 | ) 20 | ament_target_dependencies(test_base_driver_component 21 | ${dependencies} 22 | ) 23 | target_include_directories(test_base_driver_component PUBLIC 24 | ${CMAKE_CURRENT_SOURCE_DIR}/../include/ 25 | ) 26 | 27 | 28 | file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/master.dcf DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 29 | -------------------------------------------------------------------------------- /canopen_base_driver/test/test_base_driver_component.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "canopen_base_driver/node_interfaces/node_canopen_base_driver.hpp" 21 | #include "gtest/gtest.h" 22 | using namespace rclcpp_components; 23 | 24 | TEST(ComponentLoad, test_load_component_1) 25 | { 26 | rclcpp::init(0, nullptr); 27 | auto exec = std::make_shared(); 28 | auto manager = std::make_shared(exec); 29 | 30 | std::vector resources = 31 | manager->get_component_resources("canopen_base_driver"); 32 | 33 | EXPECT_EQ(2u, resources.size()); 34 | 35 | auto factory = manager->create_component_factory(resources[0]); 36 | auto instance_wrapper = 37 | factory->create_node_instance(rclcpp::NodeOptions().use_global_arguments(false)); 38 | 39 | rclcpp::shutdown(); 40 | } 41 | 42 | TEST(ComponentLoad, test_load_component_2) 43 | { 44 | rclcpp::init(0, nullptr); 45 | auto exec = std::make_shared(); 46 | auto manager = std::make_shared(exec); 47 | 48 | std::vector resources = 49 | manager->get_component_resources("canopen_base_driver"); 50 | 51 | EXPECT_EQ(2u, resources.size()); 52 | 53 | auto factory = manager->create_component_factory(resources[1]); 54 | auto instance_wrapper = 55 | factory->create_node_instance(rclcpp::NodeOptions().use_global_arguments(false)); 56 | 57 | rclcpp::shutdown(); 58 | } 59 | -------------------------------------------------------------------------------- /canopen_core/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package canopen_core 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.2.9 (2024-04-16) 6 | ------------------ 7 | * Add timeouts 8 | * Contributors: Vishnuprasad Prachandabhanu, ipa-vsp 9 | 10 | 0.3.0 (2024-12-12) 11 | ------------------ 12 | 13 | 0.2.12 (2024-04-22) 14 | ------------------- 15 | * Merge pull request `#270 `_ from gsalinas/can-namespace-pr 16 | Put components loaded by the device container into its namespace, if any. 17 | * pre-commit update 18 | * Put components loaded by the device container into its namespace, if any. 19 | * Merge pull request `#280 `_ from ipa-vsp/fix/yaml-build-error 20 | Fix undefined reference to yaml library 21 | * fix undefined reference to yaml 22 | * Fix logging in device_container.cpp (`#277 `_) 23 | * 0.2.9 24 | * forthcoming 25 | * Merge pull request `#220 `_ from ipa-vsp/feature/timeout-config 26 | Add timeouts 27 | * change from 100ms to 2000ms 28 | * non transmit time from bus.yml 29 | * timeout for booting slave 30 | * Contributors: Christoph Hellmann Santos, Gerry Salinas, Vishnuprasad Prachandabhanu, ipa-vsp 31 | 32 | 0.2.8 (2024-01-19) 33 | ------------------ 34 | 35 | 0.2.7 (2023-06-30) 36 | ------------------ 37 | * Add missing license headers and activate ament_copyright 38 | * Fix maintainer naming 39 | * Contributors: Christoph Hellmann Santos 40 | 41 | 0.2.6 (2023-06-24) 42 | ------------------ 43 | 44 | 0.2.5 (2023-06-23) 45 | ------------------ 46 | 47 | 0.2.4 (2023-06-22) 48 | ------------------ 49 | 50 | 0.2.3 (2023-06-22) 51 | ------------------ 52 | 53 | 0.2.2 (2023-06-21) 54 | ------------------ 55 | 56 | 0.2.1 (2023-06-21) 57 | ------------------ 58 | * Fix master and driver lifecycle 59 | * Fix QoS build warning in canopen_core 60 | * Use consistenlty HEX output for NodeID and Index. 61 | * Contributors: Christoph Hellmann Santos, Denis Štogl, Vishnuprasad Prachandabhanu 62 | 63 | 0.2.0 (2023-06-14) 64 | ------------------ 65 | * Created package 66 | * Contributors: Aulon Bajrami, Borong Yuan, Błażej Sowa, Christoph Hellmann Santos, Denis Štogl, Lovro, Vishnuprasad Prachandabhanu 67 | -------------------------------------------------------------------------------- /canopen_core/ConfigExtras.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Vishnuprasad Prachandabhanu 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | find_package(Boost REQUIRED system thread) 16 | -------------------------------------------------------------------------------- /canopen_core/include/canopen_core/device_container_error.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef DEVICE_CONTAINER_ERROR_HPP_ 16 | #define DEVICE_CONTAINER_ERROR_HPP_ 17 | 18 | #include 19 | #include 20 | 21 | namespace ros2_canopen 22 | { 23 | 24 | /** 25 | * @brief Device Container Exception 26 | * 27 | * This exception is used, when the device container 28 | * fails. 29 | * 30 | */ 31 | class DeviceContainerException : public std::exception 32 | { 33 | private: 34 | std::string what_; 35 | 36 | public: 37 | DeviceContainerException(std::string what) { what_ = what; } 38 | 39 | char * what(); 40 | }; 41 | 42 | } // namespace ros2_canopen 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /canopen_core/include/canopen_core/driver_error.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef DRIVER_ERROR_HPP_ 16 | #define DRIVER_ERROR_HPP_ 17 | 18 | #include 19 | #include 20 | 21 | namespace ros2_canopen 22 | { 23 | /** 24 | * @brief Driver Exception 25 | * 26 | * This exception is used, when a driver 27 | * fails. 28 | * 29 | */ 30 | class DriverException : public std::exception 31 | { 32 | private: 33 | std::string what_; 34 | 35 | public: 36 | DriverException(std::string what) { what_ = what; } 37 | 38 | char * what(); 39 | }; 40 | 41 | } // namespace ros2_canopen 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /canopen_core/include/canopen_core/master_error.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef MASTER_ERROR_HPP_ 16 | #define MASTER_ERROR_HPP_ 17 | 18 | #include 19 | 20 | namespace ros2_canopen 21 | { 22 | /** 23 | * @brief Master Exception 24 | * 25 | * This exception is used, when a master 26 | * fails. 27 | * 28 | */ 29 | class MasterException : public std::exception 30 | { 31 | private: 32 | std::string what_; 33 | 34 | public: 35 | MasterException(std::string what) { what_ = what; } 36 | 37 | char * what(); 38 | }; 39 | } // namespace ros2_canopen 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /canopen_core/include/canopen_core/visibility_control.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #ifndef CANOPEN_CORE__VISIBILITY_CONTROL_H_ 15 | #define CANOPEN_CORE__VISIBILITY_CONTROL_H_ 16 | 17 | // This logic was borrowed (then namespaced) from the examples on the gcc wiki: 18 | // https://gcc.gnu.org/wiki/Visibility 19 | 20 | #if defined _WIN32 || defined __CYGWIN__ 21 | #ifdef __GNUC__ 22 | #define CANOPEN_CORE_EXPORT __attribute__((dllexport)) 23 | #define CANOPEN_CORE_IMPORT __attribute__((dllimport)) 24 | #else 25 | #define CANOPEN_CORE_EXPORT __declspec(dllexport) 26 | #define CANOPEN_CORE_IMPORT __declspec(dllimport) 27 | #endif 28 | #ifdef CANOPEN_CORE_BUILDING_LIBRARY 29 | #define CANOPEN_CORE_PUBLIC CANOPEN_CORE_EXPORT 30 | #else 31 | #define CANOPEN_CORE_PUBLIC CANOPEN_CORE_IMPORT 32 | #endif 33 | #define CANOPEN_CORE_PUBLIC_TYPE CANOPEN_CORE_PUBLIC 34 | #define CANOPEN_CORE_LOCAL 35 | #else 36 | #define CANOPEN_CORE_EXPORT __attribute__((visibility("default"))) 37 | #define CANOPEN_CORE_IMPORT 38 | #if __GNUC__ >= 4 39 | #define CANOPEN_CORE_PUBLIC __attribute__((visibility("default"))) 40 | #define CANOPEN_CORE_LOCAL __attribute__((visibility("hidden"))) 41 | #else 42 | #define CANOPEN_CORE_PUBLIC 43 | #define CANOPEN_CORE_LOCAL 44 | #endif 45 | #define CANOPEN_CORE_PUBLIC_TYPE 46 | #endif 47 | 48 | #endif // CANOPEN_CORE__VISIBILITY_CONTROL_H_ 49 | -------------------------------------------------------------------------------- /canopen_core/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | canopen_core 5 | 0.3.0 6 | Core ros2_canopen functionalities such as DeviceContainer and master 7 | Christoph Hellmann Santos 8 | Apache-2.0 9 | 10 | ament_cmake 11 | 12 | canopen_interfaces 13 | lely_core_libraries 14 | lifecycle_msgs 15 | rclcpp 16 | rclcpp_components 17 | rclcpp_lifecycle 18 | yaml_cpp_vendor 19 | boost 20 | 21 | ament_lint_auto 22 | 23 | 24 | ament_cmake 25 | 26 | 27 | -------------------------------------------------------------------------------- /canopen_core/scripts/setup_vcan.sh: -------------------------------------------------------------------------------- 1 | modprobe vcan 2 | ip link add dev vcan0 type vcan 3 | ip link set up vcan0 4 | -------------------------------------------------------------------------------- /canopen_core/src/configuration_manager.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "canopen_core/configuration_manager.hpp" 16 | #include 17 | 18 | namespace ros2_canopen 19 | { 20 | 21 | void ConfigurationManager::init_config() 22 | { 23 | std::string dcf_path = ""; 24 | for (YAML::const_iterator it = root_.begin(); it != root_.end(); it++) 25 | { 26 | std::string driver_name = it->first.as(); 27 | if (driver_name != "options") continue; 28 | YAML::Node config_node = it->second; 29 | if (config_node["dcf_path"]) 30 | { 31 | dcf_path = config_node["dcf_path"].as(); 32 | } 33 | } 34 | 35 | for (YAML::const_iterator it = root_.begin(); it != root_.end(); it++) 36 | { 37 | std::string driver_name = it->first.as(); 38 | if (driver_name == "options") continue; 39 | YAML::Node config_node = it->second; 40 | if (!config_node["dcf_path"]) 41 | { 42 | config_node["dcf_path"] = dcf_path; 43 | } 44 | devices_.insert({driver_name, config_node}); 45 | } 46 | } 47 | 48 | uint32_t ConfigurationManager::get_all_devices(std::vector & devices) 49 | { 50 | uint32_t count = 0; 51 | for (auto it = devices_.begin(); it != devices_.end(); it++) 52 | { 53 | devices.emplace_back(it->first); 54 | count++; 55 | } 56 | return count; 57 | } 58 | 59 | } // namespace ros2_canopen 60 | -------------------------------------------------------------------------------- /canopen_core/src/device_container_error.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "canopen_core/device_container_error.hpp" 16 | #include 17 | namespace ros2_canopen 18 | { 19 | 20 | char * DeviceContainerException::what() 21 | { 22 | char * res = new char[1000]; 23 | strcpy(res, what_.c_str()); 24 | return res; 25 | } 26 | } // namespace ros2_canopen 27 | -------------------------------------------------------------------------------- /canopen_core/src/device_container_node.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Harshavadan Deshpande 2 | // Christoph Hellmann Santos 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #include "canopen_core/device_container.hpp" 17 | 18 | using namespace ros2_canopen; 19 | 20 | int main(int argc, char const * argv[]) 21 | { 22 | rclcpp::init(argc, argv); 23 | auto exec = std::make_shared(); 24 | auto device_container = std::make_shared(exec); 25 | std::thread spinThread([&device_container]() { device_container->init(); }); 26 | exec->add_node(device_container); 27 | exec->spin(); 28 | spinThread.join(); 29 | device_container->shutdown(); 30 | rclcpp::shutdown(); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /canopen_core/src/driver_error.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "canopen_core/driver_error.hpp" 16 | #include 17 | namespace ros2_canopen 18 | { 19 | char * DriverException::what() 20 | { 21 | char * res = new char[1000]; 22 | strcpy(res, what_.c_str()); 23 | return res; 24 | } 25 | } // namespace ros2_canopen 26 | -------------------------------------------------------------------------------- /canopen_core/src/master_error.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "canopen_core/master_error.hpp" 16 | #include 17 | namespace ros2_canopen 18 | { 19 | 20 | char * MasterException::what() 21 | { 22 | char * res = new char[1000]; 23 | strcpy(res, what_.c_str()); 24 | return res; 25 | } 26 | } // namespace ros2_canopen 27 | -------------------------------------------------------------------------------- /canopen_core/src/node_interfaces/node_canopen_master.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "canopen_core/node_interfaces/node_canopen_master.hpp" 16 | 17 | template class ros2_canopen::node_interfaces::NodeCanopenMaster; 18 | template class ros2_canopen::node_interfaces::NodeCanopenMaster; 19 | -------------------------------------------------------------------------------- /canopen_core/test/bus_configs/bad_driver_duplicate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | proxy_device_1: 3 | node_id: 2 4 | dcf: "simple.eds" 5 | dcf_path: "install/canopen_tests/share/canopen_tests/config/simple" 6 | driver: "ros2_canopen::CanopenDriver" 7 | package: "canopen_core" 8 | 9 | proxy_device_1: 10 | node_id: 2 11 | dcf: "simple.eds" 12 | dcf_path: "install/canopen_tests/share/canopen_tests/config/simple" 13 | driver: "ros2_canopen::CanopenDriver" 14 | package: "canopen_core" 15 | -------------------------------------------------------------------------------- /canopen_core/test/bus_configs/bad_driver_no_driver.yml: -------------------------------------------------------------------------------- 1 | --- 2 | proxy_device_1: 3 | node_id: 2 4 | dcf: "simple.eds" 5 | dcf_path: "install/canopen_tests/share/canopen_tests/config/simple" 6 | package: "canopen_core" 7 | -------------------------------------------------------------------------------- /canopen_core/test/bus_configs/bad_driver_no_id.yml: -------------------------------------------------------------------------------- 1 | --- 2 | proxy_device_1: 3 | dcf: "simple.eds" 4 | dcf_path: "install/canopen_tests/share/canopen_tests/config/simple" 5 | driver: "ros2_canopen::CanopenDriver" 6 | package: "canopen_core" 7 | -------------------------------------------------------------------------------- /canopen_core/test/bus_configs/bad_driver_no_package.yml: -------------------------------------------------------------------------------- 1 | --- 2 | proxy_device_1: 3 | node_id: 2 4 | dcf: "simple.eds" 5 | dcf_path: "install/canopen_tests/share/canopen_tests/config/simple" 6 | driver: "ros2_canopen::CanopenDriver" 7 | -------------------------------------------------------------------------------- /canopen_core/test/bus_configs/bad_master_no_driver.yml: -------------------------------------------------------------------------------- 1 | --- 2 | master: 3 | node_id: 1 4 | package: "canopen_core" 5 | -------------------------------------------------------------------------------- /canopen_core/test/bus_configs/bad_master_no_id.yml: -------------------------------------------------------------------------------- 1 | --- 2 | master: 3 | driver: "ros2_canopen::CanopenMaster" 4 | package: "canopen_core" 5 | -------------------------------------------------------------------------------- /canopen_core/test/bus_configs/bad_master_no_package.yml: -------------------------------------------------------------------------------- 1 | --- 2 | master: 3 | node_id: 1 4 | driver: "ros2_canopen::CanopenMaster" 5 | -------------------------------------------------------------------------------- /canopen_core/test/bus_configs/bad_no_master.yml: -------------------------------------------------------------------------------- 1 | --- 2 | proxy_device_1: 3 | node_id: 2 4 | dcf: "simple.eds" 5 | dcf_path: "install/canopen_tests/share/canopen_tests/config/simple" 6 | driver: "ros2_canopen::CanopenDriver" 7 | package: "canopen_core" 8 | 9 | proxy_device_2: 10 | node_id: 3 11 | dcf: "simple.eds" 12 | dcf_path: "install/canopen_tests/share/canopen_tests/config/simple" 13 | driver: "ros2_canopen::CanopenDriver" 14 | package: "canopen_core" 15 | -------------------------------------------------------------------------------- /canopen_core/test/bus_configs/good_driver.yml: -------------------------------------------------------------------------------- 1 | --- 2 | proxy_device_1: 3 | node_id: 2 4 | dcf: "simple.eds" 5 | dcf_path: "install/canopen_tests/share/canopen_tests/config/simple" 6 | driver: "ros2_canopen::CanopenDriver" 7 | package: "canopen_core" 8 | -------------------------------------------------------------------------------- /canopen_core/test/bus_configs/good_master.yml: -------------------------------------------------------------------------------- 1 | --- 2 | master: 3 | node_id: 1 4 | driver: "ros2_canopen::CanopenMaster" 5 | package: "canopen_core" 6 | -------------------------------------------------------------------------------- /canopen_core/test/bus_configs/good_master_and_two_driver.yml: -------------------------------------------------------------------------------- 1 | --- 2 | master: 3 | node_id: 1 4 | driver: "ros2_canopen::CanopenMaster" 5 | package: "canopen_core" 6 | 7 | proxy_device_1: 8 | node_id: 2 9 | dcf: "simple.eds" 10 | dcf_path: "install/canopen_tests/share/canopen_tests/config/simple" 11 | driver: "ros2_canopen::CanopenDriver" 12 | package: "canopen_core" 13 | 14 | proxy_device_2: 15 | node_id: 3 16 | dcf: "simple.eds" 17 | dcf_path: "install/canopen_tests/share/canopen_tests/config/simple" 18 | driver: "ros2_canopen::CanopenDriver" 19 | package: "canopen_core" 20 | -------------------------------------------------------------------------------- /canopen_core/test/test_errors.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include "canopen_core/device_container_error.hpp" 17 | #include "canopen_core/driver_error.hpp" 18 | #include "canopen_core/master_error.hpp" 19 | #include "gmock/gmock.h" 20 | #include "gtest/gtest.h" 21 | using namespace std::chrono_literals; 22 | using namespace ros2_canopen; 23 | using namespace testing; 24 | 25 | TEST(DriverErrorTest, test_what) 26 | { 27 | DriverException test_obj("hello"); 28 | EXPECT_TRUE(std::string("hello").compare(test_obj.what()) == 0); 29 | } 30 | 31 | TEST(MasterErrorTest, test_what) 32 | { 33 | MasterException test_obj("hello"); 34 | EXPECT_TRUE(std::string("hello").compare(test_obj.what()) == 0); 35 | } 36 | 37 | TEST(DeviceContainerErrorTest, test_what) 38 | { 39 | DeviceContainerException test_obj("hello"); 40 | EXPECT_TRUE(std::string("hello").compare(test_obj.what()) == 0); 41 | } 42 | -------------------------------------------------------------------------------- /canopen_fake_slaves/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package canopen_fake_slaves 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.2.9 (2024-04-16) 6 | ------------------ 7 | 8 | 0.3.0 (2024-12-12) 9 | ------------------ 10 | * fix loop timer for run_velocity_mode 11 | * Fix clang format 12 | * Add comments for the fake slave function 13 | * Fix the thread issue. 14 | * Apply suggestions from code review 15 | Co-authored-by: Dr. Denis 16 | * Working version. 17 | * Periodic messages sent, but not received properly. 18 | * Working logic. Still have to work on the edf file. 19 | * Put the periodic messages in OnWrite 20 | * Extend fake_slaves to publish messages via rpdo 21 | 22 | 0.2.12 (2024-04-22) 23 | ------------------- 24 | * Merge pull request `#265 `_ from kurtist123/feature/expose-fake-slave-includes 25 | build: export include directories 26 | * build: export include directories 27 | * 0.2.9 28 | * forthcoming 29 | * Contributors: Kurtis Thrush, Vishnuprasad Prachandabhanu, ipa-vsp 30 | 31 | 0.2.8 (2024-01-19) 32 | ------------------ 33 | * Add fake profile velocity (`#230 `_) 34 | * Add simple sequence homing emulation 35 | * Add fake velocity mode 36 | * Formatting 37 | --------- 38 | * Add simple sequence homing emulation (`#229 `_) 39 | * Contributors: Christoph Hellmann Santos 40 | 41 | 0.2.7 (2023-06-30) 42 | ------------------ 43 | * Add missing license headers and activate ament_copyright 44 | * Contributors: Christoph Hellmann Santos 45 | 46 | 0.2.6 (2023-06-24) 47 | ------------------ 48 | 49 | 0.2.5 (2023-06-23) 50 | ------------------ 51 | 52 | 0.2.4 (2023-06-22) 53 | ------------------ 54 | 55 | 0.2.3 (2023-06-22) 56 | ------------------ 57 | 58 | 0.2.2 (2023-06-21) 59 | ------------------ 60 | 61 | 0.2.1 (2023-06-21) 62 | ------------------ 63 | * Fix fake slave for PDOs 64 | * Contributors: Christoph Hellmann Santos 65 | 66 | 0.2.0 (2023-06-14) 67 | ------------------ 68 | * Created package 69 | * Contributors: Błażej Sowa, Christoph Hellmann Santos, James Ward, Vishnuprasad Prachandabhanu 70 | -------------------------------------------------------------------------------- /canopen_fake_slaves/Readme.md: -------------------------------------------------------------------------------- 1 | # CANopen Mock Slaves 2 | 3 | ## Motion Controller Slaves 4 | 5 | Supported modes: 6 | * Cyclic Position 7 | * Profiled Position (Thanks to motion generator https://github.com/EFeru/MotionGenerator) 8 | * Interpolated Position 9 | * Homing 10 | * Profiled Velocity 11 | -------------------------------------------------------------------------------- /canopen_fake_slaves/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | canopen_fake_slaves 5 | 0.3.0 6 | Package with mock canopen slave 7 | Christoph Hellmann Santos 8 | Apache-2.0 9 | 10 | ament_cmake 11 | 12 | lely_core_libraries 13 | lifecycle_msgs 14 | rclcpp 15 | rclcpp_lifecycle 16 | 17 | ament_lint_auto 18 | 19 | 20 | ament_cmake 21 | 22 | 23 | -------------------------------------------------------------------------------- /canopen_fake_slaves/src/basic_slave.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "canopen_fake_slaves/basic_slave.hpp" 16 | #include "rclcpp/rclcpp.hpp" 17 | 18 | int main(int argc, char * argv[]) 19 | { 20 | rclcpp::InitOptions options; 21 | options.shutdown_on_signal = true; 22 | rclcpp::init(argc, argv, options, rclcpp::SignalHandlerOptions::All); 23 | rclcpp::executors::SingleThreadedExecutor executor; 24 | auto canopen_slave = std::make_shared("basic_slave"); 25 | executor.add_node(canopen_slave->get_node_base_interface()); 26 | executor.spin(); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /canopen_fake_slaves/src/cia402_slave.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "canopen_fake_slaves/cia402_slave.hpp" 16 | #include "rclcpp/rclcpp.hpp" 17 | 18 | int main(int argc, char * argv[]) 19 | { 20 | rclcpp::init(argc, argv); 21 | rclcpp::executors::SingleThreadedExecutor executor; 22 | auto canopen_slave = std::make_shared("cia402_slave"); 23 | executor.add_node(canopen_slave->get_node_base_interface()); 24 | executor.spin(); 25 | rclcpp::shutdown(); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /canopen_interfaces/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package canopen_interfaces 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.2.9 (2024-04-16) 6 | ------------------ 7 | 8 | 0.3.0 (2024-12-12) 9 | ------------------ 10 | 11 | 0.2.12 (2024-04-22) 12 | ------------------- 13 | * 0.2.9 14 | * forthcoming 15 | * Contributors: ipa-vsp 16 | 17 | 0.2.8 (2024-01-19) 18 | ------------------ 19 | 20 | 0.2.7 (2023-06-30) 21 | ------------------ 22 | 23 | 0.2.6 (2023-06-24) 24 | ------------------ 25 | 26 | 0.2.5 (2023-06-23) 27 | ------------------ 28 | 29 | 0.2.4 (2023-06-22) 30 | ------------------ 31 | 32 | 0.2.3 (2023-06-22) 33 | ------------------ 34 | 35 | 0.2.2 (2023-06-21) 36 | ------------------ 37 | 38 | 0.2.1 (2023-06-21) 39 | ------------------ 40 | 41 | 0.2.0 (2023-06-14) 42 | ------------------ 43 | * Created package 44 | * Contributors: Błażej Sowa, Christoph Hellmann Santos, Denis Štogl, Lovro 45 | -------------------------------------------------------------------------------- /canopen_interfaces/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(canopen_interfaces) 3 | 4 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 5 | add_compile_options(-Wall -Wextra -Wpedantic) 6 | endif() 7 | 8 | # find dependencies 9 | find_package(ament_cmake REQUIRED) 10 | find_package(rosidl_default_generators REQUIRED) 11 | # uncomment the following section in order to fill in 12 | # further dependencies manually. 13 | # find_package( REQUIRED) 14 | 15 | rosidl_generate_interfaces(${PROJECT_NAME} 16 | "srv/COHeartbeatID.srv" 17 | "srv/CONmtID.srv" 18 | "srv/CORead.srv" 19 | "srv/COReadID.srv" 20 | "srv/COWrite.srv" 21 | "srv/COWriteID.srv" 22 | "srv/COTargetDouble.srv" 23 | "srv/CONode.srv" 24 | "msg/COData.msg" 25 | ) 26 | 27 | 28 | if(BUILD_TESTING) 29 | find_package(ament_lint_auto REQUIRED) 30 | # the following line skips the linter which checks for copyrights 31 | # uncomment the line when a copyright and license is not present in all source files 32 | #set(ament_cmake_copyright_FOUND TRUE) 33 | # the following line skips cpplint (only works in a git repo) 34 | # uncomment the line when this package is not in a git repo 35 | #set(ament_cmake_cpplint_FOUND TRUE) 36 | ament_lint_auto_find_test_dependencies() 37 | endif() 38 | 39 | ament_package() 40 | -------------------------------------------------------------------------------- /canopen_interfaces/msg/COData.msg: -------------------------------------------------------------------------------- 1 | uint16 index 2 | uint8 subindex 3 | uint32 data 4 | -------------------------------------------------------------------------------- /canopen_interfaces/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | canopen_interfaces 5 | 0.3.0 6 | Services and Messages for ros2_canopen stack 7 | Christoph Hellmann Santos 8 | Apache-2.0 9 | 10 | ament_cmake 11 | 12 | ament_lint_auto 13 | ament_lint_common 14 | rosidl_default_generators 15 | rosidl_default_runtime 16 | rosidl_interface_packages 17 | 18 | ament_cmake 19 | 20 | 21 | -------------------------------------------------------------------------------- /canopen_interfaces/srv/COHeartbeatID.srv: -------------------------------------------------------------------------------- 1 | uint8 nodeid 2 | uint16 heartbeat #ms 3 | --- 4 | bool success 5 | -------------------------------------------------------------------------------- /canopen_interfaces/srv/CONmtID.srv: -------------------------------------------------------------------------------- 1 | uint8 nmtcommand 2 | uint8 nodeid 3 | --- 4 | bool success 5 | -------------------------------------------------------------------------------- /canopen_interfaces/srv/CONode.srv: -------------------------------------------------------------------------------- 1 | uint8 nodeid 2 | --- 3 | bool success 4 | -------------------------------------------------------------------------------- /canopen_interfaces/srv/CORead.srv: -------------------------------------------------------------------------------- 1 | uint16 index 2 | uint8 subindex 3 | --- 4 | bool success 5 | uint32 data 6 | -------------------------------------------------------------------------------- /canopen_interfaces/srv/COReadID.srv: -------------------------------------------------------------------------------- 1 | uint8 CANOPEN_DATATYPE_INT8 = 0x02 2 | uint8 CANOPEN_DATATYPE_INT16 = 0x03 3 | uint8 CANOPEN_DATATYPE_INT32 = 0x04 4 | uint8 CANOPEN_DATATYPE_UINT8 = 0x05 5 | uint8 CANOPEN_DATATYPE_UINT16 = 0x06 6 | uint8 CANOPEN_DATATYPE_UINT32 = 0x07 7 | 8 | uint8 nodeid 9 | uint16 index 10 | uint8 subindex 11 | # 8 = uint8_t, 16 = uint16_t, 32 = uint32_t 12 | uint8 canopen_datatype 13 | --- 14 | bool success 15 | uint32 data 16 | -------------------------------------------------------------------------------- /canopen_interfaces/srv/COTargetDouble.srv: -------------------------------------------------------------------------------- 1 | float64 target 2 | --- 3 | bool success 4 | -------------------------------------------------------------------------------- /canopen_interfaces/srv/COWrite.srv: -------------------------------------------------------------------------------- 1 | uint16 index 2 | uint8 subindex 3 | uint32 data 4 | --- 5 | bool success 6 | -------------------------------------------------------------------------------- /canopen_interfaces/srv/COWriteID.srv: -------------------------------------------------------------------------------- 1 | uint8 CANOPEN_DATATYPE_INT8 = 0x02 2 | uint8 CANOPEN_DATATYPE_INT16 = 0x03 3 | uint8 CANOPEN_DATATYPE_INT32 = 0x04 4 | uint8 CANOPEN_DATATYPE_UINT8 = 0x05 5 | uint8 CANOPEN_DATATYPE_UINT16 = 0x06 6 | uint8 CANOPEN_DATATYPE_UINT32 = 0x07 7 | 8 | int8 nodeid 9 | uint16 index 10 | uint8 subindex 11 | uint32 data 12 | # 8 = uint8_t, 16 = uint16_t, 32 = uint32_t 13 | uint8 canopen_datatype 14 | --- 15 | bool success 16 | -------------------------------------------------------------------------------- /canopen_master_driver/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package canopen_master_driver 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.2.9 (2024-04-16) 6 | ------------------ 7 | * Add timeouts 8 | * Contributors: Vishnuprasad Prachandabhanu 9 | 10 | 0.3.0 (2024-12-12) 11 | ------------------ 12 | 13 | 0.2.12 (2024-04-22) 14 | ------------------- 15 | * 0.2.9 16 | * forthcoming 17 | * Merge pull request `#220 `_ from ipa-vsp/feature/timeout-config 18 | Add timeouts 19 | * Revert timeout change to master since I'm not providing a way to set that timeout. 20 | * Make 20ms a default argument of the master & driver bridges. 21 | * timeout for booting slave 22 | * Contributors: Gerry Salinas, Vishnuprasad Prachandabhanu, ipa-vsp 23 | 24 | 0.2.8 (2024-01-19) 25 | ------------------ 26 | 27 | 0.2.7 (2023-06-30) 28 | ------------------ 29 | * Add missing license headers and activate ament_copyright 30 | * Contributors: Christoph Hellmann Santos 31 | 32 | 0.2.6 (2023-06-24) 33 | ------------------ 34 | 35 | 0.2.5 (2023-06-23) 36 | ------------------ 37 | 38 | 0.2.4 (2023-06-22) 39 | ------------------ 40 | 41 | 0.2.3 (2023-06-22) 42 | ------------------ 43 | 44 | 0.2.2 (2023-06-21) 45 | ------------------ 46 | 47 | 0.2.1 (2023-06-21) 48 | ------------------ 49 | * Fix master lifecycle 50 | * Contributors: Christoph Hellmann Santos 51 | 52 | 0.2.0 (2023-06-14) 53 | ------------------ 54 | * Created package 55 | * Contributors: Błażej Sowa, Christoph Hellmann Santos 56 | -------------------------------------------------------------------------------- /canopen_master_driver/include/canopen_master_driver/lifecycle_master_driver.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Harshavadan Deshpande 2 | // Christoph Hellmann Santos 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #ifndef LIFECYCLE_MASTER_DRIVER_HPP 16 | #define LIFECYCLE_MASTER_DRIVER_HPP 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "canopen_core/master_node.hpp" 23 | #include "canopen_master_driver/node_interfaces/node_canopen_basic_master.hpp" 24 | 25 | namespace ros2_canopen 26 | { 27 | /** 28 | * @brief Lifecycle Master Node 29 | * 30 | * This class implements the Lifecycle master interface. 31 | * It uses the Lely Master Bridge and exposes a ROS node 32 | * interface. 33 | * 34 | */ 35 | class LifecycleMasterDriver : public ros2_canopen::LifecycleCanopenMaster 36 | { 37 | std::shared_ptr> 38 | node_canopen_basic_master_; 39 | 40 | public: 41 | explicit LifecycleMasterDriver(const rclcpp::NodeOptions & node_options); 42 | }; 43 | 44 | } // namespace ros2_canopen 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /canopen_master_driver/include/canopen_master_driver/master_driver.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Harshavadan Deshpande 2 | // Christoph Hellmann Santos 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #ifndef MASTER_DRIVER_HPP 16 | #define MASTER_DRIVER_HPP 17 | 18 | #include "canopen_core/master_node.hpp" 19 | #include "canopen_master_driver/node_interfaces/node_canopen_basic_master.hpp" 20 | 21 | namespace ros2_canopen 22 | { 23 | /** 24 | * @brief Master Node 25 | * 26 | * This class implements the Lifecycle master interface. 27 | * It uses the Lely Master Bridge and exposes a ROS node 28 | * interface. 29 | * 30 | */ 31 | class MasterDriver : public ros2_canopen::CanopenMaster 32 | { 33 | std::shared_ptr> node_canopen_basic_master_; 34 | 35 | public: 36 | explicit MasterDriver(const rclcpp::NodeOptions & node_options); 37 | }; 38 | 39 | } // namespace ros2_canopen 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /canopen_master_driver/include/canopen_master_driver/visibility_control.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 ROS-Industrial 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef CANOPEN_MASTER_DRIVER__VISIBILITY_CONTROL_H_ 16 | #define CANOPEN_MASTER_DRIVER__VISIBILITY_CONTROL_H_ 17 | 18 | // This logic was borrowed (then namespaced) from the examples on the gcc wiki: 19 | // https://gcc.gnu.org/wiki/Visibility 20 | 21 | #if defined _WIN32 || defined __CYGWIN__ 22 | #ifdef __GNUC__ 23 | #define CANOPEN_MASTER_DRIVER_EXPORT __attribute__((dllexport)) 24 | #define CANOPEN_MASTER_DRIVER_IMPORT __attribute__((dllimport)) 25 | #else 26 | #define CANOPEN_MASTER_DRIVER_EXPORT __declspec(dllexport) 27 | #define CANOPEN_MASTER_DRIVER_IMPORT __declspec(dllimport) 28 | #endif 29 | #ifdef CANOPEN_MASTER_DRIVER_BUILDING_LIBRARY 30 | #define CANOPEN_MASTER_DRIVER_PUBLIC CANOPEN_MASTER_DRIVER_EXPORT 31 | #else 32 | #define CANOPEN_MASTER_DRIVER_PUBLIC CANOPEN_MASTER_DRIVER_IMPORT 33 | #endif 34 | #define CANOPEN_MASTER_DRIVER_PUBLIC_TYPE CANOPEN_MASTER_DRIVER_PUBLIC 35 | #define CANOPEN_MASTER_DRIVER_LOCAL 36 | #else 37 | #define CANOPEN_MASTER_DRIVER_EXPORT __attribute__((visibility("default"))) 38 | #define CANOPEN_MASTER_DRIVER_IMPORT 39 | #if __GNUC__ >= 4 40 | #define CANOPEN_MASTER_DRIVER_PUBLIC __attribute__((visibility("default"))) 41 | #define CANOPEN_MASTER_DRIVER_LOCAL __attribute__((visibility("hidden"))) 42 | #else 43 | #define CANOPEN_MASTER_DRIVER_PUBLIC 44 | #define CANOPEN_MASTER_DRIVER_LOCAL 45 | #endif 46 | #define CANOPEN_MASTER_DRIVER_PUBLIC_TYPE 47 | #endif 48 | 49 | #endif // CANOPEN_MASTER_DRIVER__VISIBILITY_CONTROL_H_ 50 | -------------------------------------------------------------------------------- /canopen_master_driver/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | canopen_master_driver 5 | 0.3.0 6 | Basic canopen master implementation 7 | Christoph Hellmann Santos 8 | Apache-2.0 9 | 10 | ament_cmake_ros 11 | 12 | canopen_core 13 | canopen_interfaces 14 | lely_core_libraries 15 | rclcpp 16 | rclcpp_components 17 | rclcpp_lifecycle 18 | 19 | ament_lint_auto 20 | ament_lint_common 21 | 22 | 23 | ament_cmake 24 | 25 | 26 | -------------------------------------------------------------------------------- /canopen_master_driver/src/lifecycle_master_driver.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Harshavadan Deshpande 2 | // Christoph Hellmann Santos 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #include "canopen_master_driver/lifecycle_master_driver.hpp" 17 | 18 | namespace ros2_canopen 19 | { 20 | 21 | ros2_canopen::LifecycleMasterDriver::LifecycleMasterDriver(const rclcpp::NodeOptions & node_options) 22 | : LifecycleCanopenMaster(node_options) 23 | { 24 | node_canopen_master_ = 25 | std::make_shared>( 26 | this); 27 | } 28 | 29 | } // namespace ros2_canopen 30 | 31 | #include "rclcpp_components/register_node_macro.hpp" 32 | RCLCPP_COMPONENTS_REGISTER_NODE(ros2_canopen::LifecycleMasterDriver) 33 | -------------------------------------------------------------------------------- /canopen_master_driver/src/master_driver.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Harshavadan Deshpande 2 | // Christoph Hellmann Santos 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #include "canopen_master_driver/master_driver.hpp" 17 | 18 | namespace ros2_canopen 19 | { 20 | 21 | ros2_canopen::MasterDriver::MasterDriver(const rclcpp::NodeOptions & node_options) 22 | : CanopenMaster(node_options) 23 | { 24 | node_canopen_basic_master_ = 25 | std::make_shared>(this); 26 | node_canopen_master_ = std::static_pointer_cast( 27 | node_canopen_basic_master_); 28 | } 29 | 30 | } // namespace ros2_canopen 31 | 32 | #include "rclcpp_components/register_node_macro.hpp" 33 | RCLCPP_COMPONENTS_REGISTER_NODE(ros2_canopen::MasterDriver) 34 | -------------------------------------------------------------------------------- /canopen_master_driver/src/node_interfaces/node_canopen_basic_master.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Harshavadan Deshpande 2 | // Christoph Hellmann Santos 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #include "canopen_master_driver/node_interfaces/node_canopen_basic_master.hpp" 17 | #include "canopen_master_driver/node_interfaces/node_canopen_basic_master_impl.hpp" 18 | 19 | using namespace ros2_canopen::node_interfaces; 20 | 21 | template class ros2_canopen::node_interfaces::NodeCanopenBasicMaster; 22 | template class ros2_canopen::node_interfaces::NodeCanopenBasicMaster< 23 | rclcpp_lifecycle::LifecycleNode>; 24 | -------------------------------------------------------------------------------- /canopen_master_driver/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ament_add_gtest(test_node_canopen_basic_driver 2 | test_node_canopen_basic_master.cpp 3 | ) 4 | ament_target_dependencies(test_node_canopen_basic_driver 5 | ${dependencies} 6 | ) 7 | target_include_directories(test_node_canopen_basic_driver PUBLIC 8 | ${CMAKE_CURRENT_SOURCE_DIR}/../include/ 9 | ) 10 | target_link_libraries(test_node_canopen_basic_driver 11 | master_driver 12 | node_canopen_basic_master 13 | lely_master_bridge 14 | ) 15 | 16 | ament_add_gtest(test_node_canopen_basic_driver_ros 17 | test_node_canopen_basic_master_ros.cpp 18 | ) 19 | ament_target_dependencies(test_node_canopen_basic_driver_ros 20 | ${dependencies} 21 | ) 22 | target_include_directories(test_node_canopen_basic_driver_ros PUBLIC 23 | ${CMAKE_CURRENT_SOURCE_DIR}/../include/ 24 | ) 25 | target_link_libraries(test_node_canopen_basic_driver_ros 26 | master_driver 27 | node_canopen_basic_master 28 | lely_master_bridge 29 | ) 30 | 31 | ament_add_gtest(test_master_driver_component 32 | test_master_driver_component.cpp 33 | ) 34 | ament_target_dependencies(test_master_driver_component 35 | ${dependencies} 36 | ) 37 | target_include_directories(test_master_driver_component PUBLIC 38 | ${CMAKE_CURRENT_SOURCE_DIR}/../include/ 39 | ) 40 | target_link_libraries(test_master_driver_component 41 | master_driver 42 | lifecycle_master_driver 43 | lely_master_bridge 44 | ) 45 | 46 | file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/master.dcf DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 47 | -------------------------------------------------------------------------------- /canopen_master_driver/test/test_master_driver_component.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "canopen_master_driver/node_interfaces/node_canopen_basic_master.hpp" 21 | #include "gtest/gtest.h" 22 | using namespace rclcpp_components; 23 | 24 | TEST(MasterDriverComponent, test_load_lifecycle_master_driver) 25 | { 26 | rclcpp::init(0, nullptr); 27 | auto exec = std::make_shared(); 28 | auto manager = std::make_shared(exec); 29 | 30 | std::vector resources = 31 | manager->get_component_resources("canopen_master_driver"); 32 | 33 | EXPECT_EQ(2u, resources.size()); 34 | 35 | auto factory = manager->create_component_factory(resources[0]); 36 | auto instance_wrapper = 37 | factory->create_node_instance(rclcpp::NodeOptions().use_global_arguments(false)); 38 | 39 | rclcpp::shutdown(); 40 | } 41 | 42 | TEST(MasterDriverComponent, test_load_master_driver) 43 | { 44 | rclcpp::init(0, nullptr); 45 | auto exec = std::make_shared(); 46 | auto manager = std::make_shared(exec); 47 | 48 | std::vector resources = 49 | manager->get_component_resources("canopen_master_driver"); 50 | 51 | EXPECT_EQ(2u, resources.size()); 52 | 53 | auto factory = manager->create_component_factory(resources[1]); 54 | auto instance_wrapper = 55 | factory->create_node_instance(rclcpp::NodeOptions().use_global_arguments(false)); 56 | 57 | rclcpp::shutdown(); 58 | } 59 | -------------------------------------------------------------------------------- /canopen_master_driver/test/test_node_canopen_basic_master.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "canopen_master_driver/node_interfaces/node_canopen_basic_master.hpp" 16 | #include "gtest/gtest.h" 17 | 18 | class RclCppFixture 19 | { 20 | public: 21 | RclCppFixture() 22 | { 23 | rclcpp::init(0, nullptr); 24 | node = new rclcpp::Node("Node"); 25 | interface = new ros2_canopen::node_interfaces::NodeCanopenBasicMaster(node); 26 | } 27 | virtual ~RclCppFixture() 28 | { 29 | rclcpp::shutdown(); 30 | delete (interface); 31 | delete (node); 32 | } 33 | 34 | rclcpp::Node * node; 35 | ros2_canopen::node_interfaces::NodeCanopenBasicMaster * interface; 36 | }; 37 | RclCppFixture g_rclcppfixture; 38 | 39 | TEST(NodeCanopenBasicMaster, test_bad_sequence_configure) 40 | { 41 | auto iface = static_cast( 42 | g_rclcppfixture.interface); 43 | EXPECT_ANY_THROW(iface->configure()); 44 | } 45 | 46 | TEST(NodeCanopenBasicMaster, test_bad_sequence_activate) 47 | { 48 | auto iface = static_cast( 49 | g_rclcppfixture.interface); 50 | EXPECT_ANY_THROW(iface->activate()); 51 | } 52 | 53 | TEST(NodeCanopenBasicMaster, test_good_sequence) 54 | { 55 | auto iface = static_cast( 56 | g_rclcppfixture.interface); 57 | try 58 | { 59 | iface->init(); 60 | } 61 | catch (const std::exception & e) 62 | { 63 | RCLCPP_ERROR(rclcpp::get_logger("test"), e.what()); 64 | } 65 | EXPECT_NO_THROW(iface->configure()); 66 | } 67 | -------------------------------------------------------------------------------- /canopen_proxy_driver/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package canopen_proxy_driver 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.2.9 (2024-04-16) 6 | ------------------ 7 | 8 | 0.3.0 (2024-12-12) 9 | ------------------ 10 | 11 | 0.2.12 (2024-04-22) 12 | ------------------- 13 | * 0.2.9 14 | * forthcoming 15 | * Contributors: ipa-vsp 16 | 17 | 0.2.8 (2024-01-19) 18 | ------------------ 19 | 20 | 0.2.7 (2023-06-30) 21 | ------------------ 22 | * Add missing license headers and activate ament_copyright 23 | * Fix maintainer naming 24 | * Contributors: Christoph Hellmann Santos 25 | 26 | 0.2.6 (2023-06-24) 27 | ------------------ 28 | 29 | 0.2.5 (2023-06-23) 30 | ------------------ 31 | 32 | 0.2.4 (2023-06-22) 33 | ------------------ 34 | 35 | 0.2.3 (2023-06-22) 36 | ------------------ 37 | 38 | 0.2.2 (2023-06-21) 39 | ------------------ 40 | 41 | 0.2.1 (2023-06-21) 42 | ------------------ 43 | * Use consistenlty (uppercase) HEX output for NodeID and Index. 44 | * Contributors: Christoph Hellmann Santos, Denis Štogl 45 | 46 | 0.2.0 (2023-06-14) 47 | ------------------ 48 | * Created package 49 | * Contributors: Błażej Sowa, Christoph Hellmann Santos, Denis Štogl, Lovro, Vishnuprasad Prachandabhanu 50 | -------------------------------------------------------------------------------- /canopen_proxy_driver/include/canopen_proxy_driver/visibility_control.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #ifndef CANOPEN_PROXY_DRIVER__VISIBILITY_CONTROL_H_ 15 | #define CANOPEN_PROXY_DRIVER__VISIBILITY_CONTROL_H_ 16 | 17 | // This logic was borrowed (then namespaced) from the examples on the gcc wiki: 18 | // https://gcc.gnu.org/wiki/Visibility 19 | 20 | #if defined _WIN32 || defined __CYGWIN__ 21 | #ifdef __GNUC__ 22 | #define CANOPEN_PROXY_DRIVER_EXPORT __attribute__((dllexport)) 23 | #define CANOPEN_PROXY_DRIVER_IMPORT __attribute__((dllimport)) 24 | #else 25 | #define CANOPEN_PROXY_DRIVER_EXPORT __declspec(dllexport) 26 | #define CANOPEN_PROXY_DRIVER_IMPORT __declspec(dllimport) 27 | #endif 28 | #ifdef CANOPEN_PROXY_DRIVER_BUILDING_LIBRARY 29 | #define CANOPEN_PROXY_DRIVER_PUBLIC CANOPEN_PROXY_DRIVER_EXPORT 30 | #else 31 | #define CANOPEN_PROXY_DRIVER_PUBLIC CANOPEN_PROXY_DRIVER_IMPORT 32 | #endif 33 | #define CANOPEN_PROXY_DRIVER_PUBLIC_TYPE CANOPEN_PROXY_DRIVER_PUBLIC 34 | #define CANOPEN_PROXY_DRIVER_LOCAL 35 | #else 36 | #define CANOPEN_PROXY_DRIVER_EXPORT __attribute__((visibility("default"))) 37 | #define CANOPEN_PROXY_DRIVER_IMPORT 38 | #if __GNUC__ >= 4 39 | #define CANOPEN_PROXY_DRIVER_PUBLIC __attribute__((visibility("default"))) 40 | #define CANOPEN_PROXY_DRIVER_LOCAL __attribute__((visibility("hidden"))) 41 | #else 42 | #define CANOPEN_PROXY_DRIVER_PUBLIC 43 | #define CANOPEN_PROXY_DRIVER_LOCAL 44 | #endif 45 | #define CANOPEN_PROXY_DRIVER_PUBLIC_TYPE 46 | #endif 47 | 48 | #endif // CANOPEN_PROXY_DRIVER__VISIBILITY_CONTROL_H_ 49 | -------------------------------------------------------------------------------- /canopen_proxy_driver/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | canopen_proxy_driver 5 | 0.3.0 6 | Simple proxy driver for the ros2_canopen stack 7 | Christoph Hellmann Santos 8 | Apache-2.0 9 | 10 | ament_cmake_ros 11 | 12 | canopen_base_driver 13 | canopen_core 14 | canopen_interfaces 15 | rclcpp 16 | rclcpp_components 17 | rclcpp_lifecycle 18 | std_msgs 19 | std_srvs 20 | 21 | ament_lint_auto 22 | 23 | 24 | ament_cmake 25 | 26 | 27 | -------------------------------------------------------------------------------- /canopen_proxy_driver/readme.md: -------------------------------------------------------------------------------- 1 | # canopen_ros_proxy_driver 2 | 3 | ## Testing 4 | ``` 5 | colcon test --packages-select canopen_proxy_driver --event-handlers desktop_notification+ status+ summary- console_direct+ terminal_title+ 6 | ``` 7 | -------------------------------------------------------------------------------- /canopen_proxy_driver/src/lifecycle_proxy_driver.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include "canopen_proxy_driver/lifecycle_proxy_driver.hpp" 15 | 16 | using namespace ros2_canopen; 17 | 18 | LifecycleProxyDriver::LifecycleProxyDriver(rclcpp::NodeOptions node_options) 19 | : LifecycleCanopenDriver(node_options) 20 | { 21 | node_canopen_proxy_driver_ = 22 | std::make_shared>( 23 | this); 24 | node_canopen_driver_ = std::static_pointer_cast( 25 | node_canopen_proxy_driver_); 26 | } 27 | 28 | #include "rclcpp_components/register_node_macro.hpp" 29 | RCLCPP_COMPONENTS_REGISTER_NODE(ros2_canopen::LifecycleProxyDriver) 30 | -------------------------------------------------------------------------------- /canopen_proxy_driver/src/node_interfaces/node_canopen_proxy_driver.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "canopen_proxy_driver/node_interfaces/node_canopen_proxy_driver.hpp" 16 | #include "canopen_proxy_driver/node_interfaces/node_canopen_proxy_driver_impl.hpp" 17 | 18 | using namespace ros2_canopen::node_interfaces; 19 | 20 | template class ros2_canopen::node_interfaces::NodeCanopenProxyDriver; 21 | template class ros2_canopen::node_interfaces::NodeCanopenProxyDriver< 22 | rclcpp_lifecycle::LifecycleNode>; 23 | -------------------------------------------------------------------------------- /canopen_proxy_driver/src/proxy_driver.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include "canopen_proxy_driver/proxy_driver.hpp" 15 | 16 | using namespace ros2_canopen; 17 | 18 | ProxyDriver::ProxyDriver(rclcpp::NodeOptions node_options) : CanopenDriver(node_options) 19 | { 20 | node_canopen_proxy_driver_ = 21 | std::make_shared>(this); 22 | node_canopen_driver_ = std::static_pointer_cast( 23 | node_canopen_proxy_driver_); 24 | } 25 | 26 | #include "rclcpp_components/register_node_macro.hpp" 27 | RCLCPP_COMPONENTS_REGISTER_NODE(ros2_canopen::ProxyDriver) 28 | -------------------------------------------------------------------------------- /canopen_proxy_driver/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ament_add_gtest(test_node_interface 2 | test_node_interface.cpp 3 | ) 4 | ament_target_dependencies(test_node_interface 5 | ${dependencies} 6 | ) 7 | target_include_directories(test_node_interface PUBLIC 8 | ${CMAKE_CURRENT_SOURCE_DIR}/../include/ 9 | ) 10 | target_link_libraries(test_node_interface 11 | node_canopen_proxy_driver 12 | ) 13 | 14 | 15 | 16 | 17 | ament_add_gtest(test_driver_component 18 | test_driver_component.cpp 19 | ) 20 | ament_target_dependencies(test_driver_component 21 | ${dependencies} 22 | ) 23 | target_include_directories(test_driver_component PUBLIC 24 | ${CMAKE_CURRENT_SOURCE_DIR}/../include/ 25 | ) 26 | 27 | 28 | file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/master.dcf DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 29 | -------------------------------------------------------------------------------- /canopen_proxy_driver/test/test_driver_component.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Christoph Hellmann Santos 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "canopen_base_driver/node_interfaces/node_canopen_base_driver.hpp" 21 | #include "gtest/gtest.h" 22 | using namespace rclcpp_components; 23 | 24 | TEST(ComponentLoad, test_load_component_1) 25 | { 26 | rclcpp::init(0, nullptr); 27 | auto exec = std::make_shared(); 28 | auto manager = std::make_shared(exec); 29 | 30 | std::vector resources = 31 | manager->get_component_resources("canopen_proxy_driver"); 32 | 33 | EXPECT_EQ(2u, resources.size()); 34 | 35 | auto factory = manager->create_component_factory(resources[0]); 36 | auto instance_wrapper = 37 | factory->create_node_instance(rclcpp::NodeOptions().use_global_arguments(false)); 38 | 39 | rclcpp::shutdown(); 40 | } 41 | 42 | TEST(ComponentLoad, test_load_component_2) 43 | { 44 | rclcpp::init(0, nullptr); 45 | auto exec = std::make_shared(); 46 | auto manager = std::make_shared(exec); 47 | 48 | std::vector resources = 49 | manager->get_component_resources("canopen_proxy_driver"); 50 | 51 | EXPECT_EQ(2u, resources.size()); 52 | 53 | auto factory = manager->create_component_factory(resources[1]); 54 | auto instance_wrapper = 55 | factory->create_node_instance(rclcpp::NodeOptions().use_global_arguments(false)); 56 | 57 | rclcpp::shutdown(); 58 | } 59 | -------------------------------------------------------------------------------- /canopen_ros2_control/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package canopen_ros2_control 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.2.9 (2024-04-16) 6 | ------------------ 7 | 8 | 0.3.0 (2024-12-12) 9 | ------------------ 10 | * pre-commit fix 11 | * impl operation mode 12 | * Add cyclic torque mode to cia402 driver and robot system controller (`#293 `_) 13 | * Add base functions for switching to cyclic torque mode 14 | * Add cyclic torque mode as effort interface to robot_system controller 15 | * Add documentation about cyclic torque mode. 16 | --------- 17 | Co-authored-by: Christoph Hellmann Santos 18 | * Fix clang format 19 | * Update canopen_system.hpp 20 | * Add pdo mapping support 21 | * Fix clang format 22 | * Fix pre-commit 23 | * Periodic messages sent, but not received properly. 24 | * Fix the bug that the rpdo queue keeps poping although it is empty. 25 | * Use proper function to get rpdo data 26 | * Fix bug in state interface indexing.. 27 | * WIP: Extend the rpdo to have a queue (FIFO) to save the values. 28 | The read function take the latest value out of the queue and assign to the system interface. 29 | Need tests. 30 | 31 | 0.2.12 (2024-04-22) 32 | ------------------- 33 | * 0.2.9 34 | * forthcoming 35 | * Contributors: ipa-vsp 36 | 37 | 0.2.8 (2024-01-19) 38 | ------------------ 39 | * Update robot_system.cpp (`#168 `_) 40 | * Contributors: Christoph Hellmann Santos 41 | 42 | 0.2.7 (2023-06-30) 43 | ------------------ 44 | * Correct Proxy controller after changes and update tests. 45 | * Contributors: Dr. Denis, Christoph Hellmann Santos 46 | 47 | 0.2.6 (2023-06-24) 48 | ------------------ 49 | 50 | 0.2.5 (2023-06-23) 51 | ------------------ 52 | 53 | 0.2.4 (2023-06-22) 54 | ------------------ 55 | 56 | 0.2.3 (2023-06-22) 57 | ------------------ 58 | * Solve buildfarm issues 59 | * Contributors: Christoph Hellmann Santos 60 | 61 | 0.2.2 (2023-06-21) 62 | ------------------ 63 | 64 | 0.2.1 (2023-06-21) 65 | ------------------ 66 | * Use consistenlty (uppercase) HEX output for NodeID and Index. 67 | * Contributors: Christoph Hellmann Santos, Denis Štogl 68 | 69 | 0.2.0 (2023-06-14) 70 | ------------------ 71 | * Created package 72 | * Contributors: Błażej Sowa, Christoph Hellmann Santos, Denis Štogl, Lovro, Vishnuprasad Prachandabhanu, livanov93 73 | -------------------------------------------------------------------------------- /canopen_ros2_control/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(canopen_ros2_control) 3 | 4 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 5 | # add_compile_options(-Wall -Wextra -Wpedantic) 6 | endif() 7 | 8 | # find dependencies 9 | set(THIS_PACKAGE_INCLUDE_DEPENDS 10 | canopen_402_driver 11 | canopen_core 12 | canopen_proxy_driver 13 | hardware_interface 14 | pluginlib 15 | rclcpp 16 | rclcpp_components 17 | rclcpp_lifecycle 18 | ) 19 | 20 | find_package(ros2_control_test_assets) 21 | find_package(ament_cmake_gmock REQUIRED) 22 | 23 | find_package(ament_cmake REQUIRED) 24 | foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS}) 25 | find_package(${Dependency} REQUIRED) 26 | endforeach() 27 | 28 | add_library( 29 | canopen_ros2_control 30 | SHARED 31 | src/canopen_system.cpp 32 | src/cia402_system.cpp 33 | src/robot_system.cpp 34 | ) 35 | 36 | 37 | 38 | target_compile_features(canopen_ros2_control PUBLIC c_std_99 cxx_std_17) # Require C99 and C++17 39 | target_compile_options(canopen_ros2_control PUBLIC -fPIC -Wl,--no-undefined) 40 | 41 | target_include_directories(canopen_ros2_control PUBLIC include) 42 | ament_target_dependencies(canopen_ros2_control ${THIS_PACKAGE_INCLUDE_DEPENDS}) 43 | 44 | # prevent pluginlib from using boost 45 | target_compile_definitions(canopen_ros2_control PUBLIC "PLUGINLIB__DISABLE_BOOST_FUNCTIONS") 46 | 47 | pluginlib_export_plugin_description_file(hardware_interface canopen_ros2_control.xml) 48 | 49 | install( 50 | TARGETS 51 | canopen_ros2_control 52 | RUNTIME DESTINATION bin 53 | ARCHIVE DESTINATION lib 54 | LIBRARY DESTINATION lib 55 | ) 56 | 57 | install( 58 | DIRECTORY include/ 59 | DESTINATION include 60 | ) 61 | 62 | if(BUILD_TESTING) 63 | #find_package(ament_cmake_gmock REQUIRED) 64 | #find_package(ros2_control_test_assets REQUIRED) 65 | 66 | #ament_add_gmock(test_canopen_system test/test_canopen_system.cpp) 67 | #target_include_directories(test_canopen_system PRIVATE include) 68 | #ament_target_dependencies( 69 | #test_canopen_system ${THIS_PACKAGE_INCLUDE_DEPENDS} ros2_control_test_assets) 70 | endif() 71 | 72 | ament_export_include_directories( 73 | include 74 | ) 75 | ament_export_libraries( 76 | canopen_ros2_control 77 | ) 78 | ament_export_dependencies( 79 | ${THIS_PACKAGE_INCLUDE_DEPENDS} 80 | ) 81 | 82 | ament_package() 83 | -------------------------------------------------------------------------------- /canopen_ros2_control/canopen_ros2_control.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | ros2_control hardware interface. 7 | 8 | 9 | 12 | 13 | ros2_control hardware interface. 14 | 15 | 16 | 19 | 20 | ros2_control hardware interface. 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /canopen_ros2_control/include/canopen_ros2_control/helpers.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Fraunhofer IPA 2 | // Copyright (c) 2022, StoglRobotics 3 | // Copyright (c) 2022, Stogl Robotics Consulting UG (haftungsbeschränkt) (template) 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | #ifndef CANOPEN_ROS2_CONTROL__HELPER_HPP_ 18 | #define CANOPEN_ROS2_CONTROL__HELPER_HPP_ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace canopen_ros2_control 26 | { 27 | /** 28 | * @brief Struct for storing the data necessary for a triggering command. 29 | * 30 | * @details 31 | * A trigger command from a controller will write to the command double. 32 | * The result of the trigger operation will be written to the response double. 33 | * A result of 1.0 means the operation was successful. 34 | * A result of 0.0 means the operation failed. 35 | * Only a trigger command of 1.0 will be accepted and trigger the operation. 36 | * The command_available function can be used to check if a command is available, 37 | * undefined commands (other than 1.0) will be ignored. 38 | * The set_response function should be used to set the response value. 39 | * It will then clear the command. 40 | * 41 | */ 42 | struct TriggerCommand 43 | { 44 | double command = std::numeric_limits::quiet_NaN(); 45 | double response = std::numeric_limits::quiet_NaN(); 46 | std::function trigger_function; 47 | 48 | bool try_trigger() 49 | { 50 | if (command == 1.0) 51 | { 52 | response = trigger_function() ? 1.0 : 0.0; 53 | command = std::numeric_limits::quiet_NaN(); 54 | return true; 55 | } 56 | command = std::numeric_limits::quiet_NaN(); 57 | return false; 58 | } 59 | }; 60 | 61 | } // namespace canopen_ros2_control 62 | #endif 63 | -------------------------------------------------------------------------------- /canopen_ros2_control/include/canopen_ros2_control/visibility_control.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Stogl Robotics Consulting UG (haftungsbeschränkt) 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef CANOPEN_ROS2_CONTROL__VISIBILITY_CONTROL_H_ 16 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_CONTROL_H_ 17 | 18 | // This logic was borrowed (then namespaced) from the examples on the gcc wiki: 19 | // https://gcc.gnu.org/wiki/Visibility 20 | 21 | #if defined _WIN32 || defined __CYGWIN__ 22 | #ifdef __GNUC__ 23 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_EXPORT __attribute__((dllexport)) 24 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_IMPORT __attribute__((dllimport)) 25 | #else 26 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_EXPORT __declspec(dllexport) 27 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_IMPORT __declspec(dllimport) 28 | #endif 29 | #ifdef CANOPEN_ROS2_CONTROL__VISIBILITY_BUILDING_DLL 30 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_PUBLIC CANOPEN_ROS2_CONTROL__VISIBILITY_EXPORT 31 | #else 32 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_PUBLIC CANOPEN_ROS2_CONTROL__VISIBILITY_IMPORT 33 | #endif 34 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_PUBLIC_TYPE CANOPEN_ROS2_CONTROL__VISIBILITY_PUBLIC 35 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_LOCAL 36 | #else 37 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_EXPORT __attribute__((visibility("default"))) 38 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_IMPORT 39 | #if __GNUC__ >= 4 40 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_PUBLIC __attribute__((visibility("default"))) 41 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_LOCAL __attribute__((visibility("hidden"))) 42 | #else 43 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_PUBLIC 44 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_LOCAL 45 | #endif 46 | #define CANOPEN_ROS2_CONTROL__VISIBILITY_PUBLIC_TYPE 47 | #endif 48 | 49 | #endif // CANOPEN_ROS2_CONTROL__VISIBILITY_CONTROL_H_ 50 | -------------------------------------------------------------------------------- /canopen_ros2_control/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | canopen_ros2_control 5 | 0.3.0 6 | ros2_control wrapper for ros2_canopen functionalities 7 | Lovro Ivanov 8 | Denis Stogl 9 | 10 | Apache-2.0 11 | 12 | ament_cmake 13 | 14 | canopen_402_driver 15 | canopen_core 16 | canopen_proxy_driver 17 | hardware_interface 18 | pluginlib 19 | rclcpp 20 | rclcpp_components 21 | rclcpp_lifecycle 22 | 23 | ament_cmake_gmock 24 | ros2_control_test_assets 25 | 26 | 27 | ament_cmake 28 | 29 | 30 | -------------------------------------------------------------------------------- /canopen_ros2_controllers/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package canopen_ros2_controllers 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.2.9 (2024-04-16) 6 | ------------------ 7 | 8 | 0.3.0 (2024-12-12) 9 | ------------------ 10 | 11 | 0.2.12 (2024-04-22) 12 | ------------------- 13 | * 0.2.9 14 | * forthcoming 15 | * Contributors: ipa-vsp 16 | 17 | 0.2.8 (2024-01-19) 18 | ------------------ 19 | * Remove controller tests (`#249 `_) 20 | * Disable controller tests for now (`#231 `_) 21 | * Disable controller tests for now 22 | * Update iron.yml 23 | * Fix ci 24 | * Contributors: Christoph Hellmann Santos, Vishnuprasad Prachandabhanu 25 | 26 | 0.2.7 (2023-06-30) 27 | ------------------ 28 | * Correct Proxy controller after changes and update tests. 29 | * Contributors: Dr. Denis, Christoph Hellmann Santos 30 | 31 | 0.2.6 (2023-06-24) 32 | ------------------ 33 | 34 | 0.2.5 (2023-06-23) 35 | ------------------ 36 | 37 | 0.2.4 (2023-06-22) 38 | ------------------ 39 | 40 | 0.2.3 (2023-06-22) 41 | ------------------ 42 | 43 | 0.2.2 (2023-06-21) 44 | ------------------ 45 | 46 | 0.2.1 (2023-06-21) 47 | ------------------ 48 | * Fix QoS build warning canopen_ros2_controllers 49 | * Don't use ros2_control_test_assets since not needed anymore. Add load tests for all controllers. 50 | * Contributors: Christoph Hellmann Santos, Denis Štogl, Vishnuprasad Prachandabhanu 51 | 52 | 0.2.0 (2023-06-14) 53 | ------------------ 54 | * Created package 55 | * Contributors: Błażej Sowa, Christoph Hellmann Santos, Denis Štogl, Dr.-Ing. Denis Štogl, Lovro, livanov93 56 | -------------------------------------------------------------------------------- /canopen_ros2_controllers/canopen_ros2_controllers.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | Generic controller for Canopen devices providing service interfaces through ros2_control stack to the can devices. 6 | 7 | 8 | 10 | 11 | Generic controller for Canopen 402 devices providing service interfaces through ros2_control stack to the can devices. 12 | 13 | 14 | 16 | 17 | Generic controller for Canopen 402 devices providing service interfaces through ros2_control stack to the can devices. 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /canopen_ros2_controllers/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | canopen_ros2_controllers 5 | 0.3.0 6 | ros2_control controllers for ros2_canopen functionalities 7 | Denis Stogl 8 | Lovro Ivanov 9 | 10 | Apache-2.0 11 | 12 | ament_cmake 13 | 14 | canopen_402_driver 15 | canopen_interfaces 16 | canopen_proxy_driver 17 | controller_interface 18 | controller_manager 19 | hardware_interface 20 | pluginlib 21 | rclcpp 22 | rclcpp_lifecycle 23 | realtime_tools 24 | std_msgs 25 | std_srvs 26 | 27 | ament_cmake_gmock 28 | 29 | 30 | ament_cmake 31 | 32 | 33 | -------------------------------------------------------------------------------- /canopen_ros2_controllers/test/test_load_canopen_proxy_controller.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Stogl Robotics Consulting UG (haftungsbeschränkt) 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "controller_manager/controller_manager.hpp" 19 | #include "hardware_interface/resource_manager.hpp" 20 | #include "rclcpp/executor.hpp" 21 | #include "rclcpp/executors/single_threaded_executor.hpp" 22 | #include "rclcpp/utilities.hpp" 23 | 24 | TEST(TestLoadCanopenProxyController, load_controller) 25 | { 26 | rclcpp::init(0, nullptr); 27 | 28 | std::shared_ptr executor = 29 | std::make_shared(); 30 | 31 | controller_manager::ControllerManager cm( 32 | std::make_unique(), executor, "test_controller_manager"); 33 | 34 | ASSERT_NO_THROW(cm.load_controller( 35 | "test_canopen_ros2_controllers", "canopen_ros2_controllers/CanopenProxyController")); 36 | 37 | rclcpp::shutdown(); 38 | } 39 | -------------------------------------------------------------------------------- /canopen_ros2_controllers/test/test_load_cia402_device_controller.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Stogl Robotics Consulting UG (haftungsbeschränkt) 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "controller_manager/controller_manager.hpp" 19 | #include "hardware_interface/resource_manager.hpp" 20 | #include "rclcpp/executor.hpp" 21 | #include "rclcpp/executors/single_threaded_executor.hpp" 22 | #include "rclcpp/utilities.hpp" 23 | 24 | TEST(TestLoadCanopenProxyController, load_controller) 25 | { 26 | rclcpp::init(0, nullptr); 27 | 28 | std::shared_ptr executor = 29 | std::make_shared(); 30 | 31 | controller_manager::ControllerManager cm( 32 | std::make_unique(), executor, "test_controller_manager"); 33 | 34 | ASSERT_NO_THROW(cm.load_controller( 35 | "test_canopen_ros2_controllers", "canopen_ros2_controllers/Cia402DeviceController")); 36 | 37 | rclcpp::shutdown(); 38 | } 39 | -------------------------------------------------------------------------------- /canopen_ros2_controllers/test/test_load_cia402_robot_controller.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Stogl Robotics Consulting UG (haftungsbeschränkt) 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "controller_manager/controller_manager.hpp" 19 | #include "hardware_interface/resource_manager.hpp" 20 | #include "rclcpp/executor.hpp" 21 | #include "rclcpp/executors/single_threaded_executor.hpp" 22 | #include "rclcpp/utilities.hpp" 23 | 24 | TEST(TestLoadCanopenProxyController, load_controller) 25 | { 26 | rclcpp::init(0, nullptr); 27 | 28 | std::shared_ptr executor = 29 | std::make_shared(); 30 | 31 | controller_manager::ControllerManager cm( 32 | std::make_unique(), executor, "test_controller_manager"); 33 | 34 | ASSERT_NO_THROW(cm.load_controller( 35 | "test_canopen_ros2_controllers", "canopen_ros2_controllers/Cia402RobotController")); 36 | 37 | rclcpp::shutdown(); 38 | } 39 | -------------------------------------------------------------------------------- /canopen_tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(canopen_tests) 3 | 4 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 5 | add_compile_options(-Wall -Wextra -Wpedantic) 6 | endif() 7 | 8 | # find dependencies 9 | find_package(ament_cmake REQUIRED) 10 | find_package(lely_core_libraries REQUIRED) 11 | 12 | 13 | generate_dcf(simple) 14 | generate_dcf(canopen_system) 15 | cogen_dcf(cia402_system) 16 | cogen_dcf(cia402_namespaced_system) 17 | cogen_dcf(cia402) 18 | generate_dcf(cia402_lifecycle) 19 | cogen_dcf(cia402_diagnostics) 20 | generate_dcf(simple_lifecycle) 21 | cogen_dcf(simple_diagnostics) 22 | generate_dcf(robot_control) 23 | 24 | install(DIRECTORY 25 | launch rviz urdf launch_tests 26 | DESTINATION share/${PROJECT_NAME} 27 | ) 28 | 29 | 30 | 31 | if(BUILD_TESTING) 32 | if(CANOPEN_ENABLED) 33 | find_package(launch_testing_ament_cmake REQUIRED) 34 | add_launch_test(launch_tests/test_proxy_driver.py) 35 | add_launch_test(launch_tests/test_proxy_lifecycle_driver.py) 36 | endif() 37 | endif() 38 | 39 | ament_package() 40 | -------------------------------------------------------------------------------- /canopen_tests/README.md: -------------------------------------------------------------------------------- 1 | # CANopen Tests 2 | 3 | Enable launch tests with --cmake-args -DCANOPEN_ENABLED. 4 | They can only be run on devices that have vcan0 enabled. 5 | -------------------------------------------------------------------------------- /canopen_tests/ROS_NAMESPACES.md: -------------------------------------------------------------------------------- 1 | The `cia402_namespaced_system.launch.py` example demonstrates how to run a 2 | CIA402 system in a namespace to allow multiple CANOpen-based robots in the 3 | same ROS domain. This example is necessary because the controller names 4 | are picked up from `ros2_controllers.yaml` and we must define their names 5 | in that file with a `__namespace__/controller_name:` key, then use 6 | ReplaceString from `nav2_common` to dynamically add the namespace to the 7 | controller definition. 8 | 9 | For a proxy system, no example is included; it's sufficient to include, e.g. 10 | the `proxy_setup.launch.py` launch description in another launch file and 11 | push a namespace onto it with PushROSNamespace in the ordinary way. 12 | -------------------------------------------------------------------------------- /canopen_tests/config/canopen_system/bus.yml: -------------------------------------------------------------------------------- 1 | options: 2 | dcf_path: "@BUS_CONFIG_PATH@" 3 | 4 | master: 5 | node_id: 1 6 | driver: "ros2_canopen::MasterDriver" 7 | package: "canopen_master_driver" 8 | 9 | proxy_device_1: 10 | node_id: 2 11 | dcf: "simple.eds" 12 | driver: "ros2_canopen::ProxyDriver" 13 | package: "canopen_proxy_driver" 14 | 15 | proxy_device_2: 16 | node_id: 3 17 | dcf: "simple.eds" 18 | driver: "ros2_canopen::ProxyDriver" 19 | package: "canopen_proxy_driver" 20 | -------------------------------------------------------------------------------- /canopen_tests/config/canopen_system/ros2_controllers.yaml: -------------------------------------------------------------------------------- 1 | controller_manager: 2 | ros__parameters: 3 | update_rate: 10 # Hz 4 | 5 | joint_state_broadcaster: 6 | type: joint_state_broadcaster/JointStateBroadcaster 7 | 8 | node_1_controller: 9 | type: canopen_ros2_controllers/CanopenProxyController 10 | 11 | node_1_controller: 12 | ros__parameters: 13 | joint: node_1 14 | -------------------------------------------------------------------------------- /canopen_tests/config/cia402/bus.yml: -------------------------------------------------------------------------------- 1 | options: 2 | dcf_path: "@BUS_CONFIG_PATH@" 3 | 4 | master: 5 | node_id: 1 6 | driver: "ros2_canopen::MasterDriver" 7 | package: "canopen_master_driver" 8 | sync_period: 10000 9 | 10 | defaults: 11 | dcf: "cia402_slave.eds" 12 | driver: "ros2_canopen::Cia402Driver" 13 | package: "canopen_402_driver" 14 | period: 10 15 | revision_number: 0 16 | sdo: 17 | - {index: 0x60C2, sub_index: 1, value: 50} # Set interpolation time for cyclic modes to 50 ms 18 | - {index: 0x60C2, sub_index: 2, value: -3} # Set base 10-3s 19 | - {index: 0x6081, sub_index: 0, value: 1000} 20 | - {index: 0x6083, sub_index: 0, value: 2000} 21 | tpdo: # TPDO needed statusword, actual velocity, actual position, mode of operation 22 | 1: 23 | enabled: true 24 | cob_id: "auto" 25 | transmission: 0x01 26 | mapping: 27 | - {index: 0x6041, sub_index: 0} # status word 28 | - {index: 0x6061, sub_index: 0} # mode of operation display 29 | 2: 30 | enabled: true 31 | cob_id: "auto" 32 | transmission: 0x01 33 | mapping: 34 | - {index: 0x6064, sub_index: 0} # position actual value 35 | - {index: 0x606c, sub_index: 0} # velocity actual position 36 | 3: 37 | enabled: false 38 | 4: 39 | enabled: false 40 | rpdo: # RPDO needed controlword, target position, target velocity, mode of operation 41 | 1: 42 | enabled: true 43 | cob_id: "auto" 44 | mapping: 45 | - {index: 0x6040, sub_index: 0} # controlword 46 | - {index: 0x6060, sub_index: 0} # mode of operation 47 | 2: 48 | enabled: true 49 | cob_id: "auto" 50 | mapping: 51 | - {index: 0x607A, sub_index: 0} # target position 52 | - {index: 0x60FF, sub_index: 0} # target velocity 53 | 54 | nodes: 55 | cia402_device_1: 56 | node_id: 2 57 | namespace: "/test1" 58 | cia402_device_2: 59 | node_id: 3 60 | cia402_device_3: 61 | node_id: 4 62 | cia402_device_4: 63 | node_id: 5 64 | cia402_device_5: 65 | node_id: 6 66 | cia402_device_6: 67 | node_id: 7 68 | -------------------------------------------------------------------------------- /canopen_tests/config/cia402_diagnostics/bus.yml: -------------------------------------------------------------------------------- 1 | options: 2 | dcf_path: "@BUS_CONFIG_PATH@" 3 | 4 | master: 5 | node_id: 1 6 | driver: "ros2_canopen::MasterDriver" 7 | package: "canopen_master_driver" 8 | sync_period: 10000 9 | 10 | defaults: 11 | dcf: "cia402_slave.eds" 12 | driver: "ros2_canopen::Cia402Driver" 13 | package: "canopen_402_driver" 14 | diagnostics: 15 | enable: true 16 | period: 1000 # in milliseconds 17 | revision_number: 0 18 | sdo: 19 | - {index: 0x60C2, sub_index: 1, value: 50} # Set interpolation time for cyclic modes to 50 ms 20 | - {index: 0x60C2, sub_index: 2, value: -3} # Set base 10-3s 21 | - {index: 0x6081, sub_index: 0, value: 1000} 22 | - {index: 0x6083, sub_index: 0, value: 2000} 23 | tpdo: # TPDO needed statusword, actual velocity, actual position, mode of operation 24 | 1: 25 | enabled: true 26 | cob_id: "auto" 27 | transmission: 0x01 28 | mapping: 29 | - {index: 0x6041, sub_index: 0} # status word 30 | - {index: 0x6061, sub_index: 0} # mode of operation display 31 | 2: 32 | enabled: true 33 | cob_id: "auto" 34 | transmission: 0x01 35 | mapping: 36 | - {index: 0x6064, sub_index: 0} # position actual value 37 | - {index: 0x606c, sub_index: 0} # velocity actual position 38 | 3: 39 | enabled: false 40 | 4: 41 | enabled: false 42 | rpdo: # RPDO needed controlword, target position, target velocity, mode of operation 43 | 1: 44 | enabled: true 45 | cob_id: "auto" 46 | mapping: 47 | - {index: 0x6040, sub_index: 0} # controlword 48 | - {index: 0x6060, sub_index: 0} # mode of operation 49 | 2: 50 | enabled: true 51 | cob_id: "auto" 52 | mapping: 53 | - {index: 0x607A, sub_index: 0} # target position 54 | - {index: 0x60FF, sub_index: 0} # target velocity 55 | 56 | nodes: 57 | cia402_device_1: 58 | node_id: 2 59 | cia402_device_2: 60 | node_id: 3 61 | cia402_device_3: 62 | node_id: 4 63 | -------------------------------------------------------------------------------- /canopen_tests/config/cia402_lifecycle/bus.yml: -------------------------------------------------------------------------------- 1 | options: 2 | dcf_path: "@BUS_CONFIG_PATH@" 3 | 4 | master: 5 | node_id: 1 6 | driver: "ros2_canopen::LifecycleMasterDriver" 7 | package: "canopen_master_driver" 8 | sync_period: 20000 9 | 10 | cia402_device_1: 11 | node_id: 2 12 | dcf: "cia402_slave.eds" 13 | driver: "ros2_canopen::LifecycleCia402Driver" 14 | package: "canopen_402_driver" 15 | period: 20 16 | enable_lazy_load: false 17 | revision_number: 0 18 | sdo: 19 | - {index: 0x60C2, sub_index: 1, value: 50} # Set interpolation time for cyclic modes to 50 ms 20 | - {index: 0x60C2, sub_index: 2, value: -3} # Set base 10-3s 21 | tpdo: # TPDO needed statusword, actual velocity, actual position, mode of operation 22 | 1: 23 | enabled: true 24 | cob_id: "auto" 25 | transmission: 0x01 26 | mapping: 27 | - {index: 0x6041, sub_index: 0} # status word 28 | - {index: 0x6061, sub_index: 0} # mode of operation display 29 | 2: 30 | enabled: true 31 | cob_id: "auto" 32 | transmission: 0x01 33 | mapping: 34 | - {index: 0x6064, sub_index: 0} # position actual value 35 | - {index: 0x606c, sub_index: 0} # velocity actual position 36 | 3: 37 | enabled: false 38 | 4: 39 | enabled: false 40 | rpdo: # RPDO needed controlword, target position, target velocity, mode of operation 41 | 1: 42 | enabled: true 43 | cob_id: "auto" 44 | mapping: 45 | - {index: 0x6040, sub_index: 0} # controlword 46 | - {index: 0x6060, sub_index: 0} # mode of operation 47 | 2: 48 | enabled: true 49 | cob_id: "auto" 50 | mapping: 51 | - {index: 0x607A, sub_index: 0} # target position 52 | - {index: 0x60FF, sub_index: 0} # target velocity 53 | -------------------------------------------------------------------------------- /canopen_tests/config/cia402_namespaced_system/bus.yml: -------------------------------------------------------------------------------- 1 | options: 2 | dcf_path: "@BUS_CONFIG_PATH@" 3 | 4 | master: 5 | node_id: 1 6 | driver: "ros2_canopen::MasterDriver" 7 | package: "canopen_master_driver" 8 | sync_period: 10000 9 | 10 | defaults: 11 | dcf: "cia402_slave.eds" 12 | driver: "ros2_canopen::Cia402Driver" 13 | package: "canopen_402_driver" 14 | period: 10 15 | revision_number: 0 16 | sdo: 17 | - {index: 0x60C2, sub_index: 1, value: 50} # Set interpolation time for cyclic modes to 50 ms 18 | - {index: 0x60C2, sub_index: 2, value: -3} # Set base 10-3s 19 | - {index: 0x6081, sub_index: 0, value: 1000} 20 | - {index: 0x6083, sub_index: 0, value: 2000} 21 | tpdo: # TPDO needed statusword, actual velocity, actual position, mode of operation 22 | 1: 23 | enabled: true 24 | cob_id: "auto" 25 | transmission: 0x01 26 | mapping: 27 | - {index: 0x6041, sub_index: 0} # status word 28 | - {index: 0x6061, sub_index: 0} # mode of operation display 29 | 2: 30 | enabled: true 31 | cob_id: "auto" 32 | transmission: 0x01 33 | mapping: 34 | - {index: 0x6064, sub_index: 0} # position actual value 35 | - {index: 0x606c, sub_index: 0} # velocity actual position 36 | 3: 37 | enabled: false 38 | 4: 39 | enabled: false 40 | rpdo: # RPDO needed controlword, target position, target velocity, mode of operation 41 | 1: 42 | enabled: true 43 | cob_id: "auto" 44 | mapping: 45 | - {index: 0x6040, sub_index: 0} # controlword 46 | - {index: 0x6060, sub_index: 0} # mode of operation 47 | 2: 48 | enabled: true 49 | cob_id: "auto" 50 | mapping: 51 | - {index: 0x607A, sub_index: 0} # target position 52 | - {index: 0x60FF, sub_index: 0} # target velocity 53 | 54 | nodes: 55 | cia402_device_1: 56 | node_id: 2 57 | -------------------------------------------------------------------------------- /canopen_tests/config/cia402_namespaced_system/ros2_controllers.yaml: -------------------------------------------------------------------------------- 1 | __namespace__/controller_manager: 2 | ros__parameters: 3 | update_rate: 10 # Hz 4 | 5 | joint_state_broadcaster: 6 | type: joint_state_broadcaster/JointStateBroadcaster 7 | 8 | cia402_device_1_controller: 9 | type: canopen_ros2_controllers/Cia402DeviceController 10 | 11 | joint_trajectory_controller: 12 | type: joint_trajectory_controller/JointTrajectoryController 13 | 14 | forward_position_controller: 15 | type: forward_command_controller/ForwardCommandController 16 | 17 | __namespace__/cia402_device_1_controller: 18 | ros__parameters: 19 | joint: node_1 20 | 21 | __namespace__/forward_position_controller: 22 | ros__parameters: 23 | joints: 24 | - node_1 25 | interface_name: position 26 | 27 | __namespace__/joint_trajectory_controller: 28 | ros__parameters: 29 | joints: 30 | - node_1 31 | command_interfaces: 32 | - position 33 | state_interfaces: 34 | - position 35 | - velocity 36 | state_publish_rate: 100.0 37 | action_monitor_rate: 20.0 38 | allow_partial_joints_goal: false 39 | constraints: 40 | stopped_velocity_tolerance: 0.2 41 | goal_time: 0.0 42 | node_1: { trajectory: 0.2, goal: 0.1 } 43 | -------------------------------------------------------------------------------- /canopen_tests/config/cia402_system/bus.yml: -------------------------------------------------------------------------------- 1 | options: 2 | dcf_path: "@BUS_CONFIG_PATH@" 3 | 4 | master: 5 | node_id: 1 6 | driver: "ros2_canopen::MasterDriver" 7 | package: "canopen_master_driver" 8 | sync_period: 10000 9 | 10 | defaults: 11 | dcf: "cia402_slave.eds" 12 | driver: "ros2_canopen::Cia402Driver" 13 | package: "canopen_402_driver" 14 | period: 10 15 | revision_number: 0 16 | sdo: 17 | - {index: 0x60C2, sub_index: 1, value: 50} # Set interpolation time for cyclic modes to 50 ms 18 | - {index: 0x60C2, sub_index: 2, value: -3} # Set base 10-3s 19 | - {index: 0x6081, sub_index: 0, value: 1000} 20 | - {index: 0x6083, sub_index: 0, value: 2000} 21 | tpdo: # TPDO needed statusword, actual velocity, actual position, mode of operation 22 | 1: 23 | enabled: true 24 | cob_id: "auto" 25 | transmission: 0x01 26 | mapping: 27 | - {index: 0x6041, sub_index: 0} # status word 28 | - {index: 0x6061, sub_index: 0} # mode of operation display 29 | 2: 30 | enabled: true 31 | cob_id: "auto" 32 | transmission: 0x01 33 | mapping: 34 | - {index: 0x6064, sub_index: 0} # position actual value 35 | - {index: 0x606c, sub_index: 0} # velocity actual position 36 | 3: 37 | enabled: false 38 | 4: 39 | enabled: false 40 | rpdo: # RPDO needed controlword, target position, target velocity, mode of operation 41 | 1: 42 | enabled: true 43 | cob_id: "auto" 44 | mapping: 45 | - {index: 0x6040, sub_index: 0} # controlword 46 | - {index: 0x6060, sub_index: 0} # mode of operation 47 | 2: 48 | enabled: true 49 | cob_id: "auto" 50 | mapping: 51 | - {index: 0x607A, sub_index: 0} # target position 52 | - {index: 0x60FF, sub_index: 0} # target velocity 53 | 54 | nodes: 55 | cia402_device_1: 56 | node_id: 2 57 | -------------------------------------------------------------------------------- /canopen_tests/config/cia402_system/ros2_controllers.yaml: -------------------------------------------------------------------------------- 1 | controller_manager: 2 | ros__parameters: 3 | update_rate: 10 # Hz 4 | 5 | joint_state_broadcaster: 6 | type: joint_state_broadcaster/JointStateBroadcaster 7 | 8 | cia402_device_1_controller: 9 | type: canopen_ros2_controllers/Cia402DeviceController 10 | 11 | joint_trajectory_controller: 12 | type: joint_trajectory_controller/JointTrajectoryController 13 | 14 | forward_position_controller: 15 | type: forward_command_controller/ForwardCommandController 16 | 17 | cia402_device_1_controller: 18 | ros__parameters: 19 | joint: node_1 20 | 21 | forward_position_controller: 22 | ros__parameters: 23 | joints: 24 | - node_1 25 | interface_name: position 26 | 27 | joint_trajectory_controller: 28 | ros__parameters: 29 | joints: 30 | - node_1 31 | command_interfaces: 32 | - position 33 | state_interfaces: 34 | - position 35 | - velocity 36 | state_publish_rate: 100.0 37 | action_monitor_rate: 20.0 38 | allow_partial_joints_goal: false 39 | constraints: 40 | stopped_velocity_tolerance: 0.2 41 | goal_time: 0.0 42 | node_1: { trajectory: 0.2, goal: 0.1 } 43 | -------------------------------------------------------------------------------- /canopen_tests/config/robot_control/ros2_controllers.yaml: -------------------------------------------------------------------------------- 1 | controller_manager: 2 | ros__parameters: 3 | update_rate: 100 # Hz 4 | joint_state_broadcaster: 5 | type: joint_state_broadcaster/JointStateBroadcaster 6 | 7 | robot_controller: 8 | type: canopen_ros2_controllers/Cia402RobotController 9 | 10 | forward_position_controller: 11 | type: forward_command_controller/ForwardCommandController 12 | 13 | robot_controller: 14 | ros__parameters: 15 | joints: 16 | - joint1 17 | - joint2 18 | operation_mode: 1 19 | command_poll_freq: 5 20 | 21 | forward_position_controller: 22 | ros__parameters: 23 | joints: 24 | - joint1 25 | - joint2 26 | interface_name: position 27 | 28 | # joint_trajectory_controller: 29 | # ros__parameters: 30 | # joints: 31 | # - prbt_joint_1 32 | # - prbt_joint_2 33 | # - prbt_joint_3 34 | # - prbt_joint_4 35 | # - prbt_joint_5 36 | # - prbt_joint_6 37 | # command_interfaces: 38 | # - position 39 | # state_interfaces: 40 | # - position 41 | # - velocity 42 | # state_publish_rate: 100.0 43 | # action_monitor_rate: 20.0 44 | # allow_partial_joints_goal: false 45 | # constraints: 46 | # stopped_velocity_tolerance: 0.2 47 | # goal_time: 0.6 48 | # stopped_velocity_tolerance: 0.05 49 | # prbt_joint_1: {trajectory: 0.157, goal: 0.01} 50 | # prbt_joint_2: {trajectory: 0.157, goal: 0.01} 51 | # prbt_joint_3: {trajectory: 0.157, goal: 0.01} 52 | # prbt_joint_4: {trajectory: 0.157, goal: 0.01} 53 | # prbt_joint_5: {trajectory: 0.157, goal: 0.01} 54 | # prbt_joint_6: {trajectory: 0.157, goal: 0.01} 55 | -------------------------------------------------------------------------------- /canopen_tests/config/simple/bus.yml: -------------------------------------------------------------------------------- 1 | options: 2 | dcf_path: "@BUS_CONFIG_PATH@" 3 | 4 | master: 5 | node_id: 1 6 | driver: "ros2_canopen::MasterDriver" 7 | package: "canopen_master_driver" 8 | 9 | proxy_device_1: 10 | node_id: 2 11 | dcf: "simple.eds" 12 | driver: "ros2_canopen::ProxyDriver" 13 | package: "canopen_proxy_driver" 14 | polling: true 15 | period: 10 16 | namespace: "/test1" 17 | 18 | proxy_device_2: 19 | node_id: 3 20 | dcf: "simple.eds" 21 | driver: "ros2_canopen::ProxyDriver" 22 | package: "canopen_proxy_driver" 23 | polling: true 24 | period: 10 25 | namespace: "/test2" 26 | -------------------------------------------------------------------------------- /canopen_tests/config/simple_diagnostics/bus.yml: -------------------------------------------------------------------------------- 1 | options: 2 | dcf_path: "@BUS_CONFIG_PATH@" 3 | 4 | master: 5 | node_id: 1 6 | driver: "ros2_canopen::MasterDriver" 7 | package: "canopen_master_driver" 8 | 9 | defaults: 10 | dcf: "simple.eds" 11 | driver: "ros2_canopen::ProxyDriver" 12 | package: "canopen_proxy_driver" 13 | polling: true 14 | period: 10 15 | diagnostics: 16 | enable: true 17 | period: 1000 # in milliseconds 18 | 19 | nodes: 20 | proxy_device_1: 21 | node_id: 2 22 | proxy_device_2: 23 | node_id: 3 24 | -------------------------------------------------------------------------------- /canopen_tests/config/simple_lifecycle/bus.yml: -------------------------------------------------------------------------------- 1 | options: 2 | dcf_path: "@BUS_CONFIG_PATH@" 3 | 4 | master: 5 | node_id: 1 6 | driver: "ros2_canopen::LifecycleMasterDriver" 7 | package: "canopen_master_driver" 8 | 9 | proxy_device_1: 10 | node_id: 2 11 | dcf: "simple.eds" 12 | driver: "ros2_canopen::LifecycleProxyDriver" 13 | package: "canopen_proxy_driver" 14 | 15 | proxy_device_2: 16 | node_id: 3 17 | dcf: "simple.eds" 18 | driver: "ros2_canopen::LifecycleProxyDriver" 19 | package: "canopen_proxy_driver" 20 | -------------------------------------------------------------------------------- /canopen_tests/launch/analyzers/cia402_diagnostic_analyzer.yaml: -------------------------------------------------------------------------------- 1 | analyzers: 2 | ros__parameters: 3 | path: Aggregation 4 | cia402_device_1: 5 | type: diagnostic_aggregator/GenericAnalyzer 6 | path: Cia402Device1 7 | contains: [ 'cia402_device_1' ] 8 | cia402_device_2: 9 | type: diagnostic_aggregator/GenericAnalyzer 10 | path: Cia402Device2 11 | contains: [ 'cia402_device_2' ] 12 | cia402_device_3: 13 | type: diagnostic_aggregator/GenericAnalyzer 14 | path: Cia402Device3 15 | contains: [ 'cia402_device_3' ] 16 | -------------------------------------------------------------------------------- /canopen_tests/launch/analyzers/proxy_diagnostic_analyzer.yaml: -------------------------------------------------------------------------------- 1 | analyzers: 2 | ros__parameters: 3 | path: Aggregation 4 | proxy_device_1: 5 | type: diagnostic_aggregator/GenericAnalyzer 6 | path: ProxyDevice1 7 | contains: [ 'proxy_device_1' ] 8 | proxy_device_2: 9 | type: diagnostic_aggregator/GenericAnalyzer 10 | path: ProxyDevice2 11 | contains: [ 'proxy_device_2' ] 12 | -------------------------------------------------------------------------------- /canopen_tests/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | canopen_tests 5 | 0.3.0 6 | Package with tests for ros2_canopen 7 | Christoph Hellmann Santos 8 | Apache-2.0 9 | 10 | ament_cmake 11 | 12 | lely_core_libraries 13 | 14 | canopen_402_driver 15 | canopen_core 16 | canopen_proxy_driver 17 | 18 | xacro 19 | controller_manager 20 | robot_state_publisher 21 | 22 | joint_state_broadcaster 23 | joint_trajectory_controller 24 | forward_command_controller 25 | 26 | canopen_fake_slaves 27 | canopen_ros2_controllers 28 | 29 | ament_lint_auto 30 | 31 | 32 | ament_cmake 33 | 34 | 35 | -------------------------------------------------------------------------------- /canopen_tests/urdf/canopen_system/canopen_system.ros2_control.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | canopen_ros2_control/CanopenSystem 15 | ${bus_config} 16 | ${master_config} 17 | ${can_interface_name} 18 | "${master_bin}" 19 | 20 | 21 | 2 22 | 23 | 24 | 3 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /canopen_tests/urdf/canopen_system/canopen_system.urdf.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /canopen_tests/urdf/cia402_system/cia402_system.ros2_control.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | canopen_ros2_control/Cia402System 15 | ${bus_config} 16 | ${master_config} 17 | ${can_interface_name} 18 | "${master_bin}" 19 | 20 | 21 | 2 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /canopen_tests/urdf/cia402_system/cia402_system.urdf.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /canopen_tests/urdf/robot_controller/robot_controller.macro.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /canopen_tests/urdf/robot_controller/robot_controller.ros2_control.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | canopen_ros2_control/RobotSystem 14 | ${bus_config} 15 | ${master_config} 16 | ${can_interface_name} 17 | "${master_bin}" 18 | 19 | 20 | joint_1 21 | 22 | 23 | 24 | joint_2 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /canopen_tests/urdf/robot_controller/robot_controller.urdf.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 15 | 16 | 23 | 24 | -------------------------------------------------------------------------------- /canopen_utils/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package canopen_utils 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.2.9 (2024-04-16) 6 | ------------------ 7 | 8 | 0.3.0 (2024-12-12) 9 | ------------------ 10 | 11 | 0.2.12 (2024-04-22) 12 | ------------------- 13 | * 0.2.9 14 | * forthcoming 15 | * Contributors: ipa-vsp 16 | 17 | 0.2.8 (2024-01-19) 18 | ------------------ 19 | 20 | 0.2.7 (2023-06-30) 21 | ------------------ 22 | * Add missing license headers and activate ament_copyright 23 | * Fix maintainer naming 24 | * Contributors: Christoph Hellmann Santos 25 | 26 | 0.2.6 (2023-06-24) 27 | ------------------ 28 | 29 | 0.2.5 (2023-06-23) 30 | ------------------ 31 | 32 | 0.2.4 (2023-06-22) 33 | ------------------ 34 | 35 | 0.2.3 (2023-06-22) 36 | ------------------ 37 | 38 | 0.2.2 (2023-06-21) 39 | ------------------ 40 | 41 | 0.2.1 (2023-06-21) 42 | ------------------ 43 | * Add more tidy launch_test_node.py 44 | * Contributors: Christoph Hellmann Santos 45 | 46 | 0.2.0 (2023-06-14) 47 | ------------------ 48 | * Created package 49 | * Contributors: Błażej Sowa, Christoph Hellmann Santos, Vishnuprasad Prachandabhanu 50 | -------------------------------------------------------------------------------- /canopen_utils/canopen_utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-industrial/ros2_canopen/f144f280f8e3c3f47491ae15e7f44ece1781db1a/canopen_utils/canopen_utils/__init__.py -------------------------------------------------------------------------------- /canopen_utils/no_tests/_test_copyright.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Open Source Robotics Foundation, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from ament_copyright.main import main 16 | import pytest 17 | 18 | 19 | @pytest.mark.copyright 20 | @pytest.mark.linter 21 | def test_copyright(): 22 | rc = main(argv=[".", "test"]) 23 | assert rc == 0, "Found errors" 24 | -------------------------------------------------------------------------------- /canopen_utils/no_tests/_test_flake8.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Open Source Robotics Foundation, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from ament_flake8.main import main_with_errors 16 | import pytest 17 | 18 | 19 | @pytest.mark.flake8 20 | @pytest.mark.linter 21 | def test_flake8(): 22 | rc, errors = main_with_errors(argv=[]) 23 | assert rc == 0, "Found %d code style errors / warnings:\n" % len(errors) + "\n".join(errors) 24 | -------------------------------------------------------------------------------- /canopen_utils/no_tests/_test_pep257.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Open Source Robotics Foundation, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from ament_pep257.main import main 16 | import pytest 17 | 18 | 19 | @pytest.mark.linter 20 | @pytest.mark.pep257 21 | def test_pep257(): 22 | rc = main(argv=[".", "test"]) 23 | assert rc == 0, "Found code style errors / warnings" 24 | -------------------------------------------------------------------------------- /canopen_utils/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | canopen_utils 5 | 0.3.0 6 | Utils for working with ros2_canopen. 7 | Christoph Hellmann Santos 8 | Apache-2.0 9 | rclpy 10 | canopen_interfaces 11 | lifecycle_msgs 12 | std_msgs 13 | ament_lint_auto 14 | 15 | ament_python 16 | 17 | 18 | -------------------------------------------------------------------------------- /canopen_utils/resource/canopen_utils: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-industrial/ros2_canopen/f144f280f8e3c3f47491ae15e7f44ece1781db1a/canopen_utils/resource/canopen_utils -------------------------------------------------------------------------------- /canopen_utils/setup.cfg: -------------------------------------------------------------------------------- 1 | [develop] 2 | script_dir=$base/lib/canopen_utils 3 | [install] 4 | install_scripts=$base/lib/canopen_utils 5 | -------------------------------------------------------------------------------- /canopen_utils/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 ROS-Industrial 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from setuptools import setup 16 | 17 | package_name = "canopen_utils" 18 | 19 | setup( 20 | name=package_name, 21 | version="0.3.0", 22 | packages=[package_name], 23 | data_files=[ 24 | ("share/ament_index/resource_index/packages", ["resource/" + package_name]), 25 | ("share/" + package_name, ["package.xml"]), 26 | ], 27 | install_requires=["setuptools"], 28 | zip_safe=True, 29 | maintainer="christoph", 30 | maintainer_email="christoph.hellmann.santos@ipa.fraunhofer.de", 31 | description="TODO: Package description", 32 | license="Apache-2.0", 33 | tests_require=["pytest"], 34 | entry_points={ 35 | "console_scripts": [ 36 | "cyclic_tester = canopen_utils.cyclic_tester:main", 37 | "simple_tester = canopen_utils.simple_rpdo_tpdo_tester:main", 38 | ], 39 | }, 40 | ) 41 | -------------------------------------------------------------------------------- /lely_core_libraries/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package lely_core_libraries 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 0.2.9 (2024-04-16) 6 | ------------------ 7 | * Update the lely_core_libraries hash to the latest. 8 | * Contributors: Chris Lalancette, Vishnuprasad Prachandabhanu 9 | 10 | 0.3.0 (2024-12-12) 11 | ------------------ 12 | * Add yaml dependency to package.xml of `lely_core_libraries` (`#290 `_) 13 | 14 | 0.2.12 (2024-04-22) 15 | ------------------- 16 | * 0.2.9 17 | * forthcoming 18 | * Merge pull request `#267 `_ from clalancette/clalancette/update-lely-core-hash 19 | Update the lely_core_libraries hash to the latest. 20 | * Update the lely_core_libraries hash to the latest. 21 | This will fix the building of the lely_core_libraries 22 | on Ubuntu 24.04. 23 | * Contributors: Chris Lalancette, Vishnuprasad Prachandabhanu, ipa-vsp 24 | 25 | 0.2.8 (2024-01-19) 26 | ------------------ 27 | 28 | 0.2.7 (2023-06-30) 29 | ------------------ 30 | * Add missing license headers and activate ament_copyright 31 | * Fix python versions 32 | * Contributors: Christoph Hellmann Santos 33 | 34 | 0.2.6 (2023-06-24) 35 | ------------------ 36 | * Move install from external project to cmake main 37 | * Contributors: Christoph Hellmann Santos 38 | 39 | 0.2.5 (2023-06-23) 40 | ------------------ 41 | * Directly install lely 42 | * Contributors: Christoph Hellmann Santos 43 | 44 | 0.2.4 (2023-06-22) 45 | ------------------ 46 | * Add empy dependency for dcfgen 47 | * Clean up lely build folder after install for rpm build 48 | * Contributors: Christoph Hellmann Santos 49 | 50 | 0.2.3 (2023-06-22) 51 | ------------------ 52 | * Fix manual dcfgen python installation on buildfarm 53 | * Solve buildfarm dependency issues 54 | * Contributors: Christoph Hellmann Santos 55 | 56 | 0.2.2 (2023-06-21) 57 | ------------------ 58 | * Reset hard before patch applicaiton 59 | * Contributors: Christoph Hellmann Santos 60 | 61 | 0.2.1 (2023-06-21) 62 | ------------------ 63 | * Do not build dcf-tools bdist in lely_core_libraries anymore, fixes buildfarm issue 64 | * Contributors: Christoph Hellmann Santos 65 | 66 | 0.2.0 (2023-06-14) 67 | ------------------ 68 | * Created package 69 | * Contributors: Błażej Sowa, Christoph Hellmann Santos 70 | -------------------------------------------------------------------------------- /lely_core_libraries/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(lely_core_libraries) 3 | find_package(ament_cmake REQUIRED) 4 | find_package(ament_cmake_python REQUIRED) 5 | set(python_version "python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}") 6 | 7 | include(ExternalProject) 8 | ExternalProject_Add(upstr_lely_core_libraries # Name for custom target 9 | #--Download step-------------- 10 | SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/upstream 11 | INSTALL_DIR "${CMAKE_INSTALL_PREFIX}" # Installation prefix 12 | BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/build 13 | GIT_REPOSITORY https://gitlab.com/lely_industries/lely-core.git 14 | GIT_TAG fb735b79cab5f0cdda45bc5087414d405ef8f3ab 15 | TIMEOUT 60 16 | #UPDATE step apply patch to fix dcf-tools install 17 | UPDATE_COMMAND 18 | COMMAND git reset --hard 19 | COMMAND git apply --whitespace=fix --reject ${CMAKE_CURRENT_SOURCE_DIR}/patches/0001-Fix-dcf-tools.patch 20 | #CONFIGURE step execute autoreconf and configure 21 | CONFIGURE_COMMAND autoreconf -i 22 | COMMAND /configure --prefix= --disable-cython --disable-doc --disable-tests --disable-static --disable-diag 23 | #BUILD STEP execute make 24 | BUILD_COMMAND $(MAKE) -C ${CMAKE_CURRENT_BINARY_DIR}/build 25 | #INSTALL STEP do nothing as we install in main 26 | INSTALL_COMMAND "" 27 | ) 28 | 29 | #INSTALL lely_core_libraries - execute make install 30 | install(CODE "execute_process(COMMAND ${CMAKE_MAKE_PROGRAM} install VERBOSE=1 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build)") 31 | 32 | set(lely_core_cmake_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 33 | include("cmake/lely_core_libraries-extras.cmake" NO_POLICY_SCOPE) 34 | 35 | 36 | install( 37 | DIRECTORY cmake 38 | DESTINATION share/${PROJECT_NAME} 39 | ) 40 | 41 | ament_python_install_package(cogen SCRIPTS_DESTINATION lib/cogen) 42 | 43 | # install entry-point script(s) in bin as well 44 | install( 45 | DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ament_cmake_python/cogen/scripts/ 46 | DESTINATION ${CMAKE_INSTALL_PREFIX}/bin/ 47 | USE_SOURCE_PERMISSIONS) 48 | 49 | ament_export_include_directories(include) 50 | ament_export_libraries(lely-can lely-co lely-coapp lely-ev lely-io2 lely-libc lely-tap lely-util) 51 | ament_package( 52 | CONFIG_EXTRAS 53 | "cmake/lely_core_libraries-extras.cmake" 54 | ) 55 | -------------------------------------------------------------------------------- /lely_core_libraries/cogen/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ros-industrial/ros2_canopen/f144f280f8e3c3f47491ae15e7f44ece1781db1a/lely_core_libraries/cogen/__init__.py -------------------------------------------------------------------------------- /lely_core_libraries/cogen/cogen.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Christoph Hellmann Santos 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import yaml 16 | import argparse 17 | from dataclasses import dataclass 18 | from dcfgen.cli import Master, Slave 19 | 20 | 21 | def main(): 22 | parser = argparse.ArgumentParser( 23 | description="Expands the CANopen bus.yml for further processing." 24 | ) 25 | parser.add_argument("--input-file", type=str, help="The name of the input file", required=True) 26 | parser.add_argument( 27 | "--output-file", type=str, help="The name of the output file", required=True 28 | ) 29 | args = parser.parse_args() 30 | 31 | with open(args.input_file) as input: 32 | cfg = yaml.load(input, yaml.FullLoader) 33 | 34 | master = {} 35 | if "master" not in cfg: 36 | print("Found no master.") 37 | return 38 | else: 39 | master = cfg["master"] 40 | 41 | nodes = {} 42 | if "nodes" not in cfg: 43 | print("Found no nodes entry.") 44 | return 45 | else: 46 | nodes = cfg["nodes"] 47 | 48 | options = {} 49 | if "options" in cfg: 50 | options = cfg["options"] 51 | 52 | defaults = {} 53 | if "defaults" in cfg: 54 | defaults = cfg["defaults"] 55 | 56 | # add defaults to each node 57 | for node_name in nodes: 58 | for entry_name in defaults: 59 | nodes[node_name][entry_name] = defaults[entry_name] 60 | 61 | modified_file = {} 62 | modified_file["options"] = options 63 | # modified_file["defaults"] = defaults 64 | modified_file["master"] = master 65 | for node_name in nodes: 66 | modified_file[node_name] = nodes[node_name] 67 | 68 | with open(args.output_file, mode="w") as output: 69 | yaml.dump(modified_file, output) 70 | 71 | return 72 | 73 | 74 | if __name__ == "__main__": 75 | main() 76 | -------------------------------------------------------------------------------- /lely_core_libraries/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | lely_core_libraries 5 | 0.3.0 6 | 7 | ROS wrapper for lely-core-libraries 8 | 9 | Christoph Hellmann Santos 10 | Apache-2.0 11 | 12 | ament_cmake 13 | git 14 | autoconf 15 | automake 16 | libtool 17 | python3-empy 18 | python3-yaml 19 | 20 | 21 | ament_cmake 22 | 23 | 24 | -------------------------------------------------------------------------------- /lely_core_libraries/setup.cfg: -------------------------------------------------------------------------------- 1 | [options.entry_points] 2 | console_scripts = 3 | cogen = cogen.cogen:main 4 | --------------------------------------------------------------------------------